0.前言
剛接觸Flutter的小伙伴在StatefulWidget控件時會感覺難以接受
本人一開始也是,不過對React的了解讓我很快理解了Flutter的狀態(tài)觀念
本篇就說一下我對StatefulWidget一族的理解,希望可以幫你解決一些疑慮
1.從Slider開始說起
也許你在第一次使用Slider的時候會碰壁,你會發(fā)現(xiàn)它拖不動!
但如果你比較細心可以發(fā)現(xiàn)監(jiān)聽的值是在變化的,這跟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有用武之地
現(xiàn)在回想一下Android怎么改變屬性
在Android里控件修改其屬性可以直接`對象.set屬性`來設(shè)置
但在FLutter里你會奇怪的發(fā)現(xiàn):當你`slider.value=20;`時會報錯
這真是讓人不爽漓柑,對象更改屬性不是天經(jīng)地義嗎?但Flutter說:對不起镊折,你不能
這讓我恍然大悟焙矛,為什么Widget源碼里說
所有的組件都是恒定的
,它只是對元素的描述
組件的屬性無法被改變因為屬性都是final修飾的,既然無法修改,那又為什么會有狀態(tài)一說?
其實恒定和變化是相對的,多個恒定的狀態(tài)的連續(xù)重演就會產(chǎn)生動態(tài)效果
就像電影也只是圖片的疊加缚去,一張圖片是恒定的,它也只是用像素對一個場景的色彩信息進行的描述
但多個恒定的照片連續(xù)播放時就會產(chǎn)生動態(tài)的效果驶冒,讓我們感覺里面的人是活的件相,世界是運動的
這其中化腐朽為神奇的關(guān)鍵就是如何持續(xù)渲染,就像電影如何連續(xù)一幀幀的播放
這時狀態(tài)類中的setState()應(yīng)聲而出,交給我,只要喊我一聲财边,我就為你們更新狀態(tài)
這和React是如出一轍的,這種方式在我看來是非常優(yōu)雅的肌括。對象更改自身屬性與之相比就笨重了許多
前者可以通過一個狀態(tài)來表述、更新酣难、修改自己谍夭,而后者只是能通過他本身來親力親為
3:如何正確打開Slider
上面說需要狀態(tài),那就需要一個StatefulWidget,如下:有一個私有的變量_value,
在Slider拖動的過程中執(zhí)行_render方法進行渲染,在渲染時先將Slider的值給_value
在setState方法調(diào)用之后憨募,build將會重新執(zhí)行紧索,那么Slider的值就會使用_value,從而實現(xiàn)狀態(tài)的更新
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:這樣的優(yōu)勢
可能你會覺得只是使用一個Slider,還要寫個類菜谣,未免有點小題大做
麻煩必然有其價值,簡單必然有其局限珠漂。這便是宇宙的平衡晚缩。
一開始學編程時,定義了一個Circle類,可以用對象來算面積,
當時就想媳危,這有必要嗎荞彼,一個方法就搞定了啊,是不是有點小題大做。
之后漸漸發(fā)現(xiàn)面向?qū)ο蟮镊攘?我不知你們對萬物皆對象如何理解,這里說一下我的看法:
萬物皆對象并不是站在人類的角度說世間的實體都是對象,而是站在另一個維度
一個應(yīng)用便是一個小世界,里面有眾多對象相互協(xié)調(diào)合作,來完成應(yīng)用的功能待笑。
這個小世界中的一切皆為對象鸣皂。Coder需要管理這些對象的樣貌,生死,家族關(guān)系滋觉,社交關(guān)系以及工作流程签夭。
而對象的產(chǎn)生是要靠類來創(chuàng)建,所以類是至關(guān)重要的,其創(chuàng)建需要站在統(tǒng)領(lǐng)世界的上帝視角。
所以編程對我而言就是在創(chuàng)世,而我便是創(chuàng)世神,思想的高度可以讓你的眼前有一個完全不一樣的世界椎侠。
話說回來,為什么要這樣做呢?
三個詞: 易復用第租、好維護、可拓展
這三個詞會伴隨Coder的編程生涯我纪,如何讓自己創(chuàng)造的世界更好的運作慎宾,是我們殫精竭慮的
從設(shè)計模式到數(shù)據(jù)結(jié)構(gòu),從編碼到重構(gòu),我們努力調(diào)整維持這個世界的秩序,讓它們脫離bug的魔爪
面向過程中的零星代碼通過一個類的整合浅悉,形成一個創(chuàng)物的藍圖趟据,用來召喚(new)對象
不知你是否有所感覺,Android中控件用起來是比較卡手的,總的來說就是太難復用术健,代碼零星
比如汹碱,一個Slider滑動時Text跟隨顯示,在Activity中創(chuàng)建兩個對象,讓兩者協(xié)調(diào),
一兩個還好荞估,多了就會感覺分布零散咳促,而且冗余難看,為此自定義一個View?還是饒了我吧
Android中控件的組合感覺很笨重,就連點擊一下還有先找個id,但我也此心不改勘伺,未之樂此不疲,沒辦法跪腹,這就是愛
玩前端接觸React的時候我就像尋到新歡,React的組件非常吸引我,靈活,簡潔飞醉,優(yōu)雅
再看Android,恨鐵不成鋼冲茸。直到現(xiàn)在Flutter出現(xiàn)了,它帶著React的風采出現(xiàn)在移動端,甚至全端
Flutter中對于界面感覺非常友好,雖然剛來時一堆括號的嵌套讓人難以適應(yīng)缅帘,但漸漸你會發(fā)現(xiàn)他的美
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),//保留兩位有效數(shù)字
style: TextStyle(fontSize: 20),
);
}
6:狀態(tài)的魅力
比如需要象下面這樣滑動到50之后復選框選中逗栽,當點擊復選框清零
放在Android中想想都覺得凌亂,但自定義控件有麻煩,就像爐石起手全是高費的卡手心情
在Flutter中你想怎么封怎么封,只要狀態(tài)改變铃诬,我就給你響應(yīng)祭陷,這是很優(yōu)雅的。
---->[_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:關(guān)于修改
你也可以很容易的通過布局容器修改組件的相對位置
var show = Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildCheckBox(_value),
_buildText(_value),
],
),
_buildSlider(_value)
],
);
8.關(guān)于監(jiān)聽
要知道你定義的每個組件都是可以拿去復用的兵志,和Flutter原生組件地位是一樣的
我們在需要拖動的監(jiān)聽,那么就需要在渲染之前進行回調(diào),讓使用者可以接受回參
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實現(xiàn)下面的拖動更新
邏輯上不復雜,但是代碼將會非常多,因為Android很難復用組件霉涨,只能一個個來按价。
Flutter中實現(xiàn)起來就很簡潔,甚至監(jiān)聽也非常方便笙瑟。比如下面的:
短短幾行代碼就實現(xiàn)了四個的各自拖動監(jiān)聽,這是笨重的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.小結(jié)
Flutter針對界面是非常友好的楼镐,它可以作為Android視圖很好地補充。
更不用說Flutter強大的跨平臺能力,它已成為一顆新星,正冉冉升起往枷。
你還在等什么,見證一下Flutter的魅力吧框产,相信你會喜歡上它的。
結(jié)語
本文到此接近尾聲了错洁,如果想快速嘗鮮Flutter秉宿,《Flutter七日》會是你的必備佳品;如果想細細探究它,那就跟隨我的腳步,完成一次Flutter之旅屯碴。
另外本人有一個Flutter微信交流群描睦,歡迎小伙伴加入,共同探討Flutter的問題导而,本人微信號:zdl1994328
忱叭,期待與你的交流與切磋。