前言
簡(jiǎn)單粗暴來(lái)說(shuō)衣陶,一個(gè)widget,如果在運(yùn)行的過(guò)程中需要變化闸氮,就是有狀態(tài)的剪况。
Fluter的widget,安卓狀態(tài)來(lái)說(shuō)蒲跨,可以分為:有狀態(tài)(StatefulWidget)和無(wú)狀態(tài)(StatelessWidget)
無(wú)狀態(tài)(StatelessWidget)
狀態(tài)組件指的就是其內(nèi)部的狀態(tài)是來(lái)自其父組件
并使用final類(lèi)型的變量來(lái)存儲(chǔ)
译断,當(dāng)組件被build
的時(shí)候它們就使用這些不可變的數(shù)據(jù)
來(lái)構(gòu)建自己的UI。
無(wú)狀態(tài)的組件或悲,前面我們已經(jīng)使用無(wú)數(shù)次了孙咪,重點(diǎn)是看StatefulWidget,有狀態(tài)的組件巡语。
有狀態(tài)(StatefulWidget)
有狀態(tài)的wdiget翎蹈,其持有狀態(tài)可能在Widget生命周期中發(fā)生變化。
Checkbox捌臊、Radio杨蛋、Slider、InkWell理澎、Form和TextField是有狀態(tài)小部件的示例逞力,它們是StatefulWidget的子類(lèi)。
對(duì)于有可變狀態(tài)控件的管理糠爬,官方文檔是寫(xiě)了有3種模式:
控件自己管理狀態(tài)
寇荧、交給父控件管理狀態(tài)
以及混合管理 (自己和父部件各管理一部分)
。
實(shí)現(xiàn)一個(gè)StatefulWidget至少需要兩個(gè)類(lèi):
一個(gè)StatefulWidget
類(lèi)
一個(gè)State
類(lèi)执隧。
- StatefulWidget類(lèi)本身是不變的揩抡,但是State類(lèi)在Widget生命周期中始終存在,當(dāng)在State內(nèi)部改變?nèi)魏巫涌丶枰淖兞繒r(shí)户侥,都需要使用
setState
。
一句話來(lái)說(shuō)峦嗤,就是一個(gè)有狀態(tài)的組件蕊唐,需要兩個(gè)類(lèi),分別是StatefulWidget和State烁设,而狀態(tài)組件的內(nèi)容替梨,需要用到setState方法。
一装黑、 有狀態(tài)(StatefulWidget)
實(shí)現(xiàn)StatefulWidget需要的兩個(gè)類(lèi)
實(shí)現(xiàn)一個(gè)StatefulWidget至少需要兩個(gè)類(lèi):
一個(gè)StatefulWidget
類(lèi)
一個(gè)State
類(lèi)副瀑。
如何改變StatefulWidget的內(nèi)容
- StatefulWidget類(lèi)本身是不變的,但是State類(lèi)在Widget生命周期中始終存在,當(dāng)在State內(nèi)部改變?nèi)魏巫涌丶枰淖兞繒r(shí)恋谭,都需要使用
setState
糠睡。
例子
例子1 控件自己管理狀態(tài)
一個(gè)計(jì)數(shù)器,點(diǎn)擊自身疚颊,統(tǒng)計(jì)點(diǎn)擊次數(shù)狈孔。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
// final wordPair = new WordPair.random();
return MaterialApp(
title: 'Flutter 測(cè)試標(biāo)題',
theme: new ThemeData(
primaryColor: Colors.red,
),
home: new Scaffold(
appBar: AppBar(
title: Text("測(cè)試呀"),
),
body: new Counter(),
));
}
}
class Counter extends StatefulWidget {
// 經(jīng)典寫(xiě)法,繼承自StatefulWidget的Widget返回一個(gè)自己的State私有類(lèi)
_CounterState createState() => new _CounterState();
}
class _CounterState extends State<Counter> {
// State類(lèi)里面定義私有變量串稀,我們的狀態(tài)變化除抛,會(huì)通過(guò)setState方法具體操作
int _count = 0;
void _increment() {
setState(() {
++_count;
});
}
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(color: Colors.grey[100]),
child: new Center(
child: new RaisedButton(
// 按下時(shí)的事件
onPressed: _increment,
// 內(nèi)容不寫(xiě)死,數(shù)據(jù)動(dòng)態(tài)顯示母截,一切都是因?yàn)開(kāi)increment里面的setState方法
child: new Text('click count : ${_count}'))));
}
}
.
.
重要都在備注里面了到忽,如果非要說(shuō)說(shuō)邏輯,大概就是:
- 1清寇、繼承自StatefulWidget的Widget返回一個(gè)自己的State私有類(lèi)
- 2喘漏、State類(lèi)里面定義私有變量,我們的狀態(tài)變化华烟,會(huì)通過(guò)setState方法具體操作
- 3翩迈、通過(guò)一定的事件或者條件,觸發(fā)setState改變數(shù)據(jù)
- 4盔夜、數(shù)據(jù)展示的邏輯负饲,不寫(xiě)死,根據(jù)變量的調(diào)整動(dòng)態(tài)調(diào)整
.
.
例子2 父控件管理子控件狀態(tài)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
// final wordPair = new WordPair.random();
return MaterialApp(
title: 'Flutter 測(cè)試標(biāo)題',
theme: new ThemeData(
primaryColor: Colors.red,
),
home: new Scaffold(
appBar: AppBar(
title: Text("測(cè)試呀"),
),
body: new ParentWidget(),
));
}
}
//------------------------- parent widget ----------------------------------
class ParentWidget extends StatefulWidget {
ParentWidget() {
print('ParentWidget init');
}
@override
State<StatefulWidget> createState() {
return _ParentWidgetState();
}
}
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
_ParentWidgetState() {
print('ParentWidgetState init');
}
void _handleSonChanged(bool newValue) {
print('parent _handleSonChanged is call : $newValue');
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
print('ParentWidgetState build is call');
return new Center(
child: SonWidget(onChanged: _handleSonChanged, isShowAndroid: _active,),
);
}
}
//------------------------- child widget ----------------------------------
class SonWidget extends StatelessWidget {
SonWidget({
Key key, this.isShowAndroid: false,
@required this.onChanged
}): super(key: key) {
print('Son SonWidget init : ${this.isShowAndroid}');
}
final bool isShowAndroid;
final ValueChanged<bool> onChanged;
void _handleSon() {
print('child _handleSon : $isShowAndroid');
onChanged(!isShowAndroid);
}
@override
Widget build(BuildContext context) {
print('Son SonWidget build method');
return GestureDetector(
onTap: _handleSon,
child: Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: isShowAndroid ? Colors.lightGreen[700] : Colors.grey[600]
),
child: Center(
child: Text(isShowAndroid ? 'Android' : 'Flutter',
style: TextStyle(fontSize: 32.0, color: Colors.white),),
),
),
);
}
}
核心:子控件的構(gòu)造方法喂链,暴露出事件參數(shù)返十,父控件創(chuàng)建子控件的時(shí)候,傳入?yún)?shù)椭微,進(jìn)而實(shí)現(xiàn)父控件改變自控內(nèi)容的目的洞坑。
所以核心代碼:
初始化順序
初始化輸出日志:
flutter: ParentWidget init
flutter: ParentWidgetState build is call
flutter: Son SonWidget init : false
flutter: Son SonWidget build method
Reloaded 1 of 432 libraries in 2,751ms.
點(diǎn)擊回調(diào)執(zhí)行順序
點(diǎn)擊輸出日志:
第一次點(diǎn)擊
flutter: child _handleSon : false
flutter: parent _handleSonChanged is call : true
flutter: ParentWidgetState build is call
flutter: Son SonWidget init : true
flutter: Son SonWidget build method
第二次點(diǎn)擊
flutter: child _handleSon : true
flutter: parent _handleSonChanged is call : false
flutter: ParentWidgetState build is call
flutter: Son SonWidget init : false
flutter: Son SonWidget build method
第三次點(diǎn)擊
flutter: Son SonWidget init : false
flutter: Son SonWidget build method
flutter: child _handleSon : false
flutter: parent _handleSonChanged is call : true
flutter: ParentWidgetState build is call
flutter: Son SonWidget init : true
flutter: Son SonWidget build method
一切是那么重新,在父控件控制子控件的時(shí)蝇率,刷新ui會(huì)讓子控件重新初始化和和build(繪制)迟杂。
.
.
.
.
例子3 混合管理
也就是刽沾,父控件管,子控件自己也管排拷,一起管
混合管理就是某些狀態(tài)由自己管理侧漓,某些狀態(tài)由父部件來(lái)管理。
下面的例子就是一個(gè)混合管理狀態(tài)的例子攻泼,部件 TabboxC 在被點(diǎn)擊時(shí)有三個(gè)狀態(tài)變換火架,背景色鉴象,文字和邊框忙菠。
示例中,背景色和文字的狀態(tài)交由父部件來(lái)管理(和上一個(gè)示例類(lèi)似)纺弊,而邊框狀態(tài)由自己管理牛欢。
既然父部件和子部件都能管理狀態(tài),那么它們都是要繼承StatefulWidget類(lèi)淆游。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
// final wordPair = new WordPair.random();
return MaterialApp(
title: 'Flutter 測(cè)試標(biāo)題',
theme: new ThemeData(
primaryColor: Colors.red,
),
home: new Scaffold(
appBar: AppBar(
title: Text("測(cè)試呀"),
),
body: new ParentWidget2(),
));
}
}
// ------------parent widget-----------
class ParentWidget2 extends StatefulWidget {
ParentWidget2() {
print('Parent init');
}
@override
State<StatefulWidget> createState() {
return _ParentWidgetState2();
}
}
class _ParentWidgetState2 extends State<ParentWidget2> {
_ParentWidgetState2() {
print('_Parent State init');
}
bool _active = false;
void _handleTapboxChanged(bool newValue) {
print('_Parent _handleTapboxChanged method is called');
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
print('_Parent State build is called');
return TabboxC(onChanged: _handleTapboxChanged, active: _active,);
}
}
// ------------child widget-----------
class TabboxC extends StatefulWidget {
// 構(gòu)造方法
TabboxC({
Key key,
this.active: false,
@required this.onChanged
}) : super(key: key) {
print('TabboxC init');
}
final bool active;
final ValueChanged<bool> onChanged;
@override
State<StatefulWidget> createState() {
return _TapboxCState();
}
}
class _TapboxCState extends State<TabboxC> {
bool _highlight = false;
_TapboxCState() {
print('_TapboxC State init');
}
void _handleTapDown(TapDownDetails details) {
print('_TapboxC tap down');
setState(() {
_highlight = true;
});
}
void _handleTapUp(TapUpDetails details) {
print('_TapboxC tap up');
setState(() {
_highlight = false;
});
}
void _handleTapCancel() {
print('_TapboxC tap cancel');
setState(() {
_highlight = false;
});
}
void _handleTap() {
print('_TapboxC tap clicked');
widget.onChanged(!widget.active);
}
@override
Widget build(BuildContext context) {
print('_TapboxCState build is called');
return Center(
child: GestureDetector(
// down
onTapDown: _handleTapDown,
// up
onTapUp: _handleTapUp,
// cancel
onTapCancel: _handleTapCancel,
// click
onTap: _handleTap,
child: Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
// Box 顏色 父控件 控制(通過(guò)回調(diào)方法)
color: widget.active ? Colors.lightGreen[700] : Colors
.grey[600],
// 邊框顏色 自己控制
border: _highlight ? Border.all(
color: Colors.teal[700], width: 10.0) : null
),
child: Center(
child: Text(widget.active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),),
),
),
),
);
}
}
按下釋放后的一瞬間傍睹,才有邊框。
初始化
初始化時(shí)候的順序和上面類(lèi)似犹菱,我們來(lái)看看點(diǎn)擊事件被觸發(fā)時(shí)候的執(zhí)行順序:
flutter: _TapboxC tap down
flutter: _TapboxCState build is call
flutter: _TapboxC tap up
flutter: _TapboxC tap clicked
flutter: _Parent _handleTapboxChanged method is call
flutter: _Parent State build is call
flutter: TabboxC init
flutter: _TapboxCState build is call
執(zhí)行流程:
大家可能會(huì)發(fā)現(xiàn)拾稳,子部件在 Down 事件中調(diào)用了 setState(...) 方法,然后執(zhí)行了一次 build 操作腊脱;而在 Up 事件中同樣也調(diào)用了 setState(...) 方法访得,但是為什么沒(méi)有執(zhí)行 build 操作,而是直接執(zhí)行了 click 操作陕凹。這里面可能和 Android 里面類(lèi)似悍抑,在 View 的 onTouchEvent 方法里面,onClick 方法也是在 ACTION_UP 里面執(zhí)行的杜耙。
END