0.前言
刚接触Flutter的小伙伴在StatefulWidget控件时会感觉难以接受 本人一开始也是,不过对React的了解让我很快理解了Flutter的状态观念 本篇就说一下我对StatefulWidget一族的理解,希望可以帮你解决一些疑虑
1.从Slider开始说起
代码语言:javascript复制也许你在第一次使用Slider的时候会碰壁,你会发现它拖不动! 但如果你比较细心可以发现监听的值是在变化的,这跟Android是不同的
var slider = Slider(
value: 0,
max: 100,
min: 0,
onChanged: (e) {
print('onChanged:$e');
},
onChangeStart: (e) {
print('onChangeStart:$e');
},
onChangeEnd: (e) {
print('onChangeEnd:$e');
});
///------主场景-------
var scaffold = Scaffold(
body: Center(child: slider,)
);
var app = MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: scaffold,
);
void main() => runApp(app);
2:如何让Slider有用武之地
代码语言:javascript复制现在回想一下Android怎么改变属性
在Android里控件修改其属性可以直接`对象.set属性`来设置
但在FLutter里你会奇怪的发现:当你`slider.value=20;`时会报错
这真是让人不爽,对象更改属性不是天经地义吗?但Flutter说:对不起,你不能
代码语言:javascript复制这让我恍然大悟,为什么Widget源码里说
所有的组件都是恒定的
,它只是对元素的描述
组件的属性无法被改变因为属性都是final修饰的,既然无法修改,那又为什么会有状态一说?
其实恒定和变化是相对的,多个恒定的状态的连续重演就会产生动态效果
就像电影也只是图片的叠加,一张图片是恒定的,它也只是用像素对一个场景的色彩信息进行的描述
但多个恒定的照片连续播放时就会产生动态的效果,让我们感觉里面的人是活的,世界是运动的
这其中化腐朽为神奇的关键就是如何持续渲染,就像电影如何连续一帧帧的播放
这时状态类中的setState()应声而出,交给我,只要喊我一声,我就为你们更新状态
这和React是如出一辙的,这种方式在我看来是非常优雅的。对象更改自身属性与之相比就笨重了许多 前者可以通过一个状态来表述、更新、修改自己,而后者只是能通过他本身来亲力亲为
3:如何正确打开Slider
代码语言:javascript复制上面说需要状态,那就需要一个StatefulWidget,如下:有一个私有的变量_value, 在Slider拖动的过程中执行_render方法进行渲染,在渲染时先将Slider的值给_value 在setState方法调用之后,build将会重新执行,那么Slider的值就会使用_value,从而实现状态的更新
class TextSlider extends StatefulWidget {
@override
State<StatefulWidget> createState() => _TextSliderState();
}
class _TextSliderState extends State<TextSlider> {
double _value = 0;
@override
Widget build(BuildContext context) {
var show = _buildSlider(_value);
return show;
}
_buildSlider(double value) {
var slider = Slider(
value: value,
max: 100,
min: 0,
onChanged: (e) {
print('onChanged:$e');
_render();
},
onChangeStart: (e) {
print('onChangeStart:$e');
},
onChangeEnd: (e) {
print('onChangeEnd:$e');
},
);
return slider;
}
_render(double value) {
_value = value;
setState(() {});
}
}
4:这样的优势
代码语言:javascript复制可能你会觉得只是使用一个Slider,还要写个类,未免有点小题大做
麻烦必然有其价值,简单必然有其局限。这便是宇宙的平衡。
一开始学编程时,定义了一个Circle类,可以用对象来算面积,
当时就想,这有必要吗,一个方法就搞定了啊,是不是有点小题大做。
之后渐渐发现面向对象的魅力,我不知你们对万物皆对象如何理解,这里说一下我的看法:
万物皆对象并不是站在人类的角度说世间的实体都是对象,而是站在另一个维度
一个应用便是一个小世界,里面有众多对象相互协调合作,来完成应用的功能。
这个小世界中的一切皆为对象。Coder需要管理这些对象的样貌,生死,家族关系,社交关系以及工作流程。
而对象的产生是要靠类来创建,所以类是至关重要的,其创建需要站在统领世界的上帝视角。
所以编程对我而言就是在创世,而我便是创世神,思想的高度可以让你的眼前有一个完全不一样的世界。
代码语言:javascript复制话说回来,为什么要这样做呢?
三个词: 易复用、好维护、可拓展
这三个词会伴随Coder的编程生涯,如何让自己创造的世界更好的运作,是我们殚精竭虑的
从设计模式到数据结构,从编码到重构,我们努力调整维持这个世界的秩序,让它们脱离bug的魔爪
面向过程中的零星代码通过一个类的整合,形成一个创物的蓝图,用来召唤(new)对象
不知你是否有所感觉,Android中控件用起来是比较卡手的,总的来说就是太难复用,代码零星
比如,一个Slider滑动时Text跟随显示,在Activity中创建两个对象,让两者协调,
一两个还好,多了就会感觉分布零散,而且冗余难看,为此自定义一个View?还是饶了我吧
Android中控件的组合感觉很笨重,就连点击一下还有先找个id,但我也此心不改,未之乐此不疲,没办法,这就是爱
玩前端接触React的时候我就像寻到新欢,React的组件非常吸引我,灵活,简洁,优雅
再看Android,恨铁不成钢。直到现在Flutter出现了,它带着React的风采出现在移动端,甚至全端
Flutter中对于界面感觉非常友好,虽然刚来时一堆括号的嵌套让人难以适应,但渐渐你会发现他的美
Widget认为界面上的元素都成为组件,使用简单,非常容易复用。
5:组件间的组合
代码语言:javascript复制看一下Flutter中组合Slider和Text是多么简洁,只要添加一些就行了 如果Android自定义这样的控件,需要自定义ViewGroup,将两个组件拼合 所以Flutter中组件的拼合是非常方便的,使用也很简洁
---->[_TextSliderState#build]----
var show = Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[_buildText(_value), _buildSlider(_value)],
);
_buildText(double value) {
return Text(
value.toStringAsFixed(2),//保留两位有效数字
style: TextStyle(fontSize: 20),
);
}
6:状态的魅力
代码语言:javascript复制比如需要象下面这样滑动到50之后复选框选中,当点击复选框清零 放在Android中想想都觉得凌乱,但自定义控件有麻烦,就像炉石起手全是高费的卡手心情 在Flutter中你想怎么封怎么封,只要状态改变,我就给你响应,这是很优雅的。
---->[_TextSliderState#build]----
var show = Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildCheckBox(_value),
_buildText(_value),
],
),
_buildSlider(_value)
],);
_buildCheckBox(double value) {
var checked = value > 50;
return Checkbox(
value: checked,
onChanged: (bool) {
_render(0);
},
);
}
只是修改嵌套是有点小麻烦,如果有类似
wedgetChild.father=wedgetFather
这样的认干爹就好了
7:关于修改
代码语言:javascript复制你也可以很容易的通过布局容器修改组件的相对位置
var show = Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildCheckBox(_value),
_buildText(_value),
],
),
_buildSlider(_value)
],
);
8.关于监听
代码语言:javascript复制要知道你定义的每个组件都是可以拿去复用的,和Flutter原生组件地位是一样的 我们在需要拖动的监听,那么就需要在渲染之前进行回调,让使用者可以接受回参
class TextSlider extends StatefulWidget {
ValueChanged onChanged;
TextSlider({this.onChanged});
@override
State<StatefulWidget> createState() => _TextSliderState();
}
typedef ValueChanged = void Function(double value);
_render(double value) {
if(widget.onChanged!=null){
widget.onChanged(value);
}
_value = value;
print(value);
setState(() {}); }
9.复用的灵活
代码语言:javascript复制一个组件类形成之后,复用就非常方便了,如果Android实现下面的拖动更新 逻辑上不复杂,但是代码将会非常多,因为Android很难复用组件,只能一个个来。 Flutter中实现起来就很简洁,甚至监听也非常方便。比如下面的: 短短几行代码就实现了四个的各自拖动监听,这是笨重的xml所不能及的
var a = (a) {
print("a:$a");
};
var b = (b) {
print("b:$b");
};
var c = (c) {
print("c:$c");
};
var d = (d) {
print("d:$d");
};
var childs= [a,b,c,d].map((fun){
return SizedBox.fromSize(size: Size(250, 100), child: TextSlider(onChanged: fun,),);
}).toList();
var scaffold = Scaffold(
body: Center(child: Wrap(children:childs,),)
);
复制代码
10.小结
Flutter针对界面是非常友好的,它可以作为Android视图很好地补充。 更不用说Flutter强大的跨平台能力,它已成为一颗新星,正冉冉升起。 你还在等什么,见证一下Flutter的魅力吧,相信你会喜欢上它的。