做自(zì)由與創造的先行者

Flutter 添加交互

Flutter開(kāi)發手冊

創建一個有狀态的widget

重點:

要創建一個自(zì)定義有狀态widget,需創建兩個類:StatefulWidget和(hé)State

狀态對(duì)象包含widget的狀态和(hé)build() 方法。

當widget的狀态改變時(shí),狀态對(duì)象調用(yòng)setState(),告訴框架重繪widget

在本節中,您将創建一個自(zì)定義有狀态的widget。 您将使用(yòng)一個自(zì)定義有狀态widget來(lái)替換兩個無狀态widget - 紅(hóng)色實心星形圖标和(hé)其旁邊的數字計(jì)數 - 該widget用(yòng)兩個子widget管理(lǐ)一行:IconButton和(hé)Text。

實現(xiàn)一個自(zì)定義的有狀态widget需要創建兩個類:

定義一個widget類,繼承自(zì)StatefulWidget.

包含該widget狀态并定義該widget build()方法的類,它繼承自(zì)State.

本節展示如何爲Lakes應用(yòng)程序構建一個名爲FavoriteWidget的StatefulWidget。第一步是選擇如何管理(lǐ)FavoriteWidget的狀态。

Step 1: 決定哪個對(duì)象管理(lǐ)widget的狀态

Widget的狀态可以通過多種方式進行管理(lǐ),但(dàn)在我們的示例中,widget本身(FavoriteWidget)将管理(lǐ)自(zì)己的狀态。 在這(zhè)個例子中,切換星形圖标是一個獨立的操作(zuò),不會(huì)影響父窗口widget或其他(tā)用(yòng)戶界面,因此該widget可以在内部處理(lǐ)它自(zì)己的狀态。

Step 2: 創建StatefulWidget子類

FavoriteWidget類管理(lǐ)自(zì)己的狀态,因此它重寫createState()來(lái)創建狀态對(duì)象。 框架會(huì)在構建widget時(shí)調用(yòng)createState()。在這(zhè)個例子中,createState()創建_FavoriteWidgetState的實例,您将在下(xià)一步中實現(xiàn)該實例。

class FavoriteWidget extends StatefulWidget {

@override

_FavoriteWidgetState createState() => new _FavoriteWidgetState();

}

注意: 以下(xià)劃線(_)開(kāi)頭的成員或類是私有的。有關更多信息,請(qǐng)參閱Dart語言參考中的庫和(hé)可見性部分 。

Step 3: 創建State子類

自(zì)定義State類存儲可變信息 - 可以在widget的生命周期内改變邏輯和(hé)内部狀态。 當應用(yòng)第一次啓動時(shí),用(yòng)戶界面顯示一個紅(hóng)色實心的星星形圖标,表明(míng)該湖已經被收藏,并有41個“喜歡”。狀态對(duì)象存儲這(zhè)些(xiē)信息在_isFavorited和(hé)_favoriteCount變量。

狀态對(duì)象也(yě)定義了(le)build方法。此build方法創建一個包含紅(hóng)色IconButton和(hé)Text的行。 該widget使用(yòng)IconButton(而不是Icon), 因爲它具有一個onPressed屬性,該屬性定義了(le)處理(lǐ)點擊的回調方法。IconButton也(yě)有一個icon的屬性,持有Icon。

按下(xià)IconButton時(shí)會(huì)調用(yòng)_toggleFavorite()方法,然後它會(huì)調用(yòng)setState()。 調用(yòng)setState()是至關重要的,因爲這(zhè)告訴框架,widget的狀态已經改變,應該重繪。 _toggleFavorite在: 1)實心的星形圖标和(hé)數字“41” 和(hé) 2)虛心的星形圖标和(hé)數字“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'),

),

),

],

);

}

}

提示: 當文(wén)本在40和(hé)41之間變化時(shí),将文(wén)本放(fàng)在SizedBox中并設置其寬度可防止出現(xiàn)明(míng)顯的“跳躍” ,因爲這(zhè)些(xiē)值具有不同的寬度。

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

将您自(zì)定義stateful widget在build方法中添加到(dào)widget樹中。首先,找到(dào)創建圖标和(hé)文(wén)本的代碼,并删除它:

// ...

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(

// ...

);

}

}

管理(lǐ)狀态

重點是什(shén)麽?

有多種方法可以管理(lǐ)狀态.

選擇使用(yòng)何種管理(lǐ)方法

如果不是很(hěn)清楚時(shí), 那就在父widget中管理(lǐ)狀态吧.

誰管理(lǐ)着stateful widget的狀态?widget本身?父widget?都會(huì)?另一個對(duì)象?答(dá)案是……這(zhè)取決于實際情況。 有幾種有效的方法可以給你(nǐ)的widget添加互動。作(zuò)爲小(xiǎo)部件設計(jì)師。以下(xià)是管理(lǐ)狀态的最常見的方法:

widget管理(lǐ)自(zì)己的state

父widget管理(lǐ) widget狀态

混搭管理(lǐ)(父widget和(hé)widget自(zì)身都管理(lǐ)狀态))

如何決定使用(yòng)哪種管理(lǐ)方法?以下(xià)原則可以幫助您決定:

如果狀态是用(yòng)戶數據,如複選框的選中狀态、滑塊的位置,則該狀态最好(hǎo)由父widget管理(lǐ)

如果所讨論的狀态是有關界面外(wài)觀效果的,例如動畫(huà),那麽狀态最好(hǎo)由widget本身來(lái)管理(lǐ).

如果有疑問,首選是在父widget中管理(lǐ)狀态

我們将通過創建三個簡單示例來(lái)舉例說明(míng)管理(lǐ)狀态的不同方式:TapboxA、TapboxB和(hé)TapboxC。 這(zhè)些(xiē)例子功能(néng)是相似的 - 每創建一個容器,當點擊時(shí),在綠色或灰色框之間切換。 _active确定顔色:綠色爲true,灰色爲false。

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

widget管理(lǐ)自(zì)己的狀态

有時(shí),widget在内部管理(lǐ)其狀态是最好(hǎo)的。例如, 當ListView的内容超過渲染框時(shí), ListView自(zì)動滾動。大(dà)多數使用(yòng)ListView的開(kāi)發人員不想管理(lǐ)ListView的滾動行爲,因此ListView本身管理(lǐ)其滾動偏移量。

_TapboxAState 類:

管理(lǐ)TapboxA的狀态.

定義_active:确定盒子的當前顔色的布爾值.

定義_handleTap()函數,該函數在點擊該盒子時(shí)更新_active,并調用(yòng)setState()更新UI.

實現(xiàn)widget的所有交互式行爲.

// TapboxA 管理(lǐ)自(zì)身狀态.

//------------------------- 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管理(lǐ)widget的state

對(duì)于父widget來(lái)說,管理(lǐ)狀态并告訴其子widget何時(shí)更新通常是最有意義的。 例如,IconButton允許您将圖标視(shì)爲可點按的按鈕。 IconButton是一個無狀态的小(xiǎo)部件,因爲我們認爲父widget需要知(zhī)道(dào)該按鈕是否被點擊來(lái)采取相應的處理(lǐ)。

在以下(xià)示例中,TapboxB通過回調将其狀态導出到(dào)其父項。由于TapboxB不管理(lǐ)任何狀态,因此它的父類爲StatelessWidget。

ParentWidgetState 類:

爲TapboxB 管理(lǐ)_active狀态.

實現(xiàn)_handleTapboxChanged(),當盒子被點擊時(shí)調用(yòng)的方法.

當狀态改變時(shí),調用(yòng)setState()更新UI.

TapboxB 類:

繼承StatelessWidget類,因爲所有狀态都由其父widget處理(lǐ).

當檢測到(dào)點擊時(shí),它會(huì)通知(zhī)父widget.

// ParentWidget 爲 TapboxB 管理(lǐ)狀态.

//------------------------ 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時(shí),請(qǐng)考慮使用(yòng)@required爲代碼所依賴的任何參數使用(yòng)注解。

'package: flutter/foundation.dart';

混合管理(lǐ)

對(duì)于一些(xiē)widget來(lái)說,混搭管理(lǐ)的方法最有意義的。在這(zhè)種情況下(xià),有狀态widget管理(lǐ)一些(xiē)狀态,并且父widget管理(lǐ)其他(tā)狀态。

在TapboxC示例中,點擊時(shí),盒子的周圍會(huì)出現(xiàn)一個深綠色的邊框。點擊時(shí),邊框消失,盒子的顔色改變。 TapboxC将其_active狀态導出到(dào)其父widget中,但(dàn)在内部管理(lǐ)其_highlight狀态。這(zhè)個例子有兩個狀态對(duì)象_ParentWidgetState和(hé)_TapboxCState。

_ParentWidgetState 對(duì)象:

管理(lǐ)_active 狀态.

實現(xiàn) _handleTapboxChanged(), 當盒子被點擊時(shí)調用(yòng).

當點擊盒子并且_active狀态改變時(shí)調用(yòng)setState()更新UI

_TapboxCState 對(duì)象:

管理(lǐ)_highlight state.

GestureDetector監聽所有tap事(shì)件。當用(yòng)戶點下(xià)時(shí),它添加高(gāo)亮(liàng)(深綠色邊框);當用(yòng)戶釋放(fàng)時(shí),會(huì)移除高(gāo)亮(liàng)。

當按下(xià)、擡起、或者取消點擊時(shí)更新_highlight狀态,調用(yòng)setState()更新UI。

當點擊時(shí),将狀态的改變傳遞給父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,

),

),

);

}

}

另一種實現(xiàn)可能(néng)會(huì)将高(gāo)亮(liàng)狀态導出到(dào)父widget,同時(shí)保持_active狀态爲内部,但(dàn)如果您要求某人使用(yòng)該TapBox,他(tā)們可能(néng)會(huì)抱怨說沒有多大(dà)意義。 開(kāi)發人員隻會(huì)關心該框是否處于活動狀态。開(kāi)發人員可能(néng)不在乎高(gāo)亮(liàng)顯示是如何管理(lǐ)的,并且傾向于讓TapBox處理(lǐ)這(zhè)些(xiē)細節。

其他(tā)交互式widgets

Flutter提供各種按鈕和(hé)類似的交互式widget。這(zhè)些(xiē)widget中的大(dà)多數實現(xiàn)了(le)Material Design 指南, 它們定義了(le)一組具有質感的UI組件。

如果你(nǐ)願意,你(nǐ)可以使用(yòng)GestureDetector來(lái)給任何自(zì)定義widget添加交互性。 您可以在管理(lǐ)狀态和(hé)Flutter Gallery中找到(dào)GestureDetector的示例。

注意: Futter還提供了(le)一組名爲Cupertino的iOS風(fēng)格的小(xiǎo)部件 。

When you need interactivity, it’s easiest to use one of the prefabricated widgets. Here’s a partial list: 當你(nǐ)需要交互性時(shí),最容易的是使用(yòng)預制的widget。這(zhè)是預置widget部分列表:

标準 widgets:

Form

FormField

Material Components:

Checkbox

DropdownButton

FlatButton

FloatingActionButton

IconButton

Radio

RaisedButton

Slider

Switch

TextField

網站(zhàn)建設開(kāi)發|APP設計(jì)開(kāi)發|小(xiǎo)程序建設開(kāi)發
下(xià)一篇:Flutter 手勢
上(shàng)一篇:Flutter 常用(yòng)布局widgets