3.ListView

ListView是最常用的可滾動組件之一,它可以沿一個(gè)方向線性排布所有子組件,并且它也支持列表項(xiàng)懶加載(在需要時(shí)才會創(chuàng)建)

1.默認(rèn)構(gòu)造函數(shù)

我們看看ListView的默認(rèn)構(gòu)造函數(shù)定義:


ListView默認(rèn)構(gòu)造函數(shù)

注意:雖然這種方式將所有children一次性傳遞給 ListView,但子組件)仍然是在需要時(shí)才會加載(build(如有)、布局、繪制),也就是說通過默認(rèn)構(gòu)造函數(shù)構(gòu)建的 ListView 也是基于 Sliver 的列表懶加載模型??梢钥吹剑m然使用默認(rèn)構(gòu)造函數(shù)創(chuàng)建的列表也是懶加載的,但我們還是需要提前將 Widget 創(chuàng)建好,等到真正需要加載的時(shí)候才會對 Widget 進(jìn)行布局和繪制。所以我們使用最多還是ListView.builder和ListView.separated

2.ListView.builder

ListView.builder 適合列表項(xiàng)比較多或者列表項(xiàng)不確定的情況,下面看一下ListView.builder的核心參數(shù)列表:

ListView.builder

  • itemBuilder:它是列表項(xiàng)的構(gòu)建器,類型為IndexedWidgetBuilder,返回值為一個(gè)widget。當(dāng)列表滾動到具體的index位置時(shí),會調(diào)用該構(gòu)建器構(gòu)建列表項(xiàng)。
  • itemCount:列表項(xiàng)的數(shù)量,如果為null,則為無限列表
ListView.builder實(shí)例

2. ListView.separated

ListView.separated可以在生成的列表項(xiàng)之間添加一個(gè)分割組件,它比ListView.builder多了一個(gè)separatorBuilder參數(shù),該參數(shù)是一個(gè)分割組件生成器


ListView.separated

3.固定高度列表

前面說過,給列表指定 itemExtent 或 prototypeItem 會有更高的性能,所以當(dāng)我們知道列表項(xiàng)的高度都相同時(shí),強(qiáng)烈建議指定 itemExtent 或 prototypeItem 。下面看一個(gè)示例

class FixedExtentList extends StatelessWidget {
  const FixedExtentList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
        prototypeItem: ListTile(title: Text("1")),
      //itemExtent: 56,
      itemBuilder: (context, index) {
        //LayoutLogPrint是一個(gè)自定義組件,在布局時(shí)可以打印當(dāng)前上下文中父組件給子組件的約束信息
        return LayoutLogPrint(
          tag: index, 
          child: ListTile(title: Text("$index")),
        );
      },
    );
  }
}

因?yàn)榱斜眄?xiàng)都是一個(gè) ListTile,高度相同,但是我們不知道 ListTile 的高度是多少,所以指定了prototypeItem ,運(yùn)行后,控制臺打?。?/p>

指定itemExtent 或 prototypeItem高度的打印結(jié)果

可見 ListTile 的高度是 56 ,所以我們指定 itemExtent 為 56也是可以的。但是筆者還是建議優(yōu)先指定原型,這樣的話在列表項(xiàng)布局修改后,仍然可以正常工作(前提是每個(gè)列表項(xiàng)的高度相同)。

如果本例中不指定 itemExtent 或 prototypeItem ,我們看看控制臺日志信息:


沒有指定itemExtent 或 prototypeItem高度的打印結(jié)果

可以發(fā)現(xiàn),列表不知道列表項(xiàng)的具體高度,高度約束變?yōu)?0.0 到 Infinity,這樣就大大的降低了性能。

4.ListView原理

ListView 內(nèi)部組合了 Scrollable、Viewport 和 Sliver,需要注意:

1.ListView 中的列表項(xiàng)組件都是 RenderBox,并不是 Sliver, 這個(gè)一定要注意。
2.一個(gè) ListView 中只有一個(gè)Sliver,對列表項(xiàng)進(jìn)行按需加載的邏輯是 Sliver 中實(shí)現(xiàn)的。
3.ListView 的 Sliver 默認(rèn)是 SliverList,如果指定了 itemExtent ,則會使用 SliverFixedExtentList;如果 prototypeItem 屬性不為空,則會使用 SliverPrototypeExtentList,無論是是哪個(gè),都實(shí)現(xiàn)了子組件的按需加載模型

5.無限加載列表(上拉加載更多)

第一步: 頂一個(gè)一個(gè)滾動控制器

  // 定義一個(gè)的滾動控制器,
  // ScrollController間接繼承自Listenable,我們可以根據(jù)ScrollController來監(jiān)聽滾動事件
  // 可以用ScrollController來控制可滾動組件的滾動位置
ScrollController  scrollController = ScrollController();

第二步: 使用ListView.builder并添加控制器

ListView.builder(
            controller: scrollController,//注意這里一定要加上滾動的控制器,否則是無法監(jiān)聽滾動元素的
            itemBuilder: (context, index) {
              if(newList?.length == index){
                return LoadingWidget(loadText:loadingText,loadStatus: loadStatus);//自定義加載更多文案,組件
              }else{
                return Container(
                  padding: const EdgeInsets.all(10.0),
                  decoration: const BoxDecoration(
                      border: Border(
                          bottom: BorderSide(
                              width: 0.5,
                              color: Color(0xffaaaaaa)
                          )
                      )
                  ),
                  child: buildNewListItem(index),//這個(gè)根據(jù)自身需求定義組件
                );
              }
            },
            itemCount: newList!.length + 1 //這個(gè)長度一定要記得+1 需要包含那個(gè)上拉加載圖標(biāo)
        )

第三步:監(jiān)聽控制器

scrollController.addListener(() {
    /**
        調(diào)用 scrollController.position.pixels 可以獲取當(dāng)前滾動的像素點(diǎn) ;
        調(diào)用 scrollController.position.maxScrollExtent 可以獲取當(dāng)前最大可滾動位置 ;
        如果上述兩個(gè)值相等 , 那么說明已經(jīng)滾動到列表最底部了 , 此時(shí)可以執(zhí)行上拉加載更多
     */
    dynamic maxScroll = scrollController.position.maxScrollExtent;
    dynamic pixels = scrollController.position.pixels;
    if(maxScroll == pixels){
     //加載更多  
     //TODO
    }
  });

6.添加固定列表頭

@override
Widget build(BuildContext context) {
  return Column(children: <Widget>[
    ListTile(title:Text("資訊中心")),
    Expanded(
      child: ListView.builder(itemBuilder: (BuildContext context, int index) {
        return ListTile(title: Text("$index"));
      }),
    ),
  ]);
}
固定列表頭
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容