做自由与创造的先行者

Flutter 添加交互

Flutter开发手册    

创建一个有状态的widget

重点:

要创建一个自定义有状态widget,需创建两个类:StatefulWidget和State

状态对象包含widget的状态和build() 方法。

当widget的状态改变时,状态对象调用setState(),告诉框架重绘widget

在本节中,您将创建一个自定义有状态的widget。 您将使用一个自定义有状态widget来替换两个无状态widget - 红色实心星形图标和其旁边的数字计数 - 该widget用两个子widget管理一行:IconButton和Text。

实现一个自定义的有状态widget需要创建两个类:

定义一个widget类,继承自StatefulWidget.

包含该widget状态并定义该widget build()方法的类,它继承自State.

本节展示如何为Lakes应用程序构建一个名为FavoriteWidget的StatefulWidget。第一步是选择如何管理FavoriteWidget的状态。

Step 1: 决定哪个对象管理widget的状态

Widget的状态可以通过多种方式进行管理,但在我们的示例中,widget本身(FavoriteWidget)将管理自己的状态。 在这个例子中,切换星形图标是一个独立的操作,不会影响父窗口widget或其他用户界面,因此该widget可以在内部处理它自己的状态。

Step 2: 创建StatefulWidget子类

FavoriteWidget类管理自己的状态,因此它重写createState()来创建状态对象。 框架会在构建widget时调用createState()。在这个例子中,createState()创建_FavoriteWidgetState的实例,您将在下一步中实现该实例。

class FavoriteWidget extends StatefulWidget {

@override

_FavoriteWidgetState createState() => new _FavoriteWidgetState();

}

注意: 以下划线(_)开头的成员或类是私有的。有关更多信息,请参阅Dart语言参考中的库和可见性部分 。

Step 3: 创建State子类

自定义State类存储可变信息 - 可以在widget的生命周期内改变逻辑和内部状态。 当应用第一次启动时,用户界面显示一个红色实心的星星形图标,表明该湖已经被收藏,并有41个“喜欢”。状态对象存储这些信息在_isFavorited和_favoriteCount变量。

状态对象也定义了build方法。此build方法创建一个包含红色IconButton和Text的行。 该widget使用IconButton(而不是Icon), 因为它具有一个onPressed属性,该属性定义了处理点击的回调方法。IconButton也有一个icon的属性,持有Icon。

按下IconButton时会调用_toggleFavorite()方法,然后它会调用setState()。 调用setState()是至关重要的,因为这告诉框架,widget的状态已经改变,应该重绘。 _toggleFavorite在: 1)实心的星形图标和数字“41” 和 2)虚心的星形图标和数字“40”之间切换UI。

class _FavoriteWidgetState extends State {

bool _isFavorited = true;

int _favoriteCount = 41;

void _toggleFavorite() {

setState(() {

// If the lake is currently favorited, unfavorite it.

if (_isFavorited) {

_favoriteCount -= 1;

_isFavorited = false;

// Otherwise, favorite it.

} 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'),

),

),

],

);

}

}

提示: 当文本在40和41之间变化时,将文本放在SizedBox中并设置其宽度可防止出现明显的“跳跃” ,因为这些值具有不同的宽度。

Step 4: 将有stateful widget插入widget树中

将您自定义stateful widget在build方法中添加到widget树中。首先,找到创建图标和文本的代码,并删除它:

// ...

new Icon(

Icons.star,

color: Colors.red[500],

),

new Text('41')

// ...

在相同的位置创建stateful widget:

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

Widget titleSection = new Container(

// ...

child: new Row(

children: [

new Expanded(

child: new Column(

// ...

),

new FavoriteWidget(),

],

),

);

return new MaterialApp(

// ...

);

}

}

管理状态

重点是什么?

有多种方法可以管理状态.

选择使用何种管理方法

如果不是很清楚时, 那就在父widget中管理状态吧.

谁管理着stateful widget的状态?widget本身?父widget?都会?另一个对象?答案是……这取决于实际情况。 有几种有效的方法可以给你的widget添加互动。作为小部件设计师。以下是管理状态的最常见的方法:

widget管理自己的state

父widget管理 widget状态

混搭管理(父widget和widget自身都管理状态))

如何决定使用哪种管理方法?以下原则可以帮助您决定:

如果状态是用户数据,如复选框的选中状态、滑块的位置,则该状态最好由父widget管理

如果所讨论的状态是有关界面外观效果的,例如动画,那么状态最好由widget本身来管理.

如果有疑问,首选是在父widget中管理状态

我们将通过创建三个简单示例来举例说明管理状态的不同方式:TapboxA、TapboxB和TapboxC。 这些例子功能是相似的 - 每创建一个容器,当点击时,在绿色或灰色框之间切换。 _active确定颜色:绿色为true,灰色为false。

a large green box with the text, 'Active' a large grey box with the text, 'Inactive'

widget管理自己的状态

有时,widget在内部管理其状态是最好的。例如, 当ListView的内容超过渲染框时, ListView自动滚动。大多数使用ListView的开发人员不想管理ListView的滚动行为,因此ListView本身管理其滚动偏移量。

_TapboxAState 类:

管理TapboxA的状态.

定义_active:确定盒子的当前颜色的布尔值.

定义_handleTap()函数,该函数在点击该盒子时更新_active,并调用setState()更新UI.

实现widget的所有交互式行为.

// TapboxA 管理自身状态.

//------------------------- TapboxA ----------------------------------

class TapboxA extends StatefulWidget {

TapboxA({Key key}) : super(key: key);

@override

_TapboxAState createState() => new _TapboxAState();

}

class _TapboxAState extends State {

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 ? '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],

),

),

);

}

}

//------------------------- 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(),

),

),

);

}

}

父widget管理widget的state

对于父widget来说,管理状态并告诉其子widget何时更新通常是最有意义的。 例如,IconButton允许您将图标视为可点按的按钮。 IconButton是一个无状态的小部件,因为我们认为父widget需要知道该按钮是否被点击来采取相应的处理。

在以下示例中,TapboxB通过回调将其状态导出到其父项。由于TapboxB不管理任何状态,因此它的父类为StatelessWidget。

ParentWidgetState 类:

为TapboxB 管理_active状态.

实现_handleTapboxChanged(),当盒子被点击时调用的方法.

当状态改变时,调用setState()更新UI.

TapboxB 类:

继承StatelessWidget类,因为所有状态都由其父widget处理.

当检测到点击时,它会通知父widget.

// ParentWidget 为 TapboxB 管理状态.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {

@override

_ParentWidgetState createState() => new _ParentWidgetState();

}

class _ParentWidgetState extends State {

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, @required this.onChanged})

: super(key: key);

final bool active;

final ValueChanged onChanged;

void _handleTap() {

onChanged(!active);

}

Widget build(BuildContext context) {

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],

),

),

);

}

}

提示: 在创建API时,请考虑使用@required为代码所依赖的任何参数使用注解。

'package: flutter/foundation.dart';

混合管理

对于一些widget来说,混搭管理的方法最有意义的。在这种情况下,有状态widget管理一些状态,并且父widget管理其他状态。

在TapboxC示例中,点击时,盒子的周围会出现一个深绿色的边框。点击时,边框消失,盒子的颜色改变。 TapboxC将其_active状态导出到其父widget中,但在内部管理其_highlight状态。这个例子有两个状态对象_ParentWidgetState和_TapboxCState。

_ParentWidgetState 对象:

管理_active 状态.

实现 _handleTapboxChanged(), 当盒子被点击时调用.

当点击盒子并且_active状态改变时调用setState()更新UI

_TapboxCState 对象:

管理_highlight state.

GestureDetector监听所有tap事件。当用户点下时,它添加高亮(深绿色边框);当用户释放时,会移除高亮。

当按下、抬起、或者取消点击时更新_highlight状态,调用setState()更新UI。

当点击时,将状态的改变传递给父widget.

//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {

@override

_ParentWidgetState createState() => new _ParentWidgetState();

}

class _ParentWidgetState extends State {

bool _active = false;

void _handleTapboxChanged(bool newValue) {

setState(() {

_active = newValue;

});

}

@override

Widget build(BuildContext context) {

return new Container(

child: new TapboxC(

active: _active,

onChanged: _handleTapboxChanged,

),

);

}

}

//----------------------------- TapboxC ------------------------------

class TapboxC extends StatefulWidget {

TapboxC({Key key, this.active: false, @required this.onChanged})

: super(key: key);

final bool active;

final ValueChanged onChanged;

_TapboxCState createState() => new _TapboxCState();

}

class _TapboxCState extends State {

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) {

// This example adds a green border on tap down.

// On tap up, the square changes to the opposite state.

return new GestureDetector(

onTapDown: _handleTapDown, // Handle the tap events in the order that

onTapUp: _handleTapUp, // they occur: down, up, tap, cancel

onTap: _handleTap,

onTapCancel: _handleTapCancel,

child: new Container(

child: new Center(

child: new Text(widget.active ? 'Active' : 'Inactive',

style: new TextStyle(fontSize: 32.0, color: Colors.white)),

),

width: 200.0,

height: 200.0,

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,

),

),

);

}

}

另一种实现可能会将高亮状态导出到父widget,同时保持_active状态为内部,但如果您要求某人使用该TapBox,他们可能会抱怨说没有多大意义。 开发人员只会关心该框是否处于活动状态。开发人员可能不在乎高亮显示是如何管理的,并且倾向于让TapBox处理这些细节。

其他交互式widgets

Flutter提供各种按钮和类似的交互式widget。这些widget中的大多数实现了Material Design 指南, 它们定义了一组具有质感的UI组件。

如果你愿意,你可以使用GestureDetector来给任何自定义widget添加交互性。 您可以在管理状态和Flutter Gallery中找到GestureDetector的示例。

注意: Futter还提供了一组名为Cupertino的iOS风格的小部件 。

When you need interactivity, it’s easiest to use one of the prefabricated widgets. Here’s a partial list: 当你需要交互性时,最容易的是使用预制的widget。这是预置widget部分列表:

标准 widgets:

Form

FormField

Material Components:

Checkbox

DropdownButton

FlatButton

FloatingActionButton

IconButton

Radio

RaisedButton

Slider

Switch

TextField

网站建设开发|APP设计开发|小程序建设开发
下一篇:Flutter 手势
上一篇:Flutter 常用布局widgets