Flutter更新showDialog中的内容

问题描述

Flutter中使用showDialogshowModalBottomSheet后,通过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构建的控件,不会响应老页面的任何操作,因为它们是两个互不影响的路由控制的。

正确的姿势如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
showDialog(
context: context,
builder: (context) {
String label = 'test';
return StatefulBuilder(
builder: (context, state) {
print('label = $label');
return GestureDetector(
child: Text(label),
onTap: () {
label = 'test2';
print('onTap: label = $label');
// 注意不是调用老页面的setState,而是要调用builder中的setState
// 在这里为了区分,在构建builder的时候将setState方法命名为了state
state(() {});
},
);
},
);
}
);

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

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

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

自定义的StatefulWidget

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

1
2
3
4
5
6
7
8
9
showDialog(
context: context,
builder: (context) {
String label = 'test';
return AddToCartDialog(
label: label,
);
}
);
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
class AddToCartDialog extends StatefulWidget {
final String label;

AddToCartDialog({Key key, this.label}) : super(key: key);

@override
State<StatefulWidget> createState() => AddToCartDialogState();
}

class AddToCartDialogState extends State<AddToCartDialog> {
String label = '';

@override
void initState() {
super.initState();
label = widget.label;
}

@override
Widget build(BuildContext context) {
return GestureDetector(
child: Text(label),
onTap: () {
setState(() {
label = 'test2';
});
},
);
}
}

比起使用builder要麻烦一些,但是也更容易理解,不容易出错。

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :