hamburger-tech-nits

主にプログラミングのNITSな話

FlutterのStreamBuilder内で画面遷移させる

StreamBuilderはStreamの結果を元にWidgetを返却する関数であり、voidではない。なのでStreamの結果を評価した結果画面遷移したとしても、普通にNavigationの関数を呼び出すとエラーになる。

StreamBuilder<int?>(
      stream: intStream(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          context.go('/home'); // go_router
          return Container();
        },
        return Text('loading...');
      },
);

このような書き方で画面遷移しようとすると、以下のようなエラーが表示された。

======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for GoRouterDelegate:
setState() or markNeedsBuild() called during build.

This Router<Object> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: Router<Object>
  dependencies: [UnmanagedRestorationScope]
  state: _RouterState<Object>#b68c5

ビルド中にRouter生成がNGなので、それが終わってから実行するような書き方に変更する

StreamBuilder<int?>(
      stream: intStream(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
            context.go('/home');
          });
          return Container();
        },
        return Text('loading...');
      },
);

WidgetsBinding.instance.addPostFrameCallback を利用してビルド完了後のコールバックで関数を実行するので、エラーを解決できる。