Looking up a deactivated widget's ancestor is unsafe.
Looking up a deactivated widget's ancestor is unsafe.
Flutterにてダイアログ表示からの画面遷移時にエラーというか、警告がでてなんぞ?と思い深堀っていったら勉強になったので備忘録でかきこ。
エラーの内容
Looking up a deactivated widget's ancestor is unsafe.
上記が、「すでにツリーから取り除かれ(deactivated)たウィジェットの context を使って、上位ウィジェット(InheritedWidget など)を参照しようとしている」 とのこと。
主なケースとして、dispose() メソッド内で context を使って Provider.of(...) や MediaQuery.of(context) など を呼び出していると、ウィジェットがツリーから外れた後で上位要素を見に行こうとしているため、このようなエラーが出るとのこと。
要はcontext破棄されて使えないよ!っていう警告かなと。
BuildContextとは (context)
Flutter では、画面に表示するウィジェット一つひとつが「自分の BuildContext を持っている」感じです。
BuildContext は「このウィジェットがどこに存在していて、上や下にどんなウィジェットがあるのか」を管理する情報になります。
BuildContextは、Flutter のウィジェットが「どこにいるか」を教えてくれる住所のようなものだとも考えています。
街(ウィジェットツリー)
Flutter のアプリ全体は、大きな街のようにたくさんのウィジェット(建物や部屋)が集まってできています。これを ウィジェットツリー と呼びます。
住所(BuildContext)
それぞれのウィジェットは、自分が街の中のどこにあるかを示す住所を持っています。この住所が BuildContext です。
この住所のおかげで、そのウィジェットは「自分はどの建物(親ウィジェット)の中にいるか」「近くにどんなサービス(テーマ、スタイル、設定)があるか」を知ることができます。
たとえば、ウィジェットが「近くのカフェ(テーマ情報など)」を探すとき、BuildContext という住所を使って、そのカフェがどこにあるかを調べることができます。
今回のエラーとの関係性
実際に、今回出くわしたエラーが最初に述べた、Looking up a deactivated widget's ancestor is unsafe.なんですけど、ウィジェットツリーから取り除かれたcontext、を使用しているとのことでした。その問題のコードが下記。
showDialog(
/// 省略
CustomButton(
text: '確認する',
onPressed: () {
Navigator.of(context).pop();
const HogeRoute().push(context);
}
),
)
ローカルのpush通知で上記showDialog実装し、showDialog内のボタンを押下したら画面遷移する機能になります。
上記のコードを見ると、遷移する前にpop()してcontextを破棄している状態です。ですので画面遷移ができずに例のエラーがでているのだと思いました。
ただ、それだったら似たような処理は他にも記載しているし、そちらでは処理が通っているのに・・・と思ったのです。
showDialog は新しいルート(Route)を作る
何が起こっているのかchat-gptに聞いたところ、showDialogではそれ用のBuildContext(新しいウィジェットツリーに紐づいたcontext)が生成されるとのこと。
つまり「親(ホーム画面)から呼び出した showDialog の context」と「ダイアログの builder に渡される dialogContext」は実は別のツリー(別のルート)ですとのこと。
pop()した場合、dialogContextは破棄されるから、次にpushする際には使用できないよっていうのが今回のエラーの原因。
上記公式からchat-gptに聞きました。
The context argument is used to look up the Navigator for the dialog. It is only used when the method is called. It's not saved. If the context changes, the method does not need to be called again.
AI曰く、「for the dialog」というのがポイントで、「ダイアログ用の Navigator / Route を探してそこに新しい画面(ダイアログ)をプッシュしますよ」という意味が含まれています。また、「builder はその新しいルート上で呼ばれる」という話を合わせて読むと、最終的には別コンテキストが生まれる と理解できます。との回答。
ほかの画面ではpop()しても前画面のcontextを利用できて遷移できる。
ダイアログ表示の場合は、pop()した場合、dialogContextが無いので参照されるものが無いよっていう話。
ちゃんと遷移させる際に親がもつcontextをダイアログに渡してあげれば遷移できました。
以上!!!
Discussion