こんな情報をほしい人がいるのかわかりませんが、勉強として CupertinoTabScaffold を使っていた際に発生して困ったので、こんなふうにして回避したよ、という話です。
結論から言うと、大抵どこのサンプルも BuildContext 型の引数を context と表示しているので、ネストした場合も考慮して、名前を変えるなりしてちゃんと意識しましょうね、というお話です。
そもそも何をしようとしていたのか?
iOS の UITabBar のように、タブで切り替えた画面の中でナビゲーションできないかを調査していました。
material の BottomNavigationBar だと、push したときに画面下部のタブバーの部分も覆うように画面が追加されてしまうので、タブバー部分は隠れないように実装するにはどうすればよいか、という調査です。
タブバー部分が隠れないように push できる方法として、CupertinoTabScaffold があることを知りました。
何に困ったのか?
タブ内で遷移後、showDialog でプログレス表示を行い、タイトルにもある Navigator.of(context).pop() でプログレスを消そうとしたところ、予想外の結果となりました。
プログレスを消すことができませんでした。
(なんとなく、すぐに理由は推測できましたが・・・)
下の画像をご覧ください。
プログレスを表示した後、数秒後に Home2 と書かれた元の画面の方が pop されてしまっています。
一方、CupertinoTabScaffold ではなく、BottomNavigationBar の場合は下のようになります。
pop でちゃんとプログレスが消されています。
なお、BottomNavigationBar なので、 Home2 の画面は画面下部のタブを隠すように push されているのもわかります。
解決方法
適切な context を渡してあげれば OK です。
私はまだ感覚的に理解しているだけですが、結論だけ先に書いておくと
// NG コード
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return Container(...);
},
);
await Future.delayed(Duration(milliseconds: 3000));
Navigator.of(context).pop();
// OK コード
BuildContext innerContext; // 内部の context を保持しておくためのもの
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
innerContext = context; // builder で使われている context を innerContext に保存しておく
return Container(...);
},
);
await Future.delayed(Duration(milliseconds: 3000));
Navigator.of(innerContext).pop(); // context ではなく innerContext の方を使う
OK コードの方に変えたことで、下のような動きになりました。
まとめ
showDialog(
context: context,
builder: (BuildContext context) {
// 外側にある context と このスコープ内の context は同じかな?
},
);
今回は showDialog で困ったケースのお話でしたが、上記のように、ネストして builder が存在すると、その存在ごとに引数名を context にしてしまっていると思います。
そうなると、あるタイミングで使いたいと思ったときに、そのタイミングでアクセスできる【context 】は、果たして自分がほしい context なのか?ということを考える必要があります。
OK コードで示した例も、参照の保持の仕方、サンプルが await で 3 秒待つケースだから、ということで、簡単に対処できたケースの1つに過ぎません。
私自身も Flutter の勉強をし始めたばかりなので、こういった初歩的な躓きは全部躓いていきたいと思っています。
差し当たり、本件に関しては showDialog の返り値から context または context のようなものを取得できないのかを調べてみようと思います。
下のようにできたらわかりやすいですよね?
var dialog = showDialog(...);
await Future.delayed(Duration(milliseconds: 3000));
Navigator.of(dialog).pop();