目標(biāo)是修改圖中紅色實(shí)線框中的喜歡和不喜歡的五角星的修改,以及數(shù)字的修改。
在修改之前,有必要先了解一些相關(guān)的信息秽褒。
知識點(diǎn)
前面簡單的提到過壶硅,有些Widget是Statful(有狀態(tài)的),而其他的一些是Stateless(無狀態(tài)的)销斟。比如繼承自StatefulWidget的有Checkbox庐椒、Radio、Slider蚂踊、Form等约谈,這些Widget用戶都是可以做一些交互的,同樣的繼承自StatelessWidget的Widget有Text、Icon等犁钟。有狀態(tài)和無狀態(tài)的主要區(qū)別在于:有狀態(tài)的Widget在其內(nèi)部都有一個(gè)state用來標(biāo)記是否發(fā)生了變化棱诱,然后調(diào)用setState()方法來更新自己。
創(chuàng)建Stateful Widget
關(guān)鍵點(diǎn):
1涝动、創(chuàng)建一個(gè)Stateful Widget需要兩個(gè)類迈勋,分別繼承自StateFulWidget和State
2、state對象包含了widget的state和widget的build()方法
3醋粟、當(dāng)widget的state改變了的時(shí)候靡菇,state對象會調(diào)用setState()方法,告訴框架去重繪widget米愿。
到這里厦凤,其實(shí)也可以明白,只有Stateful的Widget才能修改其內(nèi)容育苟,所以要想實(shí)現(xiàn)上面的目標(biāo)较鼓,必須將原來的Icon和Text這兩個(gè)Stateless的Widget替換成Stateful類型的Widget。
所以我們必須實(shí)現(xiàn)兩個(gè)Stateful類型的Widget违柏,沒個(gè)Widget都應(yīng)該包含下面兩個(gè)點(diǎn):
- 一個(gè)StatefulWidget的子類博烂,并且實(shí)現(xiàn)了createState()方法;
- 一個(gè)State的子類漱竖,并且實(shí)現(xiàn)了build()方法脖母。
如果足夠留意,會發(fā)現(xiàn)主函數(shù)里就是一個(gè)StatefulWidget闲孤。
代碼
搞清楚了上面的,然后結(jié)合官方文檔烤礁,基礎(chǔ)的架子如下:
class FavoriteWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() => new _FavoriteWidgetState();
}
class _FavoriteWidgetState extends State {
@override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
}
完整的代碼如下所示:
class FavoriteWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() => new _FavoriteWidgetState();
}
class _FavoriteWidgetState extends State {
var _isFavorited = true; // 是否喜歡
var _favoriteCount = 41; // 喜歡個(gè)數(shù)
_toggleFavorite() {
setState((){
if(_isFavorited) {
_favoriteCount -= 1;
} else {
_favoriteCount += 1;
}
_isFavorited = !_isFavorited;
});
}
@override
Widget build(BuildContext context) {
return new Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Container(
padding: const EdgeInsets.all(0.0),
margin: const EdgeInsets.all(0.0),
child: new IconButton(icon: (_isFavorited ? new Icon(Icons.star) : new Icon(Icons.star_border)), onPressed: _toggleFavorite, color: Colors.red),
),
new SizedBox(
width: 18.0,
child: new Container(
child: new Text("$_favoriteCount"),
),
)
],
);
}
}
注意:為什么上面的Text要用SizedBox包裹起來呢讼积?如果不寫width可以嗎?(主要是為了解決跳動(dòng)的問題)
最后脚仔,拿這個(gè)widget去替換原來代碼的相應(yīng)位置的內(nèi)容勤众。
上面說到的對state的管理是交給了FavoriteWidget,即當(dāng)前Widget去管理鲤脏,而實(shí)際情況中们颜,對state的管理可以有很多種辦法吕朵,接下來會慢慢學(xué)習(xí)!
狀態(tài)管理
最通用的有三種方式:
1窥突、Widget自己管理State
2努溃、父類管理State
3、混合管理
那么如何確定使用哪種管理方式呢阻问,這里有兩個(gè)建議:
1梧税、如果是和用戶數(shù)據(jù)相關(guān)的,建議用父類來管理
2称近、如果是為了審美第队,比如動(dòng)畫,建議Widget自己管理
1刨秆、自己管理State
這種方式比較直接凳谦,也比較好理解,因?yàn)槎际窃谧约旱沫h(huán)境下處理衡未,所以直接貼出代碼:
class _TapboxA extends StatefulWidget {
@override
State<StatefulWidget> createState() => new _TapboxAState();
}
class _TapboxAState extends State<_TapboxA> {
var _active = false;
_handleTap() {
setState(() {
_active = !_active;
});
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new GestureDetector(
onTap: _handleTap,
child: new Container(
width: 200.0,
height: 200.0,
color: _active ? Colors.lightGreen[700] : Colors.grey[600],
// decoration: new BoxDecoration(
// color: _active ? Colors.lightGreen[700] : Colors.grey[600]
// ),
child: new Center(
child: new Text(
_active ? 'Active' : 'Inactive',
style: new TextStyle(fontSize: 32.0, color: Colors.white),
),
),
),
);
}
}
2尸执、父控件控制State
該控制的核心在于:回調(diào)。父控件通過傳遞參數(shù)控制子控件的展示眠屎,子控件通過函數(shù)的回調(diào)剔交,改變父控件的值,然后父控件調(diào)用setState()方法再反饋到具體的子控件的展示上改衩。
具體的代碼如下:
class _TapBoxB extends StatelessWidget { // 此時(shí)子控件是Stateless的
// var active = false;
final active;
final ValueChanged<bool> onChanged;
_TapBoxB({Key key, this.active: false, @required this.onChanged})
: super(key: key);
void _handleTap() {
onChanged(!active);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new GestureDetector(
onTap: _handleTap,
child: new Container(
child: new Center(
child: new Text(
active ? 'Active' : 'Inactive',
style: new TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: new BoxDecoration(
color: active ? Colors.lightGreen[700] : Colors.grey[600]),
),
);
}
}
class ParentWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _ParentWidgetState();
}
}
class _ParentWidgetState extends State<ParentWidget> {
var _active = false;
_handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Container(
child: new _TapBoxB(
active: _active, // 傳值
onChanged: _handleTapboxChanged, // 回調(diào)岖常,用于觸發(fā)回調(diào)
),
);
}
}
3、混合管理
通俗講葫督,就是上面兩種的柔和竭鞍,父控件控制一些自己需要的State,子控件控制一些自己的State橄镜。
代碼如下:
class _ParentWidget extends StatefulWidget {
bool active = false;
@override
State<StatefulWidget> createState() => new _ParentWidgetState();
}
class _ParentWidgetState extends State<_ParentWidget> {
void _handleTapboxChanged(bool newValue) {
setState(() {
widget.active = newValue;
});
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Container(
child: new TapboxC(onChanged: _handleTapboxChanged, active: widget.active),
);
}
}
class TapboxC extends StatefulWidget {
final bool _active;
bool _highlight = false;
final ValueChanged<bool> onChanged;
TapboxC({Key key, bool active, @required this.onChanged})
:this._active = active, super(key: key) { }
@override
State<StatefulWidget> createState() => new _TapboxCState();
}
class _TapboxCState extends State<TapboxC> {
void _handleTap() {
widget.onChanged(!widget._active);
}
void _handleTapDown(TapDownDetails details) {
setState((){
widget._highlight = true;
});
}
void _handleTapUp(TapUpDetails details) {
setState((){
widget._highlight = false;
});
}
void _handleTapCancel(){
setState((){
widget._highlight = false;
});
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new GestureDetector(
onTap: _handleTap,
onTapDown: _handleTapDown,
onTapUp: _handleTapUp,
onTapCancel: _handleTapCancel,
child: new Container(
width: 200.0,
height: 200.0,
decoration: new BoxDecoration(
color: widget._active ? Colors.lightGreen[700] : Colors.grey[600],
border: widget._highlight
? new Border.all(color: Colors.teal[700], width: 10.0)
: null,
),
),
);
}
}
這里與官方的代碼相比偎快,我把所有的和state相關(guān)的都放到了Widget類里。(但是洽胶,其實(shí)這么寫是有問題的晒夹,可以自己想想,將在下一節(jié)分析)
至此姊氓,F(xiàn)lutter里的State的管理和事件交互也做了簡單的了解了丐怯,對于我來說,還是不甚了解翔横。重要的還在于多看多寫读跷。