Sate获取StatefulWidget参数值常见的两种方式
1、StatefulWidget传参给State,在Sate直接使用StatefulWidget传来的参数值,如下:
class TestStatefulWidget extends StatefulWidget {
//参数
final String _xxx;
TestStatefulWidget(this._xxx);
@override
_TestStatefulWidgetState createState() => _TestStatefulWidgetState(this._xxx);
}
class _TestStatefulWidgetState extends State<TestStatefulWidget> {
final _xxx;
_TestStatefulWidgetState(this._xxx);
@override
Widget build(BuildContext context) {
return Container(
child: Text(_xxx), //直接使用StatefulWidget传来的参数值
);
}
}
2、StatefulWidget不传参给State,在Sate通过widget.xxx获取StatefulWidget的参数值,如下:
class TestStatefulWidget extends StatefulWidget {
//参数
final String _xxx;
TestStatefulWidget(this._xxx);
@override
_TestStatefulWidgetState createState() => _TestStatefulWidgetState();
}
class _TestStatefulWidgetState extends State<TestStatefulWidget> {
_TestStatefulWidgetState();
@override
Widget build(BuildContext context) {
return Container(
child: Text(widget._xxx), //通过widget获取StatefulWidget参数值
);
}
}
Sate通过widget.xxx获取StatefulWidget参数值之坑
上面两种获取参值方式的差别在于StatefulWidget有没有将参数直接传给State,Sate是否通过widget.xxx获取StatefulWidget参数值;
下面通过实例来演示这两种获取参数值的差别,如下:
class StatefulWidgetParamPage extends StatefulWidget {
final String title;
StatefulWidgetParamPage({Key key, this.title}) : super(key: key);
@override
_StatefulWidgetParamPageState createState() =>
_StatefulWidgetParamPageState();
}
class _StatefulWidgetParamPageState extends State<StatefulWidgetParamPage> {
int i;
@override
void initState() {
super.initState();
i = 0;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
alignment: Alignment.center,
width: double.infinity,
height: double.infinity,
child: Column(
children: <Widget>[
TestWidgetA(
TestModel(
'WidgetA参数:',
),
i.toString()),
TestWidgetB(TestModel('WidgetB参数:'), i.toString()),
RaisedButton(
onPressed: () {
i++;
setState(() {});
},
child: Text('值增加刷新')),
RaisedButton(
onPressed: () {
i++;
},
child: Text('值增加不刷新')),
RaisedButton(
onPressed: () {
setState(() {});
},
child: Text('只刷新')),
],
)),
);
}
}
class TestModel {
String title;
dynamic value;
TestModel(
this.title,
) {
var r = new Random();
value = r.nextInt(1000); //用于区分对象是否有重新创建,对象重新创建,值会变化
}
@override
String toString() {
return '$title 自定义对象${value ?? ''}';
}
}
class TestWidgetA extends StatefulWidget {
final TestModel model;
final String value;
TestWidgetA(this.model, this.value);
@override
_TestWidgetAState createState() => _TestWidgetAState();
}
class _TestWidgetAState extends State<TestWidgetA> {
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Container(
alignment: Alignment.center,
child: Text(
'${widget?.model?.toString()} change value=${widget?.value}\ncontext=$context\n'), //展示传参数据 值随刷新变
),
onTap: () {
print(
'${widget?.model?.toString()} change value=${widget?.value}\ncontext=$context\n'); //值不变,刷新后widget重新创建,值才变
/*setState(() {//内部刷新
});*/
},
);
}
}
class TestWidgetB extends StatefulWidget {
final TestModel model;
final String value;
TestWidgetB(this.model, this.value);
@override
_TestWidgetBState createState() => _TestWidgetBState(this.model, this.value);
}
class _TestWidgetBState extends State<TestWidgetB> {
final TestModel model;
final String value;
_TestWidgetBState(this.model, this.value);
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Container(
alignment: Alignment.center,
child: Text(
'${model?.toString()} change value=$value\ncontext=$context\n'), //展示传参数据 值不随刷新变
),
onTap: () {
print(
'${model?.toString()} change value=$value\ncontext=$context\n'); //值不随刷新变
/*setState(() {//内部刷新
});*/
},
);
}
}
上面代码比较多,可以先不看,先看下面的实例效果或自行跑demo,不懂再来看代码和代码说明
代码说明:
- 上面代码通过TestWidgetA、TestWidgetB两个StatefulWidget来展示Sate获取StatefulWidget参数值不同方式带来的不同效果;
- TestWidgetA:StatefulWidget不传参给State,在Sate通过widget.xxx获取StatefulWidget的参数值;
- TestWidgetB:StatefulWidget传参给State,在Sate直接使用StatefulWidget传来的参数值;
- TestWidgetA、TestWidgetB参数值有:自定义对象TestModel,外部计数值;
- TestWidgetA、TestWidgetB除State获取StatefulWidget参数值方式不一样,其它都一样,同时展示StatefulWidget参数值:自定义对象、外部计数参数值;
- 自定义对象TestModel:
1、自定义对象TestModel,在构造方法初始化里用随机数赋值,可通过观察随机数值有没有变化来判断自定义对象有没有重新创建;
2、自定义对象TestModel作为TestWidgetA、TestWidgetB两个StatefulWidget的参数,并且自定义对象TestModel是直接实例化传给widgetTestWidgetA、TestWidgetB作为参数的; - 外部计数值,用于观察外部值变化 ,Sate获取StatefulWidget参数值是否会跟着变化;
- TestWidgetA、TestWidgetB展示各自的context是为了说明各自的State没重新创建,上面例子不会引起TestWidgetA和TestWidgetB各自Sate的重新创建;
- 三个按键功能分别是: 外部计数值增加并刷新界面、 外部计数值增加不刷新界面、只刷新界面;
- 还有TestWidgetA、TestWidgetB本身的点击事件,点击在log打印,可实时看到传参值,分为内部刷新和内部不刷新两种;
外部计数值增加增加并刷新界面效果:
外部计数值增加并刷新界面效果.gif
效果说明:
- 界面刷新时,每刷新一次,TestWidgetA(Sate 通过widget.xxx获取StatefulWidget参数值)的自定义对象参数值发生了重新创建,同时外面传来的参数值随着外面值的变化而变化;
- 界面刷新时,TestWidgetB(Sate 没有通过widget.xxx获取StatefulWidget参数值)的自定义对象参数值没有发生重新创建,外面传来的参数值也没有随着外面值的变化而变化,也就是TestWidgetB参数值没有发生变化;
外部计数值增加不刷新界面效果:
外部计数值增加不刷新界面效果.gif
效果说明:
- 只是值增加时,TestWidgetA、TestWidgetB的自定义对象参数没有发生重新创建,外面传来的参数值也没有随着外面值的变化而变化;
- 再刷新时,TestWidgetA(Sate 通过widget.xxx获取StatefulWidget参数值)的自定义对象参数值马上发生了重新创建,同时外面传来的参数值随着外面值的变化而变化;TestWidgetB(Sate 没有通过widget.xxx获取StatefulWidget参数值)参数值没有发生变化;
只刷新界面效果:
只刷新界面效果.gif
效果说明:
- 只刷新界面时,TestWidgetA(Sate 通过widget.xxx获取StatefulWidget参数值)的自定义对象参数值发生了重新创建;
- TestWidgetB(Sate 没有通过widget.xxx获取StatefulWidget参数值)参数值不会发生变化;
不刷新,连续点击获取实时参数值效果:
连续点击获取实时参数值效果.png
效果说明:
- 不刷新,实时获取TestWidgetA、TestWidgetB参数值,TestWidgetA、TestWidgetB参数值都不会发生变化;
内部刷新,连续点击获取实时参数值效果:
内部刷新,连续点击获取实时参数值效果.png
效果说明:
- 内部刷新,实时获取TestWidgetA、TestWidgetB参数,TestWidgetA、TestWidgetB参数都不会发生变化;
总结
StatefulWidget传参给State,在Sate直接使用StatefulWidget传来的参数值:
- 无论StatefulWidget内部刷新界面、外部刷新界面,外部参数值变化,在Sate直接获取StatefulWidget的参数值都不会改变;
StatefulWidget不传参给State,在Sate通过widget.xxx获取StatefulWidget传来的参数值:
- StatefulWidget内部刷新界面,外面值变化时,在Sate通过widget.xxx获取参数值不会变化;
- 外部界面刷新,在Sate通过widget.xxx获取StatefulWidget的参数值随外面值变化而变化;如果自定义对象直接实例化作为StatefulWidget参数,自定义对象参数值会随刷新而发生重新创建,这点需要重点留意,有可能界面刷新,自定义对象定义里的值就变成初始化的值了。
原因分析
1、外部界面刷新:
- 外部界面刷新时,Sate里的widget发生了重新创建
对于StatefulWidget,Sate和Widget是分离的,widget是一个临时配置对象,外部界面刷新时,Sate里的widget会发生重新创建初始化,widget重新创建初始化,必会重新执行StatefulWidget的构造方法,StatefulWidget的构造方法重新构建,StatefulWidget的构造方法里的参数必会跟着重新初始化传入,所以在Sate通过widget.xxx获取StatefulWidget的参数值随外部刷新而变化; - 外部界面刷新时,Sate没有发生重新创建
由于外部界面刷新时,Sate没有发生重新创建,StatefulWidget将参数传给State,也就是拷贝了一份参数值给Sate,所以即使widget发生了重新创建,Sate获取的到StatefulWidget的参数值还是最开始StatefulWidget拷贝的参数值,没有变化;
2、StatefulWidget内部刷新:
- StatefulWidget内部刷新时,Sate和widget都没有发生重新创建,所以无论Sate是否通过widget.xxx获取StatefulWidget传来的参数值,都没有变化;
温馨提示:源码里大多都是通过widget.xxx来获取StatefulWidget参数值,所以并不是说widget.xxx不可取,这两种方式各有优劣势,用哪一种根据自己的业务进行取舍,文章主要是为了说明Sate 通过widget.xxx 获取StatefulWidget参数值需要注意参数值可能随外部环境变化而会变化。