Flutter更新showDialog中的内容

很多人在用showDialog的时候应该都遇到过这个问题,使用showDialog后,通过setState()无法更新当前dialog。其实原因很简单,因为dialog其实是另一个页面,准确地来说是另一个路由,因为dialog的关闭也是通过navigator来pop的,所以它的地位跟你当前主页面一样。这个概念一定要明确,因为无论在Android或iOS中,daliog都是依附于当前主页面的一个控件,但是在Flutter中不同,它是一个新的路由。所以使用当前主页面的setState()来更新,当然没法达到你要的效果。

showDialog方法的Api中也明确说明了这一点,dialog所持有的context已经变了:

This widget does not share a context with the location that showDialog is originally called from. Use a [StatefulBuilder] or a custom [StatefulWidget] if the dialog needs to update dynamically.

所以我们有两种方法来解决这个问题,一种是使用StatefulBuilder,另一种是使用自定义的StatefulWidget。

StatefulBuilder

很多人使用StatefulBuilder依然达不到更新的效果,是因为你用错了setState()方法。

源码中关于builder的注释中有一句:

Typically the parent's build method will construct a new tree of widgets and so a new Builder child will not be [identical] to the corresponding old one.

就像我们上面说的那样,这个builder构建的控件,不会响应老页面的任何操作,因为它们是两个互不影响的路由控制的。

showDialog(
    context: context,
    builder: (context) {
        String label = 'test';
        return StatefulBuilder(
            builder: (context, state) {
                print('label = $label');
                return GestureDetector(
                    child: Text(label),
                    onTap: () {
                        label = 'test8';
                        print('onTap:label = $label');
                        // 注意不是调用老页面的setState,而是要调用builder中的setState。
                        //在这里为了区分,在构建builder的时候将setState方法命名为了state。
                        state(() {});  
                    },
                );
            },
         );
    });

在构建builder的时候,返回给了我们两个参数,BuildContext context 和 StateSetter setState,我们可以点进源码,StatefulWidgetBuilder的注释写的很清楚:

Call [setState] to schedule the [StatefulBuilder] to rebuild.

所以我们要调用的是builder返回给我们的setState,而不是老页面的setState。

自定义的StatefulWidget

用StatefulWidget就容易多了,思路就是将控制刷新的变量放到我们自定义的StatefulWidget的State中:

showDialog(
    context: context,
    builder: (context) {
        String label = 'test';
        return DialogContent(
            label: label,
        );
    });
 
class DialogContent extends StatefulWidget {
  final String label;
 
  DialogContent({Key key, this.label}) : super(key: key);
 
  @override
  State<StatefulWidget> createState() => DialogContentState();
}
 
class DialogContentState extends State<DialogContent> {
  String label = '';
 
  @override
  void initState() {
    super.initState();
    label = widget.label;
  }
 
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Text(label),
      onTap: () {
        setState(() {
          label = 'test9';
        });
      },
    );
  }
}

现在网上的教程都是说这么写的,比起用builder要麻烦一些,但是也更容易理解,不容易出错。
参考.

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • https://blog.csdn.net/yumi0629/article/details/81939936
    o动感超人o阅读 2,925评论 0 50
  • 今天终于收到了期待已久的2019跨年跑奖牌,外观漂亮美观惹人喜爱,沉甸甸的分量积淀了自己这半年来的坚持与汗水。...
    想吃一碗燃面阅读 193评论 0 1
  • 想给UITextView添加一个功能,长按呼出新的界面。 恩,开始干吧! 首先先创建一个长按手势。 UILongP...
    jiezheng阅读 10,984评论 1 6
  • 我买了早上的车票,一早起醒来,望着窗外对面房子的檐角,闭了闭眼。不想离家,可又想念学校。矛盾。母亲说:等你回到学...
    等风的小疯子阅读 312评论 1 0
  • 现在人已经快要三十,发现有很多问题没有认真去对待,比如身体健康,经常锻炼,比如按时睡觉,保持良好的精神状态很重要。...
    思思的心事阅读 192评论 0 0