Flutter學(xué)習(xí)之九 ListView

極端很容易鳞贷,平衡才是最難的。

???? Flutter學(xué)習(xí)之八 Container

前言

Flutter中的ListView的地位虐唠,就好比于iOS中的UITableView搀愧,算是最常用的可滾動(dòng)組件之一,它可以沿一個(gè)方向線性排布所有子組件凿滤,并且它也支持列表項(xiàng)懶加載(在需要時(shí)才會(huì)創(chuàng)建)妈橄。

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

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

ListView({
  ...  
  //可滾動(dòng)widget公共參數(shù)
  Axis scrollDirection = Axis.vertical,
  bool reverse = false,
  ScrollController? controller,
  bool? primary,
  ScrollPhysics? physics,
  EdgeInsetsGeometry? padding,
  
  //ListView各個(gè)構(gòu)造函數(shù)的共同參數(shù)  
  double? itemExtent,
  Widget? prototypeItem, //列表項(xiàng)原型,后面解釋
  bool shrinkWrap = false,
  bool addAutomaticKeepAlives = true,
  bool addRepaintBoundaries = true,
  double? cacheExtent, // 預(yù)渲染區(qū)域長(zhǎng)度
    
  //子widget列表
  List<Widget> children = const <Widget>[],
})

上面的可滑動(dòng)公共參數(shù)就不再贅述了翁脆,下面是ListView各個(gè)構(gòu)造函數(shù)(ListView有多個(gè)構(gòu)造函數(shù))的共同參數(shù)眷蚓,我們重點(diǎn)來(lái)看看這些參數(shù):

  • itemExtent:該參數(shù)如果不為null,則會(huì)強(qiáng)制children的“長(zhǎng)度”為itemExtent的值反番;這里的“長(zhǎng)度”是指滾動(dòng)方向上子組件的長(zhǎng)度沙热,也就是說(shuō)如果滾動(dòng)方向是垂直方向,則itemExtent代表子組件的高度爵川;如果滾動(dòng)方向?yàn)樗椒较蚯薰保瑒titemExtent就代表子組件的寬度。在ListView中颇蜡,指定itemExtent比讓子組件自己決定自身長(zhǎng)度會(huì)有更好的性能鳖目,這是因?yàn)橹付?code>itemExtent后,滾動(dòng)系統(tǒng)可以提前知道列表的長(zhǎng)度,而無(wú)需每次構(gòu)建子組件時(shí)都去再計(jì)算一下抢韭,尤其是在滾動(dòng)位置頻繁變化時(shí)(滾動(dòng)系統(tǒng)需要頻繁去計(jì)算列表高度),和原生很像鳍贾。

  • prototypeItem:如果我們知道列表中的所有列表項(xiàng)長(zhǎng)度都相同但不知道具體是多少,這時(shí)我們可以指定一個(gè)列表項(xiàng)咆爽,該列表項(xiàng)被稱為prototypeItem(列表項(xiàng)原型)。指定 prototypeItem后,可滾動(dòng)組件會(huì)在 layout 時(shí)計(jì)算一次它延主軸方向的長(zhǎng)度漾稀,這樣也就預(yù)先知道了所有列表項(xiàng)的延主軸方向的長(zhǎng)度,所以和指定 itemExtent一樣缕贡,指定prototypeItem會(huì)有更好的性能收擦。注意,itemExtentprototypeItem互斥宴猾,不能同時(shí)指定它們。

  • shrinkWrap:該屬性表示是否根據(jù)子組件的總長(zhǎng)度來(lái)設(shè)置ListView的長(zhǎng)度讹剔,默認(rèn)值為false 。默認(rèn)情況下由捎,ListView會(huì)在滾動(dòng)方向盡可能多的占用空間。當(dāng)ListView在一個(gè)無(wú)邊界(滾動(dòng)方向上)的容器中時(shí)为居,shrinkWrap必須為true

  • addAutomaticKeepAlives:該屬性比較復(fù)雜,以后再說(shuō)蹬音。

  • addRepaintBoundaries:該屬性表示是否將列表項(xiàng)(子組件)包裹在RepaintBoundary組件中劫狠。RepaintBoundary 讀者可以先簡(jiǎn)單理解為它是一個(gè)”繪制邊界“,將列表項(xiàng)包裹在RepaintBoundary中可以避免列表項(xiàng)不必要的重繪懦砂,但是當(dāng)列表項(xiàng)重繪的開(kāi)銷非常小(如一個(gè)顏色塊,或者一個(gè)較短的文本)時(shí),不添加RepaintBoundary反而會(huì)更高效(具體原因會(huì)在本書(shū)后面 Flutter 繪制原理相關(guān)章節(jié)中介紹)。如果列表項(xiàng)自身來(lái)維護(hù)是否需要添加繪制邊界組件愿阐,則此參數(shù)應(yīng)該指定為false

默認(rèn)構(gòu)造函數(shù)有一個(gè)children參數(shù)辛孵,它接受一個(gè)Widget列表(List<Widget>)焚廊。這種方式適合只有少量的子組件數(shù)量已知且比較少的情況嚼隘,反之則應(yīng)該使用ListView.builder 按需動(dòng)態(tài)構(gòu)建列表項(xiàng)谤狡。

注意:雖然這種方式將所有children一次性傳遞給 ListView,但子組件仍然是在需要時(shí)才會(huì)加載(build(如有)、布局、繪制)暖呕,也就是說(shuō)通過(guò)默認(rèn)構(gòu)造函數(shù)構(gòu)建的ListView 也是基于 Sliver的列表懶加載模型。

舉個(gè)栗子:

_listView() {
  return ListView(
    shrinkWrap: true,
    padding: const EdgeInsets.all(20.0),
    children: const <Widget>[
      Text('張三'),
      Text('李四'),
      Text('王五'),
      Text('趙六'),
    ],
  );
}

效果如下:


ListView.png

可以看到戚揭,雖然使用默認(rèn)構(gòu)造函數(shù)創(chuàng)建的列表也是懶加載的,但我們還是需要提前將Widget創(chuàng)建好靴姿,等到真正需要加載的時(shí)候才會(huì)對(duì) Widget 進(jìn)行布局和繪制恨旱。

ListView.builder

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

ListView.builder({
  // ListView公共參數(shù)已省略  
  ...
  required IndexedWidgetBuilder itemBuilder,
  int itemCount,
  ...
})

+itemBuilder:它是列表項(xiàng)的構(gòu)建器耕陷,類型為IndexedWidgetBuilder嗜诀,返回值為一個(gè)widget。當(dāng)列表滾動(dòng)到具體的index位置時(shí)拂蝎,會(huì)調(diào)用該構(gòu)建器構(gòu)建列表項(xiàng)皇钞。

  • itemCount:列表項(xiàng)的數(shù)量掉盅,如果為null蔓钟,則為無(wú)限列表侣集。

舉個(gè)栗子:

_listViewBuild() {
  return ListView.builder(
      itemCount: 100,
      itemExtent: 50.0, //強(qiáng)制高度為50.0
      itemBuilder: (BuildContext context, int index) {
        return ListTile(title: Text("_listViewBuild $index"));
      });
}

效果如下:


ListViewBuilder.gif

ListView.separated

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

舉個(gè)栗子:

_listViewSeparated() {
  // widget預(yù)定義以供復(fù)用。
  Widget blue = Container(color: Colors.blue, height: 10);
  Widget red = Container(color: Colors.red, height: 10);
  return ListView.separated(
    itemCount: 100,
    //列表項(xiàng)構(gòu)造器
    itemBuilder: (BuildContext context, int index) {
      return ListTile(title: Text("$index"));
    },
    //分割器構(gòu)造器
    separatorBuilder: (BuildContext context, int index) {
      return index % 2 == 0 ? blue : red;
    },
  );
}

效果如下:


_listViewSeparated.gif

當(dāng)然還有一個(gè)場(chǎng)景就是屎媳,每個(gè)item之間有一定的間距丹禀,也可以用這個(gè)實(shí)現(xiàn)。

總結(jié)

  • 我在開(kāi)發(fā)中一般使用ListView.builder,里面的itemBuilder對(duì)應(yīng)的原生的cell,然后自定義cell就行了,用起來(lái)還是比較舒服的。
  • 如果item之間有分割線或是間距的面哥,ListView.separated當(dāng)首選吱涉。

后記

常見(jiàn)的列表頁(yè)面ListView一般都能搞定鳖链,其對(duì)應(yīng)iOS 原生的UITableView。那原生的UICollectionView呢,在Flutter中要使用GridView,這個(gè)以后再寫(xiě)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子把夸,更是在濱河造成了極大的恐慌岂膳,老刑警劉巖簸喂,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竿奏,死亡現(xiàn)場(chǎng)離奇詭異候址,居然都是意外死亡稳其,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)龄恋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)铣卡,“玉大人蝉仇,你說(shuō)我怎么就攤上這事睦疫〉紫矗” “怎么了亥揖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)夏跷,這世上最難降的妖魔是什么猫态? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任疚膊,我火速辦了婚禮酿联,結(jié)果婚禮上喳张,老公的妹妹穿的比我還像新娘舅桩。我一直安慰自己排监,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布熄浓。 她就那樣靜靜地躺著情臭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赌蔑。 梳的紋絲不亂的頭發(fā)上俯在,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音娃惯,去河邊找鬼跷乐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛趾浅,可吹牛的內(nèi)容都是我干的愕提。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼皿哨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浅侨!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起证膨,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤如输,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后央勒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體不见,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年崔步,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稳吮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡井濒,死狀恐怖灶似,靈堂內(nèi)的尸體忽然破棺而出慎陵,到底是詐尸還是另有隱情,我是刑警寧澤喻奥,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站捏悬,受9級(jí)特大地震影響撞蚕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜过牙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一甥厦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寇钉,春花似錦刀疙、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至撵溃,卻和暖如春疚鲤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缘挑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工集歇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人语淘。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓诲宇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親惶翻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子姑蓝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容