Life is Really Short, Have Your Life!!

ござ先輩の主に技術的なメモ

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

どーするか

公式の推奨は、StatefulWidgetWidgetのライフサイクルを管理する方式。

dart-lang.github.io

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

stackoverflow.com