做(zuò)自(zì)由與創造的先行(xíng)者

Flutter 添加交互

Flutter開(kāi)發手冊

創建一(yī)個有狀态的widget

重點:

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

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

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

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

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

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

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

本節展示如(rú)何為(wèi)Lakes應用程序構建一(yī)個名為(wèi)FavoriteWidget的StatefulWidget。第一(yī)步是選擇如(rú)何管理FavoriteWidget的狀态。

Step 1: 決定哪個對象管理widget的狀态

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

Step 2: 創建StatefulWidget子類

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

class FavoriteWidget extends StatefulWidget {

@override

_FavoriteWidgetState createState() => new _FavoriteWidgetState();

}

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

Step 3: 創建State子類

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

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

按下(xià)IconButton時(shí)會調用_toggleFavorite()方法,然後它會調用setState()。 調用setState()是至關重要(yào)的,因為(wèi)這(zhè)告訴框架,widget的狀态已經改變,應該重繪。 _toggleFavorite在: 1)實心的星形圖标和(hé)數(shù)字“41” 和(hé) 2)虛心的星形圖标和(hé)數(shù)字“40”之間(jiān)切換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和(hé)41之間(jiān)變化時(shí),将文本放在SizedBox中并設置其寬度可(kě)防止出現明(míng)顯的“跳(tiào)躍” ,因為(wèi)這(zhè)些值具有不同的寬度。

Step 4: 将有stateful widget插入widget樹(shù)中

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

// ...

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(

// ...

);

}

}

管理狀态

重點是什麽?

有多種方法可(kě)以管理狀态.

選擇使用何種管理方法

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

誰管理着stateful widget的狀态?widget本身?父widget?都(dōu)會?另一(yī)個對象?答(dá)案是……這(zhè)取決于實際情況。 有幾種有效的方法可(kě)以給你的widget添加互動。作(zuò)為(wèi)小部件設計師(shī)。以下(xià)是管理狀态的最常見(jiàn)的方法:

widget管理自(zì)己的state

父widget管理 widget狀态

混搭管理(父widget和(hé)widget自(zì)身都(dōu)管理狀态))

如(rú)何決定使用哪種管理方法?以下(xià)原則可(kě)以幫助您決定:

如(rú)果狀态是用戶數(shù)據,如(rú)複選框的選中狀态、滑塊的位置,則該狀态最好由父widget管理

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

如(rú)果有疑問(wèn),首選是在父widget中管理狀态

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

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

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

有時(shí),widget在內(nèi)部管理其狀态是最好的。例如(rú), 當ListView的內(nèi)容超過渲染框時(shí), ListView自(zì)動滾動。大多數(shù)使用ListView的開(kāi)發人(rén)員(yuán)不想管理ListView的滾動行(xíng)為(wèi),因此ListView本身管理其滾動偏移量。

_TapboxAState 類:

管理TapboxA的狀态.

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

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

實現widget的所有交互式行(xíng)為(wèi).

// TapboxA 管理自(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管理widget的state

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

在以下(xià)示例中,TapboxB通(tōng)過回調将其狀态導出到其父項。由于TapboxB不管理任何狀态,因此它的父類為(wèi)StatelessWidget。

ParentWidgetState 類:

為(wèi)TapboxB 管理_active狀态.

實現_handleTapboxChanged(),當盒子被點擊時(shí)調用的方法.

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

TapboxB 類:

繼承StatelessWidget類,因為(wèi)所有狀态都(dōu)由其父widget處理.

當檢測到點擊時(shí),它會通(tōng)知父widget.

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

'package: flutter/foundation.dart';

混合管理

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

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

_ParentWidgetState 對象:

管理_active 狀态.

實現 _handleTapboxChanged(), 當盒子被點擊時(shí)調用.

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

_TapboxCState 對象:

管理_highlight state.

GestureDetector監聽所有tap事件。當用戶點下(xià)時(shí),它添加高亮(深綠(lǜ)色邊框);當用戶釋放時(shí),會移除高亮。

當按下(xià)、擡起、或者取消點擊時(shí)更新_highlight狀态,調用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,

),

),

);

}

}

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

其他(tā)交互式widgets

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

如(rú)果你願意,你可(kě)以使用GestureDetector來給任何自(zì)定義widget添加交互性。 您可(kě)以在管理狀态和(hé)Flutter Gallery中找到GestureDetector的示例。

注意: Futter還提供了(le)一(yī)組名為(wèi)Cupertino的iOS風(fēng)格的小部件 。

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

标準 widgets:

Form

FormField

Material Components:

Checkbox

DropdownButton

FlatButton

FloatingActionButton

IconButton

Radio

RaisedButton

Slider

Switch

TextField

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