Flutter 中由 BuildContext 引发的血案

Posted 萧文翰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter 中由 BuildContext 引发的血案相关的知识,希望对你有一定的参考价值。

今天和各位分享一个博主在实际开发中遇到的问题,以及解决方法。废话不多说,我们先来看需求:
我们要做一个ios风格的底部菜单弹出组件,具体涉及showCupertinoModalPopup()方法,该方法被执行后,会出现如下图类似所示的菜单弹出视图:

相信这个弹出菜单视图都有见过吧?下面重点来了:在本次的项目需求中,该视图的选项文字是由Server端返回的。也就是说,这些选项的内容和个数都不固定,因此不能将其在代码中写固定值。
为了简化代码以突出重点,下面放上我在一开始的实现方案:

  openActionSheet() 
    List<Widget> menuWidgets = new List();
    menuItems.forEach((element) 
      menuWidgets.add(CupertinoActionSheetAction(
        child: Text(element),
        onPressed: () 
          Navigator.pop(context);
          debugPrint("操作$element被执行“);
        ,
        isDefaultAction: true,
      ));
    );

    showCupertinoModalPopup(
        context: context,
        builder: (buildContext) 
          return CupertinoActionSheet(
              title: Text('测试菜单'),
              message: Text('点击菜单项试试吧!'),
              actions: menuWidgets);
        );
  

如上述代码所示,openActionSheet()是显示该组件的方法。其中,showCupertinoModalPopup()为Flutter SDK内置方法,其作用即显示这个组件;再其上面的循环以及List声明、赋值等操作实际上就是在动态添加菜单项。menuItems类型是List<String>。
通过对代码的解释,相信大家能够一目了然地看出,当某个菜单项被点击时,整个菜单组件消失,并打印Debug Log(对应为真实项目要执行的操作)。
大家觉得上述代码有问题吗?如果有问题,问题在哪儿呢?
现在公布答案:这段代码有问题!
上述代码执行时,当用户点击菜单项后,其运行结果并非如我们预想的那样:菜单组消失并输出Log,而变成了:整个页面被Pop,菜单组保留,并输出Log!
这是什么原因呢?
实际上,罪魁祸首就在我们循环遍历赋值操作时的这条语句:

Navigator.pop(context);

这里的context是整个页面的BuildContext,而非菜单组的。这里我们要明确一个概念——我们想Pop谁,一定要用谁的BuildContext对象。
在这里,正确的BuildContext对象是谁呢?它在这里:

showCupertinoModalPopup(
    context: context,
    builder: (buildContext) 
      return CupertinoActionSheet(
          title: Text('测试菜单'),
          message: Text('点击菜单项试试吧!'),
          actions: menuWidgets);
    
);

注意到了吗?上面第三行括号里的buildContext才是我们真正要用的对象。因此,正确的做法是什么呢?

  openActionSheet() 
    BuildContext tempContext;
    List<Widget> menuWidgets = new List();
    menuItems.forEach((element) 
      menuWidgets.add(CupertinoActionSheetAction(
        child: Text(element),
        onPressed: () 
          Navigator.pop(tempContext);
          debugPrint("操作$element被执行");
        ,
        isDefaultAction: true,
      ));
    );

    showCupertinoModalPopup(
        context: context,
        builder: (buildContext) 
          tempContext = buildContext;
          return CupertinoActionSheet(
              title: Text('测试菜单'),
              message: Text('点击菜单项试试吧!'),
              actions: menuWidgets);
        );
  

如上所示,我们只需将正确的对象“带”到其作用域外面就可以了。
好了,这就是本篇文章的全部内容,希望能够对你有所帮助!

以上是关于Flutter 中由 BuildContext 引发的血案的主要内容,如果未能解决你的问题,请参考以下文章

BuildContext 在 Flutter 中做了啥?

Flutter核心类分析深入理解BuildContext

Flutter核心类分析深入理解BuildContext

Flutter中的参数类型'Widget Function(BuildContext)'不能分配给参数类型'Widget Function(BuildContext,Widget)'错误

Flutter BLoC 类中的 BuildContext

从0开始设计Flutter独立APP | 第三篇: 一劳永逸解决全局BuildContext问题