Tags: Flutter

Riverpod の ConsumerWidget を使っていて 'DO NOT use BuildContext across asynchronous gaps.' が出たときの対応

Riverpod の ConsumerWidget を使っていて ‘DO NOT use BuildContext across asynchronous gaps.’ が出た場合、サジェストされる対応方法では解決しないので解決方法を記載する。

開発環境

  • Flutter SDK: 3.0.5
  • flutter_riverpod: 2.0.0-dev.9

本文

問題

下記のように、Riverpod の ConsumerWidget の build 関数内で await の後に Navigator.pop(context); を呼ぶようなコードを書いたとする。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class AddPage extends ConsumerWidget {

  const AddPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
        appBar: AppBar(title: const Text('新規登録')),
        body: SingleChildScrollView(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              const ResultParts(),
              ElevatedButton(
                child: const Text('保存'),
                onPressed: () async {
                    final result = await _save(context, ref);
                    if (result) {
                        const snackBar = SnackBar(
                            content: Text('保存しました'),
                            backgroundColor: Colors.green,
                        );
                        ScaffoldMessenger.of(context).showSnackBar(snackBar);
                        Navigator.pop(context, 'saved');
                    }
                },
              ),
            ],
          ),
        ));
  }

  Future<bool> _save(BuildContext context, WidgetRef ref) async {
    final result =
        await ref.read(addPageProvider.notifier).save().then((value) {
        return true;
    }).catchError((err) {
      showDialog<int>(
        context: context,
        barrierDismissible: false,
        builder: (context) {
            return ErrorDialog(e: err);
        },
      );
      return false;
    });
    return result;
  }
}

このとき Android Studio では下記の warning が出る。

DO NOT use BuildContext across asynchronous gaps.

上記のリンク先に対応方法が書いてあり、

if (!mounted) return;

と書くとのことなのだが、この mounted というのが ConsumerWidget には存在しない。

対応

mounted の代わりを探したが見つからなかったため、手っ取り早く await の後に処理を呼ぶのを止めた。
then で繋げてその中に処理を記述するようにする。

たとえば下記のように修正すると warning が出なくなる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class AddPage extends ConsumerWidget {

  const AddPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
        appBar: AppBar(title: const Text('新規登録')),
        body: SingleChildScrollView(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              const ResultParts(),
              ElevatedButton(
                child: const Text('保存'),
                onPressed: () async {
                    await _save(context, ref);
                },
              ),
            ],
          ),
        ));
  }

    Future<void> _save(BuildContext context, WidgetRef ref) async {
        await ref.read(addPageProvider.notifier).save().then((value) {
            const snackBar = SnackBar(
                content: Text('保存しました'),
                backgroundColor: Colors.green,
            );
            ScaffoldMessenger.of(context).showSnackBar(snackBar);
            Navigator.pop(context, 'saved');
        }).catchError((err) {
            showDialog<int>(
                context: context,
                barrierDismissible: false,
                builder: (context) {
                    return ErrorDialog(e: err);
                },
            );
        });
    }
}

その他今週の成果物

https://gitlab.com/k1350/flutter_anger_log

以上