
前言
簡單粗暴來說,一個widget,如果在運(yùn)行的過程中需要變化,就是有狀態(tài)的。
Fluter的widget,安卓狀態(tài)來說,可以分為:有狀態(tài)(StatefulWidget)和無狀態(tài)(StatelessWidget)
無狀態(tài)(StatelessWidget)
狀態(tài)組件指的就是其內(nèi)部的狀態(tài)是來自其父組件并使用final類型的變量來存儲,當(dāng)組件被build的時候它們就使用這些不可變的數(shù)據(jù)來構(gòu)建自己的UI。
無狀態(tài)的組件,前面我們已經(jīng)使用無數(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的子類。
對于有可變狀態(tài)控件的管理,官方文檔是寫了有3種模式:
控件自己管理狀態(tài)、交給父控件管理狀態(tài)以及混合管理 (自己和父部件各管理一部分)。
實(shí)現(xiàn)一個StatefulWidget至少需要兩個類:
一個StatefulWidget類
一個State類。
- StatefulWidget類本身是不變的,但是State類在Widget生命周期中始終存在,當(dāng)在State內(nèi)部改變?nèi)魏巫涌丶枰淖兞繒r,都需要使用
setState。
一句話來說,就是一個有狀態(tài)的組件,需要兩個類,分別是StatefulWidget和State,而狀態(tài)組件的內(nèi)容,需要用到setState方法。
一、 有狀態(tài)(StatefulWidget)
實(shí)現(xiàn)StatefulWidget需要的兩個類
實(shí)現(xiàn)一個StatefulWidget至少需要兩個類:
一個StatefulWidget類
一個State類。
如何改變StatefulWidget的內(nèi)容
- StatefulWidget類本身是不變的,但是State類在Widget生命周期中始終存在,當(dāng)在State內(nèi)部改變?nèi)魏巫涌丶枰淖兞繒r,都需要使用
setState。
例子
例子1 控件自己管理狀態(tài)
一個計(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 測試標(biāo)題',
theme: new ThemeData(
primaryColor: Colors.red,
),
home: new Scaffold(
appBar: AppBar(
title: Text("測試呀"),
),
body: new Counter(),
));
}
}
class Counter extends StatefulWidget {
// 經(jīng)典寫法,繼承自StatefulWidget的Widget返回一個自己的State私有類
_CounterState createState() => new _CounterState();
}
class _CounterState extends State<Counter> {
// State類里面定義私有變量,我們的狀態(tài)變化,會通過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(
// 按下時的事件
onPressed: _increment,
// 內(nèi)容不寫死,數(shù)據(jù)動態(tài)顯示,一切都是因?yàn)開increment里面的setState方法
child: new Text('click count : ${_count}'))));
}
}
.
.
重要都在備注里面了,如果非要說說邏輯,大概就是:
- 1、繼承自StatefulWidget的Widget返回一個自己的State私有類
- 2、State類里面定義私有變量,我們的狀態(tài)變化,會通過setState方法具體操作
- 3、通過一定的事件或者條件,觸發(fā)setState改變數(shù)據(jù)
- 4、數(shù)據(jù)展示的邏輯,不寫死,根據(jù)變量的調(diào)整動態(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 測試標(biāo)題',
theme: new ThemeData(
primaryColor: Colors.red,
),
home: new Scaffold(
appBar: AppBar(
title: Text("測試呀"),
),
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)建子控件的時候,傳入?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
一切是那么重新,在父控件控制子控件的時,刷新ui會讓子控件重新初始化和和build(繪制)。

.
.

.
.
例子3 混合管理
也就是,父控件管,子控件自己也管,一起管
混合管理就是某些狀態(tài)由自己管理,某些狀態(tài)由父部件來管理。
下面的例子就是一個混合管理狀態(tài)的例子,部件 TabboxC 在被點(diǎn)擊時有三個狀態(tài)變換,背景色,文字和邊框。
示例中,背景色和文字的狀態(tài)交由父部件來管理(和上一個示例類似),而邊框狀態(tài)由自己管理。
既然父部件和子部件都能管理狀態(tài),那么它們都是要繼承StatefulWidget類。
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 測試標(biāo)題',
theme: new ThemeData(
primaryColor: Colors.red,
),
home: new Scaffold(
appBar: AppBar(
title: Text("測試呀"),
),
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 顏色 父控件 控制(通過回調(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),),
),
),
),
);
}
}

按下釋放后的一瞬間,才有邊框。
初始化
初始化時候的順序和上面類似,我們來看看點(diǎn)擊事件被觸發(fā)時候的執(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í)行流程:

大家可能會發(fā)現(xiàn),子部件在 Down 事件中調(diào)用了 setState(...) 方法,然后執(zhí)行了一次 build 操作;而在 Up 事件中同樣也調(diào)用了 setState(...) 方法,但是為什么沒有執(zhí)行 build 操作,而是直接執(zhí)行了 click 操作。這里面可能和 Android 里面類似,在 View 的 onTouchEvent 方法里面,onClick 方法也是在 ACTION_UP 里面執(zhí)行的。
END