Flutter进阶—创建有状态控件
Flutter进阶—构建布局实例展示了如何创建以下布局。 当应用程序首次启动时,这颗实心星标是红色的,表明这个景点曾经被收藏过。实心星标旁边的人数表明,有66人喜欢这个景点。现在需要完成一个任务,点击该实心星标删除其收藏的状态,用空心星标取代实心星标并减少收藏人数。点击再次收藏景点,画一颗实心星标,增加收藏人数。 要完成此操作,需要创建一个包含Icon(图标)和Text(文本)的自定义控件,Icon(图标)和Text(文本)本身就是控件。因为点击Icon(图标)会改变这两个控件的状态,所以自定义控件应该同时管理两者。 有状态和无状态的控件有状态和无状态的控件
创建有状态控件创建一个自定义的有状态控件,管理具有IconButton和Text两个子控件的行,用来替换两个无状态控件,实心红色星标和数字计数。 实现自定义的有状态控件需要创建两个类
第一步:决定哪个对象管理控件的状态控件的状态可以通过多种方式进行管理,但在下面的示例中,控件本身,FavoriteWidget将管理自己的状态。在这个例子中,切换星标是一个独立的动作,不会影响父控件或用户界面的其余部分,所以该控件可以在内部处理其状态。 第二步:子类StatefulWidgetFavoriteWidget类管理自己的状态,所以它覆盖createState()来创建State对象,框架在构建控件时调用createState()。在这个例子中,createState()创建一个_FavoriteWidgetState的实例。 class FavoriteWidget extends StatefulWidget {
@override
_FavoriteWidgetState createState() => new _FavoriteWidgetState();
}
第三步:子类State自定义State类存储可变信息(可在控件的整个生命周期内更改的逻辑和内部状态),当应用程序首次启动时,用户界面将显示一个实心的红色星标,表示该景点有“收藏”状态,并有66“收藏”。状态对象将此信息存储在_isFavorited和_favoriteCount变量中。 状态对象还定义了build(),build()创建一行包含红色IconButton和Text。该控件使用IconButton,而不是Icon,因为它具有一个onPressed属性,该属性定义了用于处理点击的回调方法。IconButton也有一个icon 属性保存图标。 _toggleFavorite()方法,当按下IconButton时调用,调用setState()。调用setState()是至关重要的,因为它告诉框架控件的状态已经改变,控件应该重新绘制。 class _FavoriteWidgetState extends State<FavoriteWidget> {
bool _isFavorited = true;
int _favoriteCount = 66;
void _toggleFavorite() {
setState(() {
// 如果景点目前被收藏
if(_isFavorited) {
_favoriteCount -= 1;
_isFavorited = false;
// 景点未被收藏
} else {
_favoriteCount += 1;
_isFavorited = true;
}
});
}
@override
Widget build(BuildContext context) {
return new Row(
mainAxisSize: MainAxisSize.min,children: [
new Container(
padding: new EdgeInsets.all(0.0),child: new IconButton(
icon: (_isFavorited
? new Icon(Icons.star)
: new Icon(Icons.star_border)),color: Colors.red[500],onPressed: _toggleFavorite,)
),new SizedBox(
width: 18.0,child: new Container(
child: new Text('$_favoriteCount'),)
)
]
);
}
}
第四步:将有状态控件插入控件树在应用程序的构建方法中将您的自定义有状态控件添加到用户界面。首先找到创建图标和文本的代码,然后将其删除: // ...
new Icon(
Icons.star,),new Text('66')
// ...
在同一位置,创建有状态控件: class _MyHomePageState extends State<MyHomePage> {
// ...
@override
Widget build(BuildContext context) {
Widget titleSection = new Container(
// ...
child: new Row(children: [
new Expanded(
child: new Column(
// ...
),new FavoriteWidget(),]
)
);
// ...
}
管理状态通过以下原则可以帮助您决定如何管理状态
我们将通过创建三个简单的示例来演示管理状态的不同方式:TapboxA、TapboxB和TapboxC。这些例子都是类似的,每个都创建一个容器,当点击时,在一个绿色或灰色框之间切换。布尔值_active确定颜色,绿色为活动、灰色为非活动。 控件管理自己的状态有时,这个控件最有意义的是在内部管理它的状态。例如,当ListView的内容超过渲染框时,ListView会自动滚动。大多数使用ListView的开发人员不想管理ListView的滚动行为,因此ListView本身管理其滚动偏移。 _TapboxAState类:
//------------------------- TapboxA ---------------------------------
class TapboxA extends StatefulWidget {
TapboxA({Key key}) : super(key: key);
@override
_TapboxAState createState() => new _TapboxAState();
}
class _TapboxAState extends State<TapboxA> {
bool _active = false;
void _handleTap() {
setState(() {
_active = !_active;
});
}
Widget build(BuildContext context) {
return new GestureDetector(
onTap: _handleTap,child: new Container(
child: new Center(
child: new Text(
_active ? '有效的' : '无效的',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],)
)
);
}
}
//------------------------- MyApp ----------------------------------
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',home: new Scaffold(
appBar: new AppBar(
title: new Text('Flutter Demo')
),body: new Center(
child: new TapboxA(),)
)
);
}
}
父控件管理控件的状态通常情况下,父控件最大的意义的是管理状态,并在更新时告诉其子控件。例如,IconButton允许您将图标作为可点击按钮。IconButton是一个无状态的控件,因为我们决定父控件需要知道按钮是否被点击,所以可以采取适当的措施。 在以下示例中,TapboxB通过回调将其状态导出到其父级。因为TapboxB不管理任何状态,它会对StatelessWidget进行子类化。 ParentWidgetState类:
TapboxB类:
//------------------------ ParentWidget --------------------------------
class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => new _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return new Container(
child: new TapboxB(
active: _active,onChanged: _handleTapboxChanged,)
);
}
}
//------------------------- TapboxB ---------------------------------
class TapboxB extends StatelessWidget {
TapboxB({
Key key,this.active: false,// import 'package:flutter/foundation.dart';
@required this.onChanged
}):super(key: key);
final bool active;
final ValueChanged<bool> onChanged;
void _handleTap() {
onChanged(!active);
}
Widget build(BuildContext context) {
return new GestureDetector(
onTap: _handleTap,child: new Container(
child: new Center(
child: new Text(
active ? '有效的' : '无效的',decoration: new BoxDecoration(
color: active ? Colors.lightGreen[700] : Colors.grey[600],)
)
);
}
}
混合管理状态的方法对于一些控件,混合管理状态是很有必要的。在这种情况下,状态控件管理某些状态,父控件管理状态的其他方面。 在TapboxC示例中,点击按钮,点击框的周围会出现一个深绿色的边框。不点按钮,边框消失。 TapboxC将其_active状态导出到其父级,但在内部管理其_highlight状态。此示例有两个状态对象 _ParentWidgetState和_TapboxCState。 _ParentWidgetState对象
_TapboxCState对象
//---------------------------- ParentWidget ----------------------------
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return new Container(
child: new TapboxC(
active: _active,)
);
}
}
//----------------------------- TapboxC ------------------------------
class TapboxC extends StatefulWidget {
TapboxC({
Key key,// import 'package:flutter/foundation.dart';
@required this.onChanged
}) :super(key: key);
final bool active;
final ValueChanged<bool> onChanged;
_TapboxCState createState() => new _TapboxCState();
}
class _TapboxCState extends State<TapboxC> {
bool _highlight = false;
void _handleTapDown(TapDownDetails details) {
setState((){
_highlight = true;
});
}
void _handleTapUp(TapUpDetails details) {
setState((){
_highlight = false;
});
}
void _handleTapCancel() {
setState((){
_highlight = false;
});
}
void _handleTap() {
widget.onChanged(!widget.active);
}
Widget build(BuildContext context) {
return new GestureDetector(
onTapDown: _handleTapDown,onTapUp: _handleTapUp,onTap: _handleTap,onTapCancel: _handleTapCancel,child: new Container(
child: new Center(
child: new Text(
widget.active ? '有效的' : '无效的',decoration: new BoxDecoration(
color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],border: _highlight
? new Border.all(
color:Colors.teal[700],width: 10.0)
:null,)
)
);
}
}
实际我们也可以将高亮状态导出到父级,同时保持内部活动状态,但是如果您要求别人使用该点击框,别人可能会抱怨说它没有任何意义。开发人员关心该点击框是否处于活动状态,但开发人员可不在乎如何高亮显示,并且更喜欢点击框自己处理这些细节。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |