📝
Don't use 'BuildContext's across async gaps.
赤じゃないからと無視し続けてきたけれど
地味に黄色の波線にじりじりと包囲されてきたので、
そろそろ真面目に考えようと思って、documentを読んだ。
ダメな例
void onButtonTapped(BuildContext context) async {
await Future.delayed(const Duration(seconds: 1));
Navigator.of(context).pop();
}
非同期処理中にcontextって書くなって言われているのはわかるけれど、
じゃあどうするの。
ドキュメントには三つの回避方法が書いてある。
1つ目は、非同期処理から独立させる。
void onButtonTapped(BuildContext context) {
Navigator.of(context).pop();
}
・・・といったって、そうできないからそうなってるわけで・・・
残りの二つがそっくりなのに微妙に違うので、初級者は困る。
void onButtonTapped(BuildContext context) async {
await Future.delayed(const Duration(seconds: 1));
if (!context.mounted) return;
Navigator.of(context).pop();
}
abstract class MyState extends State<MyWidget> {
void foo() async {
await Future.delayed(const Duration(seconds: 1));
if (!mounted) return; // Checks `this.mounted`, not `context.mounted`.
Navigator.of(context).pop();
}
}
なにが違うんだ!
まずやってみる
とりあえず、両方やってみて、今回は2つ目が効いた。
こんな感じ。
Future<void> fetchPrincipalByLocation(String keywords) async {
try {
List<String> location = keywords.split(',').map((e) => e.trim()).toList();
listPrincipal = await client.principal.getPrincipal(keywords: location);
principalIds = listPrincipal.map((item) => item.id as int).toList();
if (!mounted) return; <= ここをプラス
if (listPrincipal.length < 5) {
// データが5件に満たない場合、アラートを表示
showDialog(
context: context, <= ここに波線が出ていた
builder: (BuildContext context) {
return AlertDialog( 以下略
もう少し理解したい
で、爺様に聞いてみた。その答えが以下だけれど、もしかすると爺様の学習期間以後の変化なのかもしれない。ちょっとためらってる感じがある。
違い
if (!mounted) return; は State クラス内部で使われ、その State オブジェクトがまだウィジェットツリーに存在するかどうかをチェックします。
if (!context.mounted) return; は、ウィジェットの BuildContext がまだ有効かどうかをチェックします。ただし、このメソッドはFlutterの標準APIには存在しないため、使用する前に特定のフレームワークがこの機能を提供しているかを確認する必要があります。
Discussion
爺様へ教えてあげてください。存在しますよ、と。
BuildContext
のmountedプロパティは、Flutter 3.7.0で追加されました。3.7なので、1年以上前だと思います。それまではこのプロパティがなかったため、StatelessWidget内でmountedを確認する術が無かったのですね。
ありがとうございます!Flutterは成長が早いので、ときどき爺様の歳を感じます笑