Flutter | 狀態管理

345丶發表於2021-03-11

792ecfe6228e4f61b130c8987e88c946_tplv-k3u1fbpfcp-zoom-1

本文示例程式碼

概述

響應式的程式設計框架中都有一個永恆的主題 "狀態管理",無論是在 React/Vue 還是在Flutter中,他們的問題和解決的思想都是一致的

額.....,響應式程式設計是啥意思????????

響應式程式設計,以下答案參考自百度百科:

響應式程式設計是一種面向資料流和變化傳播的正規化

在指令式程式設計中, a+b = c ,表示 將表示式的結果賦值給 c,而之後改變 b 或者 c 不會影響到 A

在響應式程式設計中,c 的值會隨著 a 或者 b 的值更新而更新

看到這裡終於明白響應是程式設計是個啥玩意了

img

其實上面的例子中,a 和 b 指的就是狀態,而 c 則代表的就是使用者可以看到的,如介面等。

也就是說,當狀態發生變化的時候,頁面也會隨之重新整理,

個人理解:響應式程式設計解決的就是資料一致性的問題。保證在狀態發生改變之後,可以立即同步到頁面中;

Flutter 中的狀態管理

在 Flutter 中,StatefulWidget 的狀態應該被誰管理?

Widget 本身?父 Widget 還是別的物件來管理? 答案是取決於實際情況

以下是管理狀態最常見的方法:

  • Widget 管理自己的狀態

    如果狀態時有關介面外觀效果的,例如顏色,動畫,那麼狀態最好由 Widget本身來管理

  • Widget 管理子 Widget 的狀態

    如果狀態是使用者資料,如選中的狀態,滑塊的位置,則該狀態最好由父 Widget 管理

  • 混合管理(父 Widget 和 子 Widget 都管理狀態)

    如果某一個狀態是不同 Widget 共享的,則最後由他們共同的 父 Widget 管理

其實在 Widget 內部管理狀態封裝性會好一些,而在父 Widget 中管理會比較靈活。有些時候,如果不確定到底應該怎麼管理,那麼首選的應該是在父 Widget 中管理。

實踐

  • Widget 管理自身的狀態

    class _TapBoxAState extends State<TapBoxA> {
      bool _active = false;
    
      void _handleTap() {
        setState(() {
          _active = !_active;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          onTap: _handleTap,
          child: Container(
            width: 200,
            height: 200,
            child: Center(
              child: Text(_active ? "Active" : "Inactive",
                  style: TextStyle(fontSize: 32, color: Colors.white)),
            ),
            decoration:
                BoxDecoration(color: _active ? Colors.lightBlue : Colors.green),
          ),
        );
      }
    }
    複製程式碼
    345
  • 父 Widget 管理子 Widget 的狀態

    對於 父 Widget 來說,管理狀態並告訴子Widget合適更新通常是比較好的方式。例如 IconButton 是一個圖示按鈕,他是他是一個無狀態的 Widget,應為我們父 Widget 需要知道該按鈕是否被點選來採取相應的處理

    示例:

    //------------------------ ParentWidget -----------------------
    class ParentWidget extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _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: TapBoxB(
            active: _active,
            onChange: _handleTapBoxChanged,
          ),
        );
      }
    }
    //------------------------ TapBoxB -----------------------
    class TapBoxB extends StatelessWidget {
      final bool active;
      final ValueChanged<bool> onChange;
    
      TapBoxB({Key key, this.active, this.onChange});
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          child: Container(
            width: 200,
            height: 200,
            child: Center(
              child: Text(active ? "Active" : "Inactive",
                  style: TextStyle(fontSize: 32, color: Colors.white)),
            ),
            decoration:
                BoxDecoration(color: active ? Colors.lightBlue : Colors.green),
          ),
          onTap: () => onChange(!active),
        );
      }
    }
    複製程式碼

    上面栗子中,TapBoxB 通過回撥將自己的狀態傳遞到父元件,狀態由父元件管理,因此它的父元件為 StatefullWidget ,但是由於 TapBoxB 本身不管理任何狀態,所以是 StatelessWidget

    每次 setState 的時候都會重新執行 build 方法,將狀態傳遞到子元件,因此TabBoxB 不需要對狀態進行管理,直接使用即可

    執行效果和上圖一樣

  • 混合狀態管理

    對於一些元件來說,混合管理的方式會非常有用,元件自身管理一些內部的狀態,而父元件管理一些其他的外部狀態

    示例:

    //------------------------ ParentWidget -----------------------
    class ParentWidget extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _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: TapBoxC(
            active: _active,
            onChange: _handleTapBoxChanged,
          ),
        );
      }
    }
    
    //------------------------ TapBoxC -----------------------
    class TapBoxC extends StatefulWidget {
      final bool active;
      final ValueChanged<bool> onChange;
    
      TapBoxC({Key key, this.active, this.onChange});
    
      @override
      State<StatefulWidget> createState() => _TapBoxCState();
    }
    
    class _TapBoxCState extends State<TapBoxC> {
      bool _highlight = false;
    
      void _handleTapDown(TapDownDetails detailis) {
        setState(() {
          _highlight = true;
        });
      }
    
      void _handleTapUp(TapUpDetails detailis) {
        setState(() {
          _highlight = false;
        });
      }
    
      void _handleTapCancel() {
        setState(() {
          _highlight = false;
        });
      }
    
      void _handleTap() {
        widget.onChange(!widget.active);
      }
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          onTap: _handleTap,
          onTapDown: _handleTapDown,
          //按下事件
          onTapUp: _handleTapUp,
          //抬起事件
          onTapCancel: _handleTapCancel,
          //取消事件
          child: Container(
            width: 200,
            height: 200,
            child: Center(
              child: Text(widget.active ? "Active" : "Inactive",
                  style: TextStyle(fontSize: 32, color: Colors.white)),
            ),
            decoration: BoxDecoration(
                color: widget.active ? Colors.lightBlue : Colors.green,
                border: _highlight
                    ? Border.all(color: Colors.teal[700], width: 10)
                    : null),
          ),
        );
      }
    }
    複製程式碼

    效果如下:

    345

    手指按下的時候回出現一個邊框,抬起時,邊框消失,點選完成之後,元件的顏色改變

    對於開發人員來說,只關心元件是否處於 Active 狀態,而不會在意邊框的具體實現,所以,我們將邊框的狀態隱藏在內部,對外只暴露 active 狀態

  • 全域性的狀態管理

    當應用中需要一些跨元件,路由的狀態需要同步時,上面的幾種方法便很難勝任了。比如,在設定頁面修改應用的語言,為了讓設定實時生效,我們期望在語言狀態改變時,App 中依賴語言的元件可以重新 build 一下,但是這些依賴語言的元件並不在一起,所以這種情況使用上面這幾種辦法很難管理

    這是正確的做法是通過一個全域性的狀態管理器來處理這種相距較遠的元件之間通訊,目前有兩種解決辦法:

    1,實現一個全域性的事件匯流排,將語言的狀態改變對應為一個事件,然後在 App 中依賴語言的元件 initState 方法中訂閱語言改變事件,當使用者切換語言之後,訂閱此事件的元件就會收到通知,收到通知後重新 setState 即可

    2,使用一些專門用於狀態管理的包,如 Provider,Redux 等,具體的使用可上 pub 檢視詳細資訊

參考

Flutter 實戰

相關文章