【flutter學習】之自定義組件(Stateless和Stateful)(一)


一,概述  

  在Flutter開發中,我們會經常和各種控件打交道,它們也能滿足業務的大部分需求。但是,我們往往需要將多個控件組合起來,才能實現業務的需求,而且這樣寫出來的代碼維護起來非常困難。因此,我們可以把那些需要多個控件組合才能實現的功能自定義化,成為一個自定義控件,易於維護。

二,自定義無狀態組件和有狀態組件

  Flutter框架給我們提供了StatelessWidgetStatefulWidget兩個抽象類,用於自定義控件。

  • 無狀態控件    
      首先我們看一下StatelessWidget抽象類。它可以定義一個不需要可變狀態的控件,我們可以稱其為“無狀態控件”,它通過構建一系列其他控件來描述用戶界面的一部分,構建過程以遞歸方式執行,直到用戶界面的描述完全具體化。
      比如,下面是一個名為“GreenBoard”的StatelessWidget子類的框架,它的里面包括一個Container控件,中文名稱是“容器”,然后設置color屬性,也就是背景色為綠色。

    class GreenBoard extends StatelessWidget {
    const GreenBoard({ Key key }) : super(key: key); @override Widget build(BuildContext context) { return new Container( color: const Color(0xFF2DBD3A) ); } }

      另外,我們覆蓋了build這個抽象方法,該方法用於描述由此控件所實現的那一部分用戶界面。其中,抽象類BuildContext是該控件在控件樹中的位置句柄。

    通常情況下,控件的構造函數參數不止一個,每個參數對應一個final修飾的屬性,修改上一個例子,使其成為一個可以設置背景顏色和子控件的通用控件。

    class Board extends StatelessWidget {
      ///[構造函數] 
      
    const Board({ Key key, this.color: const Color(0xFF2DBD3A), this.child, }) : super(key: key);
      ///[屬性]    final Color color; final Widget child; ///[重寫系統方法] @override Widget build(BuildContext context) {
    return new Container( color: color, child: child, ); } }
    • 總結

      • 按照Flutter框架的語法規范,控件的構造函數只能使用命名參數,命名參數可以使用@required注解為必需參數。另外語法規范還規定了,第一個參數是key,最后一個參數是childchildren或其他類似參數。  

      • 如果一個無狀態控件的父控件會定期更改控件的配置,或者它依賴於頻繁更改的繼承控件,那么優化build方法的性能,以保持流暢的渲染性能是非常重要的。有以下做法可以用於減少重新構建無狀態控件的影響:

      • 盡可能減少build方法傳遞創建的節點數量及其創建的任何控件。例如,我們可以考慮只使用Align對齊控件)或CustomSingleChildLayout自定義單個子控件布局控件),而不是精心安排Row行布局控件)、Column列布局控件)、Padding填充控件)和SizedBox指定大小的框控件)來定位單個子控件。想繪制正確的圖形效果時,考慮一下使用CustomPaint畫布控件),而不是復雜的多個Container容器)分層和Decoration裝飾)。
      • 盡可能使用const修飾控件,並為控件提供一個const構造函數,以便控件的調用方法也可以這樣做。
  • 有狀態控件
      在了解完如何使用StatelessWidget定義一個無狀態控件后,我們學習如何使用StatefulWidget定義一個具有可變狀態的控件,我們可以稱其為“有狀態控件”。首先要搞清楚的是,狀態是什么?狀態是在構建控件時可以同步讀取的信息,並且在控件的生命周期內可以改變,控件的使用者應該在狀態發生變化時使用State.setState方法及時通知框架。

      StatefulWidget的實例本身是不可變的,我們需要將其可變狀態存儲在由createState方法創建的單獨的State對象中,或者存儲在該State所訂閱的對象中。例如,我們將之前名為“GreenBoard”的StatelessWidget子類改造成StatefulWidget的子類框架。

    class GreenBoard extends StatefulWidget {
      const GreenBoard({ Key key }) : super(key: key);
    
      @override
      _GreenBoardState createState() => new _GreenBoardState();
    }
    
    class _GreenBoardState extends State<GreenBoard> {
      @override
      Widget build(BuildContext context) {
        return new Container(
          color: const Color(0xFF2DBD3A)
        );
      }
    }

      只要我們調用了一個StatefulWidget,框架就會調用createState,這意味着,在控件樹中,可能有多個不同位置的State對象與同一個StatefulWidget關聯。同樣的,如果一個StatefulWidget被我們從樹中移除,並且再次被我們插入到樹中,框架將再次調用createState來創建一個新的State對象,簡化了State對象的生命周期。

      我們再將之前名為“Board”的StatelessWidget子類改造成StatefulWidget的子類框架。除了可以設置背景顏色和子控件,還有一個可以被調用的,用來改變它的內部狀態的方法。

    class Board extends StatefulWidget {
      const Board({
        Key key,
        this.color: const Color(0xFF2DBD3A),
        this.child,
      }) : super(key: key);
    
      final Color color;
      final Widget child;
    
      _BoardState createState() => new _BoardState();
    }
    
    class _BoardState extends State<Board> {
      double _size = 1.0;
    
      void grow() {
        setState(() { _size += 0.1; });
      }
    
      @override
      Widget build(BuildContext context) {
        return new Container(
          color: widget.color,
          transform: new Matrix4.diagonal3Values(_size, _size, 1.0),
          child: widget.child,
        );
      }
    }

    StatefulWidget有兩種不同的類型:

    • 第一種是在State.initState中分配資源,並將其置於State.dispose中釋放,而且不依賴於InheritedWidget或調用State.setState。這樣的控件通常在應用程序或頁面的根部使用,並通過ChangeNotifierStream或其他這樣的對象與子控件通信。遵循這種模式的有狀態控件相對便宜(在CPU和GPU周期方面),因為它們是一次構建的,而且不會更新。因此,它們可以實現一些非常復雜的build方法。
    • 第二種是使用State.setState或依賴於InheritedWidget的控件。這些控件通常會在應用程序的生命周期中重新構建很多次,因此降低重新構建這種控件的影響至關重要。實際上,它們也可以使用State.initStateState.didChangeDependencies來分配資源,但重點是它們要重新構建。

    有以下做法可以用於減少重新構建有狀態控件的影響:

    • 把狀態推到樹葉上。例如,我們的頁面上有一個嘀嗒嘀嗒的時鍾,此時不應該將狀態置於頁面的頂部,因為這樣的話,每當時鍾嘀嗒時,整個頁面都會重新構建。我們應該創建一個專用的時鍾控件,這樣只會更新它自己。
    • 盡可能減少build方法傳遞創建的節點數量及其創建的任何控件。理想情況下,有狀態的控件只會創建一個控件,而這個控件將是一個RenderObjectWidget。很顯然,在實際開發中,我們不一定能做到這一點,但控件越接近這個理想狀態,效率就越高。
    • 如果子樹不更改,則緩存表示該子樹的控件,並在每次使用該子樹時重新使用它。重用控件的效率要比創建新的控件要高效得多,將有狀態的部分分解成一個帶有子參數的控件是這樣做的常見方法。
    • 盡可能使用const修飾控件,這相當於緩存一個控件,並重新使用它。

三,關於有狀態與無狀態的選擇

  • 自定義StatelessWidget 
    如果自定義的控件僅依賴於對象本身的配置信息,僅僅是用於展示給定的信息。那我們應該選擇使用StatelessWidget創建一個無狀態控件。
  • 自定義StatefulWidget
    如果自定義的控件可以與用戶進行交互,比如通過鍵盤輸入內容、通過滑動屏幕移動滑塊、點擊時改變狀態,又或者是隨着時間的推移而變化,比如數據Feed會更新狀態。這時我們應該選擇使用StatefulWidget創建一個有狀態控件。

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM