flutter awai后不能使用context问题解析

更新时间:2023-05-25 21:55

awai后不能使用context问题解析 报错:Do not use BuildContexts across async gaps.

理解这个问题需要明白 context和widget的对应关系,context和wididget一对一对应,一个context对应一个widget,不明白可以哔哩哔哩搜索王叔不是秃子中有专门讲解..

问题从以下代码中备注解释

// From SO answer
// https://stackoverflow.com/a/69512692/10476111

import 'dart:async';

import 'package:flutter/material.dart';

class TestPage extends StatelessWidget {
  const TestPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home Page')),
      body: Center(
        child: ElevatedButton(
          child: Text('Open Test Page'),
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(builder: (_) => TestCon()),
            );
          },
        ),
      ),
    );
  }
}

class TestCon extends StatefulWidget {
  @override
  State<TestCon> createState() => _TestConState();
}

class _TestConState extends State<TestCon> {
  late final Timer timer;

  @override
  void initState() {
    super.initState();
    timer = Timer.periodic(Duration(milliseconds: 500), (timer) {
      setState(() {});
    });
  }

  @override
  void dispose() {
    timer.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final time = DateTime.now().millisecondsSinceEpoch;
    return Scaffold(
      appBar: AppBar(title: Text('Test Page')),
      body: Center(
        child: Column(
          children: [
            Text('Current Time: $time'),
            MySafeButton(key: UniqueKey()),
            MyDangerousButton(key: UniqueKey()),
          ],
        ),
      ),
    );
  }
}

class MySafeButton extends StatelessWidget {
  const MySafeButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      child: Text('Open Dialog Then Pop Safely'),
      onPressed: () async {
//当像下面的MyDangerousButton那样不先把(Navigator.of(context)[备注Navigator.of(context)其实就是context对应的widiget])存储时候,
//因为await原因,因为是异步方法,所以await回来后可能context已经被flutter丢弃(initState中的代码传入UniqueKey一直刷新)
//所以会报错旧context已经被丢弃,使用丢弃的context中自然会报错
        final navigator = Navigator.of(context);  
        await showDialog(
          context: context,
          
          builder: (_) => AlertDialog(            //注意这里的build传入的是_
            title: Text('Dialog Title'),
            actions: [
              TextButton(onPressed: () {
                navigator.pop();       
                //当执行这条语句时,实际上也是在TestCon中查找存在的dialog关闭
                //navigator.pop();  
                //比如再次执行这条语句就是关闭TestCon页面了
              }, child: Text("关闭")),
            ],
          ),
/*        //当我们把上面的(_) =>改为 (context)=> 时
          builder:(context)=> AlertDialog(
            title: Text('Dialog Title'),
            actions: [
              TextButton(onPressed: () {
                Navigator.of(context).pop();  
//这时可以正常关闭弹窗,因为当前context是alertdialog自己的widget对应的context
//不会受上层MySafeButton 丢弃的影响

              }, child: Text("关闭")),
            ],
          ),
*/

        );
         navigator.pop();  
         //注意这里是关闭TestCon页面,flutter从context往上查找最近的navigator关闭
      },
    );
  }
}

class MyDangerousButton extends StatelessWidget {
  const MyDangerousButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      child: Text('Open Dialog Then Pop Dangerously'),
      onPressed: () async {
        await showDialog(
          context: context,
          builder: (_) => AlertDialog(
            title: Text('Dialog Title'),
          ),
        );

        Navigator.of(context).pop();  
//报错 Do not use BuildContexts across async gaps.
//所以我们可以提前把旧context对应的navigator也就是Navigator.of(context)存储起来,
//这样就不会出从旧的context查找navigator的问题,也就是上面mysafeBUtton中所用的解决方法
      },
    );
  }
}