14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Flutter】ページの初期化時に一度だけBlocの処理をコールしたい

Posted at

やりたかったこと

FlutterでBlocパターンでの実装において、StatefulWidgetの初期化時に一度だけ、Blocの処理をコールしたい、!

具体的には、トップページの初期化時に一度だけ新着ポップアップ通知の確認や、初回チュートリアルの表示確認をしたいケースでした。

TopPage,Blocは以下のような実装になります。

top_page.dart
class TopPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => TopPageState();
}

class TopPageState extends State<TopPage> {
  TopBloc _bloc;
  
  @override 
  void didChangeDependencies() {
    super.didChangeDependencies();
    _bloc = Provider.of<TopBloc>(context);
  }

  // _bloc.checklaunchAction.add()をどこで呼ぶ?

  @override 
  Widget build() {
    ...
  }
}

class TopBloc {
  
  final PublishSubject<void> _checkLaunchActionController = PublishSubject();

  Sink<void> get checkLaunchAction => _checkLaunchActionController.sink;

  TopBloc() {
    _checkLaunchActionController.listen((_) {
      // ポップアップの確認処理など
    });
  }

  dispose() {
    // controllerのdispose処理
  }
}

TopBloc,TopPageは以下のようにProviderを経由して、生成されます。これをすることで、TopPage内でProvider.of<TopBloc>(context)でBlocを参照することができます。

hoge.dart
Provider<TopBloc>(
  child: TopPage(),
  builder: (context) => TopBloc(),
  dispose: (_, bloc) {
    bloc.dispose();
  },
);

案1: initState()で呼ぶ

Providerを使って、Blocを初期化し、その子ウィジェットとしてStatefulWidgetを生成するため、initStateの段階では、Provider.of(context)で必要なContextが取得できないため却下。

案2: didChangeDependencies()で呼ぶ

このコールバックでは、contextが取得できるため、Blocの参照をState側のプロパティで保持することをよくやりますが、画面遷移をしたりすると、何度も呼ばれる可能性があるため、却下。

案3: Blocのコンストラクタで呼ぶ

Blocのコンストラクタで、先に確認処理を実行しておき、その処理結果をBlobで保持しておくという方法もありますが、これだとBlocに結果を問い合わせるタイミングが難しいことや、処理が追いづらくなるという点で却下になりました。

結論

StateのinitStateにて、WidgetsBinding.instance.addPostFrameCallback()内に一度だけ呼び出したいものを記述する。

これをすることで、Widgetの最初にビルドが完了した後に呼ばれることが保証されるため、必然的にBlocが取得でき、一度だけ呼ばれるということも保証されます。

top_page.dart
class TopPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => TopPageState();
}

class TopPageState extends State<TopPage> {
  ...

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      // ここで処理を呼び出す
      _bloc.checkLaunchAction.add(null);
    });
  }

  ...
}

また、以下のようにmixinを定義して、あたかも新しいライフサイクルコールバックかのように書くこともできます

initial_runnable_state.dart
mixin InitialRunnableState<T extends StatefulWidget> on State<T> {
  void runOnInitialBuild();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      runOnInitialBuild();
    });
  }
}
top_page.dart
class TopPageState extends State<TopPage> with InitialRunnableState<TopPage> {
  
  ...

  @override
  void runOnInitialBuild() {
    // ここで処理を呼び出す
    _bloc.checkLaunchAction.add(null);
  }

  ...
}

こちらのmixinについては、以下のリポジトリに公開しています。
https://github.com/youmitsu/InitialRunnableState

pub.devにも公開しました
https://pub.dev/packages/initial_runnable_state

間違いあれば指摘いただけると助かります!最後までご覧いただきありがとうございました。

14
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?