flutter widget: ListView

ListView 是一個線性布局的widgets 列表.
ListView -extends->BoxScrollView -extends->ScrollView -extends->StatelessWidget

ListView是最常用的滑動組件。它在滾動方向上一個接一個地顯示它的孩子。在交叉軸中,需要孩子填充ListView。

如果子控件非空,使用 itemExtent強制指定子控件高度范圍。通過itemExtent設置值,比子控件自己決定范圍更高效,因為滾動機制可以通過設置的預設的itemExtent來節約工作,比如在滾動位置急劇變化的時候。

在構建ListView時有4中選擇:
  1. 利用顯示的自列表來構造List<Widget>。此構造函數適合于具有少量子元素的列表視圖,因為構造列表需要為可能顯示在列表視圖中的每個子元素執行工作,而不僅僅是那些實際可見的子元素。
  2. ListView.builder利用IndexedWidgetBuilder來按需構造。這個構造函數適合于具有大量(或無限)子視圖的列表視圖,因為構建器只對那些實際可見的子視圖調用。
  3. 使用ListView.separated構造函數,采用兩個IndexedWidgetBuilder:itemBuilder根據需要構建子項separatorBuilder類似地構建出現在子項之間的分隔符子項。此構造函數適用于具有固定數量的子控件的列表視圖。
  4. 使用ListView.customSliverChildDelegate構造,它提供了定制子模型的其他方面的能力。 例如,SliverChildDelegate可以控制用于估計實際上不可見的孩子的大小的算法。

控制滾動的初始offset,可以通過設置ScrollController.initialScrollOffset屬性。

默認情況下,ListView將自動填充列表的可滾動的末端,以避免MediaQuery的填充所指示的部分阻塞。若要避免此行為,請重寫可以空的padding屬性。

ListView.builder demo
body: new ListView.builder(
        padding: new EdgeInsets.all(5.0),
        itemExtent: 50.0,
        itemBuilder: (BuildContext context,int index){
          return new Text("text $index");
        },
      ),
image.png

這里的listView 可以無限往下滑動;)

ListView.separated demo
   body: new ListView.separated(
          itemBuilder: (BuildContext context, int index) {
            return new Text("text $index");
          },
          separatorBuilder: (BuildContext context, int index) {
            return new Container(height: 1.0, color: Colors.red);
          },
          itemCount: 40),
image.png
子結點的生命周期
1.創建

在布局列表時,可見的子元素、狀態和呈現對象將基于現有控件(例如當使用默認構造函數時)或延遲提供的控件(例如當使用ListView.builder構造函數時)延遲地創建。

2.銷毀

當從視圖滑出時,關聯的元素子樹、狀態和呈現對象將被銷毀。當向后滾動時,位于列表中相同位置的新子節點將與新元素、狀態和呈現對象一起延遲地重新創建。

3.銷毀時的數據保存

為了保存子元素在視圖中滾動和退出視圖時的狀態,可以做以下選擇:
1> 將與UI狀態驅動無關的業務邏輯從列表子子樹中移出。例如,如果一個列表包含來自高速緩存的網絡響應的帶有上投票數的帖子,那么將帖子列表和上投票數存儲在列表外部的數據模型中。讓列表子UI子樹很容易從真實模型對象的源中重新創建。使用子控件子樹中的StatefulWidget只存儲瞬時UI狀態。
2>KeepAlive作為需要保存的列表子控件子樹的root結點。KeepAlive使得孩子子樹的頂部結點渲染對象的子結點保持存活。當關聯的頂部渲染對象滾動到視圖之外時,列表將子對象的渲染對象(以及通過擴展,其關聯的元素和狀態)保存在高速緩存列表中,而不是銷毀它們。當滾動回到視圖中時,渲染對象將按照當前現狀被重新繪制(如果在中間階段沒有被標記為臟)。

這只在addAutomaticKeepAlivesaddRepaintBoundaries為false的情況下有效,因為這些參數導致ListView將每個子小部件子樹與其他小部件包裝在一起。
3> 使用AutomaticKeepAlive控件(當addautomatickeepalives設置為ture的時候默認會插入)。而不是向KeepAlive一樣,當滑出屏幕的無條件的緩存孩子的子樹,AutomaticKeepAlive可以讓子樹的派生的邏輯控制是否需要緩存該子樹。

例如,EditableText 會在它有輸入焦點時發送子結點子樹以便保持存活狀態。 如果它沒有焦點,并且沒有其他派生類通過KeepAliveNotification發出保持活動的信號,則滾動選擇時將清除列表子元素子樹。
AutomaticKeepAlive派生類通常使用AutomaticKeepAliveClientMixin發信號通知它保持活動狀態,然后實現wantKeepAlive getter并調用updateKeepAlive

換到CustomScrollView

ListView基本上是一個CustomScrollView,在CustomScrollView.slivers屬性中僅僅有一個SliverList。
如果ListView不滿足需求,例如因為滾動視圖既有列表又有網格,或者因為列表要與SliverAppBar等組合在一起,所以直接將代碼從使用ListView移植到使用 CustomScrollView直接。

ListView上的key,scrollDirection,reverse,controller,primary,physics和shrinkWrap屬性直接映射到CustomScrollView上具有相同名稱的屬性。

CustomScrollView.slivers屬性應該是包含SliverList或SliverFixedExtentList的列表; 如果是前者,ListView上的itemExtent為null,如果是后者itemExtent不為null。

ListView上的childrenDelegate屬性對應于SliverList.delegate(或SliverFixedExtentList.delegate)屬性。 新的ListView構造函數的children參數對應于childrenDelegate是具有相同參數的SliverChildListDelegate。 新的ListView.builder構造函數的itemBuilder和childCount參數對應于childrenDelegate是一個帶有匹配參數的SliverChildBuilderDelegate。

padding屬性對應于在CustomScrollView.slivers屬性中具有SliverPadding而不是列表本身,并且使SliverList成為SliverPadding的子級。

CustomScrollViews不會自動避免像ListView這樣的MediaQuery障礙。 要重現該行為,請將slivers包裹在SliverSafeAreas中。

將代碼移植到使用CustomScrollView后,可以將其他slivers(例如SliverGrid或SliverAppBar)放入CustomScrollView.slivers列表中。

demo

body: new ListView(
        shrinkWrap: true,
        padding: const EdgeInsets.all(20.0),
        children: <Widget>[
          const Text('I\'m dedicating every day to you'),
          const Text('Domestic life was never quite my style'),
          const Text('When you smile, you knock me out, I fall apart'),
          const Text('And I thought I was so smart'),
        ],
      ),
image.png
 body: new CustomScrollView(
        shrinkWrap: true,
        slivers: <Widget>[
          new SliverPadding(
            padding: const EdgeInsets.all(20.0),
            sliver: new SliverList(
                delegate: new SliverChildListDelegate(<Widget>[
                  const Text('I\'m dedicating every day to you'),
                  const Text('Domestic life was never quite my style'),
                  const Text('When you smile, you knock me out, I fall apart'),
                  const Text('And I thought I was so smart'),
                  const Text('I\'m dedicating every day to you'),
                  const Text('Domestic life was never quite my style'),
                  const Text('When you smile, you knock me out, I fall apart'),
                  const Text('And I thought I was so smart'),
                ])),
          )
        ],
      ),
image.png
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,098評論 25 708
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,864評論 2 59
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網絡請求組件 FMDB本地數據庫組件 SD...
    陽明AGI閱讀 16,003評論 3 119
  • 今天真的很開心,你們是我的動力,也是小山人的引擎 我這幾個月的堅持,都值了 今天我跟我的創業導師談了詳細談了小山人...
    星球里的外星人閱讀 258評論 0 0
  • 1. 黑黑烏鴉,飛上樹杈,光光籬笆,冬天來啦~ 烏鴉呱呱,沒有筑家,現在來搭,已經遲啦~ 告誡大家,不...
    水木清華12008閱讀 224評論 1 1