🐈

【Flutter】StatefulWidget,StatelessWidgetで、riverpodの状態更新が可能な箇所と方法の比較まとめ

2023/02/03に公開

はじめに

例えば、下記のように、riverpod で state を定義したとします。
注目は、state を更新するためのメソッドupdateMessageです。
このupdateMessageメソッドを StatefulWidget, StatelessWidget 内で実行するケースについて、
メソッドの実行箇所実行方法を比較・まとめしました。

stateの定義

class MessageState with _$MessageState {
  const factory MessageState._({
    required String message,
  }) = _MessageState;

  factory MessageState.create({
    required String message,
  }) =>
      MessageState._(
        message: message,
      );
}

class MessageStateNotifier extends Notifier<MessageState> {
  
  MessageState build() {
    const message = "initial message";
    return MessageState.create(message: message);
  }

  void updateMessage(String newMessage) {
    state = state.copyWith(message: newMessage);
  }

コード比較画像

x

実行箇所と実行方法まとめ

実行方法/実行箇所 ① メソッド内 ②builder 内 ③ イベント処理内
(A) そのまま実行 エラー エラー O
(B) Future で実行[1] O O -
(C) addPostFrameCallback で実行 O O -
  • 可能なら、(A) そのまま実行したい。
  • しかし、①, ② の箇所では、(A) だとエラーになるため、(B) or (C) で実行する必要がある。
  • (B), (C) どちらが最適かは、まだ深堀りしていない[2]

サンプルコード全体

サンプルコード全体
main.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'home_page.dart';

void main() {
  runApp(const ProviderScope(
    child: MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}
home_page.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'home_page.dart';

void main() {
  runApp(const ProviderScope(
    child: MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}
message_state.dart
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

part 'message_state.freezed.dart';

// --------------------------------------------------
//
// MessageState
//
// --------------------------------------------------

class MessageState with _$MessageState {
  const factory MessageState._({
    required String message,
  }) = _MessageState;

  factory MessageState.create({
    required String message,
  }) =>
      MessageState._(
        message: message,
      );
}

class MessageStateNotifier extends Notifier<MessageState> {
  
  MessageState build() {
    const message = "initial message";
    return MessageState.create(message: message);
  }

  void updateMessage(String newMessage) {
    state = state.copyWith(message: newMessage);
  }
}

typedef MessageStateNotifierProvider
    = NotifierProvider<MessageStateNotifier, MessageState>;

MessageStateNotifierProvider messageStateNotifierProviderCreator() {
  return NotifierProvider<MessageStateNotifier, MessageState>(
      () => MessageStateNotifier());
}
message_stateful_widget.dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'message_state.dart';

// --------------------------------------------------
//
// State
//
// --------------------------------------------------
class MessageStatefulWidgetState {
  static final messageStateNotiferProvider =
      messageStateNotifierProviderCreator();
}

// --------------------------------------------------
//
// Widget
//
// --------------------------------------------------
class MessageStatefulWidget extends StatefulHookConsumerWidget {
  const MessageStatefulWidget({super.key, required this.title});

  final String title;

  
  ConsumerState<MessageStatefulWidget> createState() =>
      _MessageStatefulWidgetState();
}

class _MessageStatefulWidgetState extends ConsumerState<MessageStatefulWidget> {
  
  void initState() {
    // unawaited(Future(() {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update initState future");
    // }));

    // WidgetsBinding.instance.addPostFrameCallback((_) {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update initState addPostFrameCallback");
    // });

    super.initState();
  }

  
  void didChangeDependencies() {
    // unawaited(Future(() {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update didChangeDependencies future");
    // }));

    // WidgetsBinding.instance.addPostFrameCallback((_) {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update didChangeDependencies addPostFrameCallback");
    // });

    super.didChangeDependencies();
  }

  
  void didUpdateWidget(covariant MessageStatefulWidget oldWidget) {
    // unawaited(Future(() {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update didUpdateWidget future");
    // }));

    // WidgetsBinding.instance.addPostFrameCallback((_) {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update didUpdateWidget addPostFrameCallback");
    // });

    super.didUpdateWidget(oldWidget);
  }

  
  void dispose() {
    // unawaited(Future(() {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update dispose future");
    // }));

    // WidgetsBinding.instance.addPostFrameCallback((_) {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update dispose addPostFrameCallback");
    // });

    super.dispose();
  }

  
  Widget build(BuildContext context) {
    // unawaited(Future(() {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update build future");
    // }));

    // WidgetsBinding.instance.addPostFrameCallback((_) {
    //   ref
    //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update build addPostFrameCallback");
    // });

    return LayoutBuilder(builder: (context, constraints) {
      // unawaited(Future(() {
      //   ref
      //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
      //       .updateMessage("update builder future");
      // }));

      // WidgetsBinding.instance.addPostFrameCallback((_) {
      //   ref
      //       .read(MessageStatefulWidgetState.messageStateNotiferProvider.notifier)
      //       .updateMessage("update builder addPostFrameCallback");
      // });

      return Container(
        margin: const EdgeInsets.all(20),
        padding: const EdgeInsets.symmetric(horizontal: 20),
        width: double.infinity,
        color: Colors.grey.withOpacity(0.5),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Text(
              widget.title,
              style: const TextStyle(
                fontSize: 24,
              ),
            ),
            Container(
              color: Colors.white,
              padding: const EdgeInsets.all(20),
              child: Text(
                ref
                    .watch(
                        MessageStatefulWidgetState.messageStateNotiferProvider)
                    .message,
              ),
            ),
            ElevatedButton(
              onPressed: () {
                ref
                    .read(MessageStatefulWidgetState
                        .messageStateNotiferProvider.notifier)
                    .updateMessage("update onPressed");
              },
              child: const Text("update message"),
            ),
          ],
        ),
      );
    });
  }
}

message_stateless_widget.dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'message_state.dart';

// --------------------------------------------------
//
// State
//
// --------------------------------------------------
class MessageStatelessWidgetState {
  static final messageStateNotiferProvider =
      messageStateNotifierProviderCreator();
}

// --------------------------------------------------
//
// Widget
//
// --------------------------------------------------
class MessageStatelessWidget extends HookConsumerWidget {
  const MessageStatelessWidget({super.key, required this.title});

  final String title;

//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
  
  Widget build(BuildContext context, WidgetRef ref) {
    // unawaited(Future(() {
    //   ref
    //       .read(MessageStatelessWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update build future");
    // }));

    // WidgetsBinding.instance.addPostFrameCallback((_) {
    //   ref
    //       .read(MessageStatelessWidgetState.messageStateNotiferProvider.notifier)
    //       .updateMessage("update build addPostFrameCallback");
    // });

    return LayoutBuilder(builder: (context, constraints) {
      // unawaited(Future(() {
      //   ref
      //       .read(MessageStatelessWidgetState.messageStateNotiferProvider.notifier)
      //       .updateMessage("update builder future");
      // }));

      // WidgetsBinding.instance.addPostFrameCallback((_) {
      //   ref
      //       .read(MessageStatelessWidgetState.messageStateNotiferProvider.notifier)
      //       .updateMessage("update builder addPostFrameCallback");
      // });

      return Container(
        margin: const EdgeInsets.all(20),
        padding: const EdgeInsets.symmetric(horizontal: 20),
        width: double.infinity,
        color: Colors.grey.withOpacity(0.5),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Text(
              title,
              style: const TextStyle(
                fontSize: 24,
              ),
            ),
            Container(
              color: Colors.white,
              padding: const EdgeInsets.all(20),
              child: Text(
                ref
                    .watch(
                        MessageStatelessWidgetState.messageStateNotiferProvider)
                    .message,
              ),
            ),
            ElevatedButton(
              onPressed: () {
                ref
                    .read(MessageStatelessWidgetState
                        .messageStateNotiferProvider.notifier)
                    .updateMessage("update onPressed");
              },
              child: const Text("update message"),
            ),
          ],
        ),
      );
    });
  }
}

関連記事

本記事の続編を書きました。
https://zenn.dev/konbu33/articles/b531204096b055

脚注
  1. Future は、明示的に、unawaited()で実行しています。 ↩︎

  2. 個人の感覚的な話ですが、(C)は「非公開な?内部的な?インタフェース」を利用しているように思えるため、また、エラーメッセージに、Future で実行する旨の記載があるため、(B)を利用しています。 ↩︎

Discussion