Flutter 可滾動Widget

前言

本文介紹 Flutter 中可滾動的 Widget滑蚯,主要有 ListView喷户、GridView和PageView。

一趴俘、ListView

ListView 是可以線性排列子Widget 的可滾動Widget蜗巧。ListView 可以和數(shù)據(jù)綁定用來實現(xiàn)瀑布流。

1. 使用方法

① 使用默認的構造函數(shù)蕾盯,給 children 屬性賦值
② 使用 ListView.builder幕屹,可用于和數(shù)據(jù)綁定實現(xiàn)大量或無限的列表
③ 使用 ListView.separated,具有分割項的 ListView.builder
④ 使用 ListView.custom级遭,需要使用 SliverChildDelegate

(1)使用默認的構造函數(shù)望拖,給 children 屬性賦值

  • 使用場景:
    適用于數(shù)據(jù)量少的情況
  • 代碼示例:
import 'package:flutter/material.dart';

class ListViewDefaultWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('ListViewDefaultWidget'),
        ),
        body: ListView(
          children: <Widget>[
            ListTile(
              title: new Text('Title1'),
            ),
            ListTile(
              title: new Text('Title2'),
            ),
            ListTile(
              title: new Text('Title3'),
            ),
            ListTile(
              title: new Text('Title4'),
            ),
            ListTile(
              title: new Text('Title5'),
            ),
            ListTile(
              title: new Text('Title6'),
            ),
          ],
        ),
      ),
    );
  }
}
  • 運行結果:


    Screenshot_1562897333.png

(2)使用 ListView.builder,可用于和數(shù)據(jù)綁定實現(xiàn)大量或無限的列表

  • 使用場景:
    適用于構建大量或無限的列表挫鸽,并且 ListView.builder 只會構建那些實際可見的子Widget说敏。

大部分屬性和 ListView 一樣,多了 itemCount 和 @required IndexedWidgetBuilder itemBuilder 屬性丢郊。
itemCount:代表子Widget 的數(shù)量盔沫,可以讓 ListView 預估最大滑動距離医咨,從而提升性能。如果不賦值架诞,為null拟淮,則子節(jié)點數(shù)由[itemBuilder]返回null的最小索引確定。
@required IndexedWidgetBuilder itemBuilder:itemBuilder 用于創(chuàng)建實際可見的子Widget谴忧,只有索引大于或等于零且小于 itemCount 才會調用 itemBuilder很泊。

  • 代碼示例:
import 'package:flutter/material.dart';

void main() => runApp(ListViewBuilderWidget(
    items: List<String>.generate(10000, (i) => "Item $I"),
));

class ListViewBuilderWidget extends StatelessWidget {

  List<String> items;

  ListViewBuilderWidget({Key key, @required this.items}) :super(key: key);

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('ListViewBuilderWidget'),
        ),
        body: ListView.builder(
            itemCount: items.length,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text('${items[index]}'),
              );
            }),
      ),
    );
  }
}

(3)使用 ListView.separated,具有分割項的 ListView.builder

  • 使用場景:
    適用于需要分割線的列表沾谓。

相比 ListView.builder 多了一個 separatorBuilder委造。

  • 代碼示例:
import 'package:flutter/material.dart';

class ListViewBuilderWidget extends StatelessWidget {

  List<String> items;

  ListViewBuilderWidget({Key key, @required this.items}) :super(key: key);

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('ListViewBuilderWidget'),
        ),
        body: ListView.builder(
            itemCount: items.length,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text('${items[index]}'),
              );
            }),
      ),
    );
  }
}
  • 運行結果:


(4)使用 ListView.custom,需要使用 SliverChildDelegate

  • 使用場景:
    SliverChildDelegate 有個默認實現(xiàn) SliverChildListDelegate均驶,我們可以用 SliverChildListDelegate 來實現(xiàn) ListView.custom昏兆。
  • 代碼示例:
import 'package:flutter/material.dart';

class ListViewCustomWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('ListViewCustomWidget'),
        ),
        body: ListView.custom(
            childrenDelegate: new SliverChildListDelegate(<Widget>[
          ListTile(
            title: new Text('Title1'),
          ),
          ListTile(
            title: new Text('Title2'),
          ),
          ListTile(
            title: new Text('Title3'),
          ),
          ListTile(
            title: new Text('Title4'),
          ),
          ListTile(
            title: new Text('Title5'),
          ),
        ])),
      ),
    );
  }
}

2. ListView 的構造函數(shù)及參數(shù)說明

class ListView extends BoxScrollView {
  ListView({
    Key key,//可選;類型Key辣恋;Widget的標識
    Axis scrollDirection = Axis.vertical,//可選亮垫;類型Axis;滑動的方向
    bool reverse = false,//可選伟骨;類型bool饮潦;列表項的排列順序
    ScrollController controller,//可選;類型ScrollController携狭;控制ListView的滾動位置
    bool primary,//可選继蜡;類型bool;是否是父級關聯(lián)的主滾動視圖
    ScrollPhysics physics,//可選逛腿;類型ScrollPhysics稀并;設置滾動效果
    bool shrinkWrap = false,//可選;類型bool单默;是否根據(jù)列表項的總長度來設置ListView的長度
    EdgeInsetsGeometry padding,//可選碘举;類型EdgeInsetsGeometry;ListView 的內邊距
    this.itemExtent,//可選搁廓;類型double引颈;列表項的大小
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    double cacheExtent,
    List<Widget> children = const <Widget>[],
    int semanticChildCount,
    DragStartBehavior dragStartBehavior = DragStartBehavior.down,
  }) 
    ...
}

二、GridView

GridView 是一個可以構建二維網(wǎng)格列表的可滾動Widget境蜕。

1. 使用方法:

① 使用默認的構造函數(shù)蝙场,給 children 屬性賦值
② 使用 GridView.count
③ 使用 GridView.extent
④ 使用 GridView.builder,可用于和數(shù)據(jù)綁定實現(xiàn)大量或無限的列表
⑤ 使用 GridView.custom

(1)使用默認的構造函數(shù)粱年,給 children 屬性賦值

  • 使用場景:
    適用于使用少量 子Widget 的GridView
  • 代碼示例:
import 'package:flutter/material.dart';

class GridViewDefaultWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
          appBar: AppBar(title: new Text('Flutter 可滾動Widget -- GridView')),
          body: GridView(
            gridDelegate:
                SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),//3列
            children: <Widget>[
              ListTile(
                title: Text('Title1'),
              ),
              ListTile(
                title: Text('Title2'),
              ),
              ListTile(
                title: Text('Title3'),
              ),
              ListTile(
                title: Text('Title4'),
              ),
              ListTile(
                title: Text('Title5'),
              ),
              ListTile(
                title: Text('Title6'),
              ),
            ],
          )),
    );
  }
}
  • 運行結果:


(2)使用 GridView.count
將構造函數(shù)里的 gridDelegate 屬性售滤,拆分成了 crossAxisCount、mainAxisSpacing、crossAxisSpacing 和 childAspectRatio完箩。

  • 代碼示例:
import 'package:flutter/material.dart';

class GridViewCountWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
          appBar: AppBar(title: new Text('Flutter 可滾動Widget -- GridView')),
          body: GridView.count(
            crossAxisCount: 3,
            children: <Widget>[
              ListTile(
                title: Text('Title1'),
              ),
              ListTile(
                title: Text('Title2'),
              ),
              ListTile(
                title: Text('Title3'),
              ),
              ListTile(
                title: Text('Title4'),
              ),
              ListTile(
                title: Text('Title5'),
              ),
              ListTile(
                title: Text('Title6'),
              ),
            ],
          )),
    );
  }
}

(3)使用 GridView.extent
布局算法不一樣

  • 代碼示例:
import 'package:flutter/material.dart';

class GridViewExtentWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
          appBar: AppBar(title: new Text('Flutter 可滾動Widget -- GridView')),
          body: GridView.extent(
            maxCrossAxisExtent: 300,
            children: <Widget>[
              ListTile(
                title: Text('Title1'),
              ),
              ListTile(
                title: Text('Title2'),
              ),
              ListTile(
                title: Text('Title3'),
              ),
              ListTile(
                title: Text('Title4'),
              ),
              ListTile(
                title: Text('Title5'),
              ),
              ListTile(
                title: Text('Title6'),
              ),
              ListTile(
                title: Text('Title7'),
              ),
            ],
          )),
    );
  }
}

(4)使用 GridView.builder赐俗,可用于和數(shù)據(jù)綁定實現(xiàn)大量或無限的列表

  • 使用場景:
    用于構建大量或無限的列表,而且只會構建實際可見的子Widget嗜憔。
  • 代碼示例:
import 'package:flutter/material.dart';

void main() => runApp(GridViewBuilderWidget(
      items: List<String>.generate(10000, (i) => "Item $I"),
    ));

class GridViewBuilderWidget extends StatelessWidget {
  final List<String> items;

  GridViewBuilderWidget({Key key, @required this.items}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      home: Scaffold(
        appBar: AppBar(title: new Text('Flutter 可滾動Widget -- GridView')),
        body: GridView.builder(
          gridDelegate:
              SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4),
          itemCount: items.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text('${items[index]}'),
            );
          },
        ),
      ),
    );
  }
}

(5)使用 GridView.custom

  • 使用場景:
    增加了 childrenDelegate 的屬性秃励,類型為 SliverChildDelegate,可以自定義子Widget吉捶。
  • 代碼示例:
import 'package:flutter/material.dart';

void main() => runApp(GridViewCustomWidget(
      items: List<String>.generate(10000, (i) => "Item $I"),
    ));

class GridViewCustomWidget extends StatelessWidget {
  final List<String> items;

  GridViewCustomWidget({Key key, @required this.items}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      home: Scaffold(
        appBar: AppBar(title: new Text('Flutter 可滾動Widget -- GridView')),
        body: GridView.custom(
          gridDelegate:
              SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
          childrenDelegate: SliverChildListDelegate(<Widget>[
            ListTile(
              title: Text('Title1'),
            ),
            ListTile(
              title: Text('Title2'),
            ),
            ListTile(
              title: Text('Title3'),
            ),
            ListTile(
              title: Text('Title4'),
            ),
            ListTile(
              title: Text('Title5'),
            ),
            ListTile(
              title: Text('Title6'),
            ),
            ListTile(
              title: Text('Title7'),
            ),
            ListTile(
              title: Text('Title8'),
            ),
          ]),
        ),
      ),
    );
  }
}

2. GridView 構造函數(shù)和參數(shù)說明

class GridView extends BoxScrollView {
  GridView({
    Key key,//可選夺鲜;類型 Key;Widget的標識
    Axis scrollDirection = Axis.vertical,//可選呐舔;類型 Axis币励;滑動的方向
    bool reverse = false,//可選;類型 bool珊拼;列表項的排列順序
    ScrollController controller,//可選食呻;類型 ScrollController;控制滾動位置
    bool primary,//可選澎现;類型 bool仅胞;是否是與父級關聯(lián)的主滾動視圖
    ScrollPhysics physics,//可選;類型 ScrollPhysics剑辫;設置滾動效果干旧,值必須為ScrollPhysics的子類,比如:AlwaysScrollableScrollPhysics():可以讓 GridView 里沒有足夠的內容也能滑動妹蔽,ScrollPhysics():GridView 在沒有足夠的內容的時候不能滑動
    bool shrinkWrap = false,//可選椎眯;類型 bool;是否根據(jù)列表項的總長度來設置 GridView 的長度
    EdgeInsetsGeometry padding,//可選胳岂;類型 EdgeInsetsGeometry编整;內邊距
    @required this.gridDelegate,//必選;類型 SliverGridDelegate乳丰;控制子Widget布局的委托
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    double cacheExtent,
    List<Widget> children = const <Widget>[],
    int semanticChildCount,
  })
  ...
}

三掌测、PageView

PageView 是可以一頁一頁滑動的可滾動Widget,類似 Android 中ViewPager产园。

1.使用方式:

① 使用默認的構造函數(shù)
② 使用 PageView.builder
③ 使用 PageView.custom

(1)使用默認的構造函數(shù)汞斧,給 children 屬性賦值

  • 使用場景:
    只適用于那些只有少量子Widget 的 PageView。
  • 代碼示例:
import 'package:flutter/material.dart';

class PageViewDefaultWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: new Text('PageViewDefaultWidget'),
        ),
        body: PageView(
          onPageChanged: (index) {
            print('current page $index');
          },
          children: <Widget>[
            ListTile(
              title: new Text('Title0'),
            ),
            ListTile(
              title: new Text('Title1'),
            ),
            ListTile(
              title: new Text('Title2'),
            ),
            ListTile(
              title: new Text('Title3'),
            ),
            ListTile(
              title: new Text('Title4'),
            ),
          ],
        ),
      ),
    );
  }
}
  • 運行結果:


(2) 使用 PageView.builder

  • 使用場景:
    用于構建大量和無限的列表淆两。而且只會構建那些實際可見的子Widget奕筐。
  • 代碼示例:
    多了 itemCount 和 itemBuilder 屬性
void main() => runApp(PageViewBuilderWidget(
      items: List<String>.generate(10000, (i) => "Item $I"),
    ));

class PageViewBuilderWidget extends StatelessWidget {
  final List<String> items;

  PageViewBuilderWidget({Key key, @required this.items}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      home: Scaffold(
        appBar: AppBar(title: new Text('Flutter 可滾動Widget -- PageView')),
        body: PageView.builder(
          onPageChanged: (index) {
            print('current page $index ');
          },
          itemCount: items.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text('${items[index]}'),
            );
          },
        ),
      ),
    );
  }
}

(3)使用 PageView.custom

  • 使用場景:
    增加了childrenDelegate 的屬性踢涌,類型為 SliverChildDelegate,具有自定義子Widget 的能力插佛,不合適大量數(shù)據(jù)婶熬。
  • 代碼示例:
import 'package:flutter/material.dart';

void main() => runApp(PageViewCustomWidget());

class PageViewCustomWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      home: Scaffold(
          appBar: AppBar(title: new Text('Flutter 可滾動Widget -- PageView')),
          body: PageView.custom(
            onPageChanged: (index) {
              print('current page $index ');
            },
            childrenDelegate: SliverChildListDelegate(<Widget>[
              ListTile(title: Text('Title0')),
              ListTile(title: Text('Title1')),
              ListTile(title: Text('Title2')),
              ListTile(title: Text('Title3')),
              ListTile(title: Text('Title4')),
            ]),
          )),
    );
  }
}

2. PageView 的構造函數(shù)和參數(shù)說明

class PageView extends StatefulWidget {
  PageView({
    Key key,//可選剑勾;類型 Key埃撵;Widget的標識
    this.scrollDirection = Axis.horizontal,//可選;類型 Axis虽另;滑動方向
    this.reverse = false,//可選暂刘;類型 bool;子Widget的排列順序
    PageController controller,//可選捂刺;類型 PageController谣拣;控制滑動
    this.physics,//可選;類型 ScrollPhysics族展;設置滾動效果
    this.pageSnapping = true,//可選森缠;類型 bool;默認值false仪缸,用來禁止頁面捕捉贵涵,對自定義滾動行為有用
    this.onPageChanged,//可選;類型 ValueChanged<int>恰画;監(jiān)聽頁面的切換
    List<Widget> children = const <Widget>[],//可選宾茂;類型 List<Widget;PageView的列表項
    this.dragStartBehavior = DragStartBehavior.down,//可選拴还;類型 DragStartBehavior跨晴;確定處理拖動開始行為的方式。如果設置為DragStartBehavior.start自沧,則在檢測到拖動手勢時將開始滾動拖動行為坟奥。如果設置為DragStartBehavior.down,它將在首次檢測到向下事件時開始
  }) : 
    ...
}

總結

本文主要介紹的可滾動的 Widget拇厢,ListView爱谁、GridView 和 PageView。除了這三個之外還有很多可以滾動的 Widget孝偎,如 SingleChildScrollView访敌、CustomScrollView 等等,若需要了解更多的 Widget 可以去查看官網(wǎng)的介紹https://flutter.dev/docs/development/ui/widgets/scrolling衣盾。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末寺旺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子势决,更是在濱河造成了極大的恐慌阻塑,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件果复,死亡現(xiàn)場離奇詭異陈莽,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門走搁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來独柑,“玉大人,你說我怎么就攤上這事私植〖烧ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵曲稼,是天一觀的道長索绪。 經(jīng)常有香客問我,道長贫悄,這世上最難降的妖魔是什么者春? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮清女,結果婚禮上钱烟,老公的妹妹穿的比我還像新娘。我一直安慰自己嫡丙,他們只是感情好拴袭,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著曙博,像睡著了一般拥刻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上父泳,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天般哼,我揣著相機與錄音,去河邊找鬼惠窄。 笑死蒸眠,一個胖子當著我的面吹牛,可吹牛的內容都是我干的杆融。 我是一名探鬼主播楞卡,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脾歇!你這毒婦竟也來了蒋腮?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤藕各,失蹤者是張志新(化名)和其女友劉穎池摧,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體激况,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡作彤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年踢京,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宦棺。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖黔帕,靈堂內的尸體忽然破棺而出代咸,到底是詐尸還是另有隱情,我是刑警寧澤成黄,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布呐芥,位于F島的核電站,受9級特大地震影響奋岁,放射性物質發(fā)生泄漏思瘟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一闻伶、第九天 我趴在偏房一處隱蔽的房頂上張望滨攻。 院中可真熱鬧,春花似錦蓝翰、人聲如沸光绕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诞帐。三九已至,卻和暖如春爆雹,著一層夾襖步出監(jiān)牢的瞬間停蕉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工钙态, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留慧起,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓册倒,卻偏偏與公主長得像完慧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子剩失,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344