爲Flutter應用程序添加交互性

你會學到什麼:html

  • 如何響應信號。
  • 如何建立自定義小部件。
  • 無狀態和有狀態小部件之間的區別。

你如何修改你的應用程序,使其對用戶輸入作出反應? 在本教程中,您將爲僅包含非交互式小部件的應用添加交互性。 具體來講,您將經過建立一個管理兩個無狀態小部件的自定義狀態小部件來修改圖標以使其能夠點擊。java

內容

有狀態和無狀態的小部件
建立一個有狀態的小部件ios

  • 第1步:決定哪一個對象管理小部件的狀態
  • 第2步:子類StatefulWidget
  • 第3步:子類狀態
  • 第4步:將有狀態小部件插入小部件樹中
  • 問題?

管理狀態git

  • 小部件管理本身的狀態
  • 父母管理小部件的狀態
  • 混搭方法

其餘交互式小部件github

  • 標準小部件
  • 材料組件

資源web

準備好

若是您已經在Flutter佈局中構建佈局,請跳到下一節。macos

確保你已經創建了你的環境。app

一旦你有一個鏈接和啓用的設備,或者你已經啓動了iOS模擬器(Flutter安裝的一部分),你很好!框架

Flutter的Building Layouts展現瞭如何爲下面的截圖建立佈局。less

當應用第一次啓動時,這顆恆星是純紅色的,代表這個湖之前已經被收藏了。 星號旁邊的數字表示41我的對此湖感興趣。 完成本教程後,輕敲星星將刪除其偏好狀態,用輪廓線代替實心星並減小計數。 再次輕拍湖面,畫出星星並增長計數。

爲了實現這一點,您將建立一個包含星號和計數的自定義小部件,它們都是小部件。 由於點擊明星會更改這兩個小部件的狀態,因此同一個小部件應該同時管理這兩個小部件。

您能夠正確觸摸第2步:子類StatefulWidget中的代碼。 若是您想嘗試不一樣方式管理狀態,請跳至管理狀態

有狀態和無狀態的小部件

重點是什麼?

  • 有些小部件是有狀態的,有些是無狀態的。
  • 若是一個小部件發生變化 - 用戶與它進行交互,例如 - 它是有狀態的。
  • 小部件的狀態由能夠改變的值組成,例如滑塊的當前值或複選框是否被選中。
  • 小部件的狀態存儲在狀態對象中,從而將小部件的狀態與外觀分開。
  • 當小部件的狀態改變時,狀態對象調用setState(),告訴框架重繪小部件。

無狀態小部件沒有內部狀態來管理。 IconIconButtonTextStatelessWidget子類的無狀態小部件示例。

有狀態的小部件是動態的。 用戶能夠與有狀態的小部件進行交互(例如經過輸入表單或移動滑塊),或者隨着時間的推移而變化(多是數據饋送致使UI更新)。 CheckboxRadioSliderInkWellFormTextFieldStatefulWidget子類的有狀態小部件的示例。

建立一個有狀態的小部件

重點是什麼?

  • 要建立一個自定義狀態小部件,能夠建立兩個類:StatefulWidget和State。
  • 狀態對象包含小部件的狀態和小部件的build()方法。
  • 當小部件的狀態改變時,狀態對象調用setState(),告訴框架重繪小部件。

在本節中,您將建立一個自定義有狀態小部件。 您將使用一個自定義狀態小部件替換兩個無狀態小部件 - 純紅星和其旁邊的數字計數 - 該小部件用兩個子部件管理一行:IconButton和Text。

實現一個定製的有狀態小部件須要建立兩個類:

  • 定義小部件的StatefulWidget的子類。
  • State的一個子類,它包含該小部件的狀態並定義小部件的build()方法。

本節展現如何爲Lakes應用程序構建一個名爲Favorite Widget的有狀態小部件。 第一步是選擇如何管理Favorite Widgets狀態。

第1步:決定哪一個對象管理小部件的狀態

小部件的狀態能夠經過多種方式進行管理,但在咱們的示例中,小部件自己(FavoriteWidget)將管理本身的狀態。 在這個例子中,切換星號是一個獨立的操做,不會影響父窗口小部件或其餘用戶界面,所以窗口小部件能夠在內部處理它的狀態。

在管理狀態中瞭解更多關於窗口小部件和狀態的分離以及如何管理狀態的信息。

第2步:子類StatefulWidget

FavoriteWidget類管理本身的狀態,所以它重寫createState()來建立狀態對象。 當它想要構建小部件時,框架調用createState()。 在這個例子中,createState()建立_FavoriteWidgetState的一個實例,你將在下一步中實現它。

class FavoriteWidget extends StatefulWidget {
  @override
  _FavoriteWidgetState createState() => new _FavoriteWidgetState();
}

注意:如下劃線(_)開頭的成員或類是私有的。 有關更多信息,請參閱Dart語言參考中的庫和可見性部分。

第3步:子類狀態

自定義State類存儲可變信息 - 能夠在小部件的生命週期內改變的邏輯和內部狀態。 當應用第一次啓動時,用戶界面顯示一個穩固的紅色星星,代表該湖有「最喜歡」的狀態,並有41個「喜歡」。 狀態對象將這些信息存儲在_isFavorited_favoriteCount變量中。

狀態對象還定義了build方法。 此build方法建立一個包含紅色IconButton和Text的行。 該小部件使用IconButton(而不是Icon),由於它有一個onPressed屬性,該屬性定義了處理水龍頭的回調方法。 IconButton也有一個保存圖標的Icon屬性。

_toggleFavorite()方法在按下IconButton時調用,它調用setState()。 調用setState()是相當重要的,由於這會告訴框架小部件的狀態已經改變,而且小部件應該重繪。 _toggleFavorite函數在1)星形圖標和數字「41」,以及2)star_border圖標和數字「40」之間交換UI。

class _FavoriteWidgetState extends State<FavoriteWidget> {
  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'),
          ),
        ),
      ],
    );
  }
}

提示:將文本放置在SizedBox中並設置其寬度可防止文本在40和41之間變化時出現明顯的「跳躍」 - 不然會發生這種狀況,由於這些值具備不一樣的寬度。

第4步:將有狀態小部件插入小部件樹中

將您的自定義狀態小部件添加到應用構建方法中的小部件樹中。 首先,找到建立圖標和文本的代碼,並刪除它:

// ...
new Icon(
  Icons.star,
  color: Colors.red[500],
),
new Text('41')
// ...

在相同的位置建立有狀態的小部件:

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(
      // ...
    );
  }
}

而已! 當您從新加載應用程序時,星形圖標如今應該響應點擊。

問題?

若是您沒法運行代碼,請在IDE中查找可能的錯誤。 調試Flutter應用程序可能會有所幫助。 若是仍然沒法找到問題,請根據GitHub上的交互式湖區示例檢查代碼。

若是您仍然有疑問,請參閱獲取支持

本頁面的其他部分介紹了能夠管理窗口小部件狀態的幾種方式,並列出了其餘可用的交互窗口小部件。

管理狀態

重點是什麼?

  • 管理狀態有不一樣的方法。
  • 您做爲小部件設計師,選擇使用哪一種方法。
  • 若是有疑問,首先管理父窗口小部件中的狀態。

誰管理有狀態小部件的狀態? 小部件自己? 父窗口小部件? 都? 另外一個對象? 答案是......這取決於依賴高關係。有幾種有效的方法可讓你的小部件互動。做爲小部件設計師,您根據您指望使用的小部件作出決定。如下是管理狀態的最多見方法:

你如何決定使用哪一種方法? 如下原則能夠幫助您決定:

  • 若是有問題的狀態是用戶數據,例如複選框的選中或未選中模式或滑塊位置,則該狀態最好由父控件管理。
  • 若是所討論的狀態是審美的,例如動畫,那麼狀態最好由小部件自己來管理。

若是有疑問,首先管理父窗口小部件中的狀態。

咱們將經過建立三個簡單示例來舉例說明管理狀態的不一樣方式:TapboxA,TapboxB和TapboxC。 這些例子都是相似的工做 - 每建立一個容器,當點擊時,在綠色或灰色框之間切換。 _active布爾值肯定顏色:綠色表示激活或者灰色表示不激活。

這些示例使用GestureDetector捕獲Container上的活動。

小部件管理本身的狀態

有時,小部件在內部管理其狀態是最有意義的。 例如,當ListView的內容超過渲染框時,ListView自動滾動。 大多數使用ListView的開發人員不想管理ListView的滾動行爲,所以ListView自己管理其滾動偏移量。

_TapboxAState類:

  • 管理TapboxA的狀態。
  • 定義_active布爾值決定框的當前顏色。
  • 定義_handleTap()函數,輕擊框時該函數更新_active,並調用setState()函數來更新UI。
  • 實現小部件的全部交互式行爲。
// TapboxA manages its own state.

//------------------------- 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 ? '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(),
        ),
      ),
    );
  }
}

Dart代碼:lib/main.dart

父部件管理小部件的狀態

對於父窗口小部件來講,管理狀態並告訴其子窗口小部件什麼時候更新一般是最有意義的。 例如,IconButton容許您將圖標視爲可點按的按鈕。 IconButton是一個無狀態的小部件,由於咱們認爲父部件須要知道該按鈕是否已被輕敲,因此它能夠採起適當的行動。

在如下示例中,TapboxB經過回調將其狀態導出到其父項。 因爲TapboxB無論理任何狀態,所以它的子類爲無狀態部件。

ParentWidgetState類:

  • 管理TapboxB的_active狀態。
  • 實現_handleTapboxChanged(),當方塊被點擊時調用該方法。
  • 當狀態改變時,調用setState()來更新UI。

TapboxB類:

  • 擴展StatelessWidget,由於全部狀態都由其父級處理。
  • 當檢測到輕擊時,它會通知父母。
// ParentWidget manages the state for 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, @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 ? '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],
        ),
      ),
    );
  }
}

Dart代碼:lib/main.dart

提示:建立API時,請考慮對代碼所依賴的任何參數使用@required註釋。 要使用@required,請導入foundation庫(該庫從新導出Dart的meta.dart庫):

import 」package:flutter/foundation.dart「;

混搭方法

對於一些小部件來講,混合搭配的方法最有意義。 在這種狀況下,有狀態小部件管理一些狀態,而且父小部件管理狀態的其它方面。

在TapboxC示例中,按下時,框的周圍會出現一個深綠色的邊框。 擡起時,邊框消失,框的顏色改變。 TapboxC將其_active狀態導出到其父項,但在內部管理_highlight狀態。 這個例子有兩個狀態對象_ParentWidgetState和_TapboxCState。

_ParentWidgetState對象:

  • 管理_active狀態。
  • 實現_handleTapboxChanged(),當方塊被點擊時調用該方法。
  • 調用setState()以在發生輕擊和_active狀態改變時更新UI。

_TapboxCState對象:

  • 管理_highlight狀態。
  • GestureDetector監聽全部輕擊事件。 隨着用戶點擊,它添加高亮(實現爲深綠色邊框)。 當用戶釋放水龍頭時,它會消除高光。
  • 按下時,擡起或點擊取消調用setState()更新界面而且_highlight狀態改變。
  • 在點擊事件中,將該狀態更改傳遞給父部件,以使用widget屬性採起適當的操做。
//---------------------------- 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 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<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) {
    // 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,
        ),
      ),
    );
  }
}

備用實現可能會將高亮狀態導出到父級,同時在內部保持活動狀態,但若是你問某人使用那個水龍頭盒,他們可能會抱怨說這沒有什麼意義。 開發人員會關心該框是否處於活動狀態。開發人員可能不在意突出顯示是如何管理的,而且傾向於輕敲框處理這些細節。

Dart代碼:lib/main.dart

其它交互式小部件

Flutter提供各類按鈕和相似的交互式小部件。 這些小部件中的大多數實現了Material Design指南,它們定義了一組具備自認UI的組件。

若是你願意,你可使用GestureDetector來創建任何自定義小部件的交互性。 您能夠在管理狀態Flutter圖庫中找到GestureDetector的示例。

注意:Flutter還提供了一組名爲Cupertino的iOS風格的小部件。

當你須要交互性時,最容易使用預製的小部件之一。 這是一個部分列表:

標準小部件:

材料組件:

資源

將交互添加到您的應用時,如下資源可能會有所幫助。

相關文章
相關標籤/搜索