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
},