Asynchronous Gap on BuildContext
Flutter2.x系からこの問題がフィーチャーされるようになった気がする。1.xの時はそこまで。
どんな問題?
- 非同期でサーバーにデータを送った後、ダイアログを閉じるようなケース。
- 非同期で
await
を入れた所で、Flutterのウィジェットツリーの再構築が同期されたかは、全くもって保証されない。 - そのため、非同期処理後に
Navigator.of(context).pop()
とかやると例外が発生する。
TextButton( child: const Text("いいえ(エラーになる)"), onPressed: () async { await doSomething(); Navigator.pop(context); //このcontextが同期されない } )
At this point the state of the widget's element tree is no longer stable. To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method
どーするか
公式の推奨は、StatefulWidget
でWidgetのライフサイクルを管理する方式。
void onButtonTapped() async { await Future.delayed(const Duration(seconds: 1)); if (!mounted) return; Navigator.of(context).pop(); }
StatefulWidgetを使わない場合、await
する前の Context
をキャッシュすると良いみたいなStackOverflowがあったけど、そもそもキャッシュするもんじゃないだろこれっていう思いが強い。
onPressed: () async { final navigator = Navigator.of(context); // store the Navigator await showDialog( context: context, builder: (_) => AlertDialog( title: Text('Dialog Title'), ), ); navigator.pop(); // use the Navigator, not the BuildContext },