手记

[Flutter必备]-StatefulWidget的打开方式

0.前言

刚接触Flutter的小伙伴在StatefulWidget控件时会感觉难以接受
本人一开始也是,不过对React的了解让我很快理解了Flutter的状态观念
本篇就说一下我对StatefulWidget一族的理解,希望可以帮你解决一些疑虑


1.从Slider开始说起

也许你在第一次使用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有用武之地

现在回想一下Android怎么改变属性

在Android里控件修改其属性可以直接`对象.set属性`来设置   但在FLutter里你会奇怪的发现:当你`slider.value=20;`时会报错   这真是让人不爽,对象更改属性不是天经地义吗?但Flutter说:对不起,你不能 复制代码

这让我恍然大悟,为什么Widget源码里说所有的组件都是恒定的,它只是对元素的描述
组件的属性无法被改变因为属性都是final修饰的,既然无法修改,那又为什么会有状态一说?

其实恒定和变化是相对的,多个恒定的状态的连续重演就会产生动态效果   就像电影也只是图片的叠加,一张图片是恒定的,它也只是用像素对一个场景的色彩信息进行的描述 但多个恒定的照片连续播放时就会产生动态的效果,让我们感觉里面的人是活的,世界是运动的 这其中化腐朽为神奇的关键就是如何持续渲染,就像电影如何连续一帧帧的播放   这时状态类中的setState()应声而出,交给我,只要喊我一声,我就为你们更新状态   复制代码

这和React是如出一辙的,这种方式在我看来是非常优雅的。对象更改自身属性与之相比就笨重了许多
前者可以通过一个状态来表述、更新、修改自己,而后者只是能通过他本身来亲力亲为


3:如何正确打开Slider

上面说需要状态,那就需要一个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:这样的优势

可能你会觉得只是使用一个Slider,还要写个类,未免有点小题大做

麻烦必然有其价值,简单必然有其局限。这便是宇宙的平衡。 一开始学编程时,定义了一个Circle类,可以用对象来算面积, 当时就想,这有必要吗,一个方法就搞定了啊,是不是有点小题大做。 之后渐渐发现面向对象的魅力,我不知你们对万物皆对象如何理解,这里说一下我的看法: 万物皆对象并不是站在人类的角度说世间的实体都是对象,而是站在另一个维度   一个应用便是一个小世界,里面有众多对象相互协调合作,来完成应用的功能。 这个小世界中的一切皆为对象。Coder需要管理这些对象的样貌,生死,家族关系,社交关系以及工作流程。 而对象的产生是要靠类来创建,所以类是至关重要的,其创建需要站在统领世界的上帝视角。 所以编程对我而言就是在创世,而我便是创世神,思想的高度可以让你的眼前有一个完全不一样的世界。   复制代码

话说回来,为什么要这样做呢?

三个词: 易复用、好维护、可拓展 这三个词会伴随Coder的编程生涯,如何让自己创造的世界更好的运作,是我们殚精竭虑的   从设计模式到数据结构,从编码到重构,我们努力调整维持这个世界的秩序,让它们脱离bug的魔爪   面向过程中的零星代码通过一个类的整合,形成一个创物的蓝图,用来召唤(new)对象 不知你是否有所感觉,Android中控件用起来是比较卡手的,总的来说就是太难复用,代码零星   比如,一个Slider滑动时Text跟随显示,在Activity中创建两个对象,让两者协调, 一两个还好,多了就会感觉分布零散,而且冗余难看,为此自定义一个View?还是饶了我吧   Android中控件的组合感觉很笨重,就连点击一下还有先找个id,但我也此心不改,未之乐此不疲,没办法,这就是爱   玩前端接触React的时候我就像寻到新欢,React的组件非常吸引我,灵活,简洁,优雅   再看Android,恨铁不成钢。直到现在Flutter出现了,它带着React的风采出现在移动端,甚至全端   Flutter中对于界面感觉非常友好,虽然刚来时一堆括号的嵌套让人难以适应,但渐渐你会发现他的美   Widget认为界面上的元素都成为组件,使用简单,非常容易复用。 复制代码

5:组件间的组合

看一下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:状态的魅力

比如需要象下面这样滑动到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:关于修改

你也可以很容易的通过布局容器修改组件的相对位置



var show = Row(   mainAxisSize: MainAxisSize.min,   crossAxisAlignment: CrossAxisAlignment.center,   children: <Widget>[     Column(       mainAxisAlignment: MainAxisAlignment.center,       children: <Widget>[         _buildCheckBox(_value),         _buildText(_value),       ],     ),     _buildSlider(_value)   ], ); 复制代码

8.关于监听

要知道你定义的每个组件都是可以拿去复用的,和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.复用的灵活

一个组件类形成之后,复用就非常方便了,如果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的魅力吧,相信你会喜欢上它的。



1人推荐
随时随地看视频
慕课网APP