<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## ListView ListView是最常用的可滾動widget,它可以沿一個方向線性排布所有子widget。我們看看ListView的默認構造函數定義: ``` ListView({ ... //可滾動widget公共參數 Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController controller, bool primary, ScrollPhysics physics, EdgeInsetsGeometry padding, //ListView各個構造函數的共同參數 double itemExtent, bool shrinkWrap = false, bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, double cacheExtent, //子widget列表 List<Widget> children = const <Widget>[], }) ``` 上面參數分為兩組:第一組是可滾動widget公共參數,前面已經介紹過,不再贅述;第二組是ListView各個構造函數(ListView有多個構造函數)的共同參數,我們重點來看看這些參數,: - itemExtent:該參數如果不為null,則會強制children的"長度"為itemExtent的值;這里的"長度"是指滾動方向上子widget的長度,即如果滾動方向是垂直方向,則itemExtent代表子widget的高度,如果滾動方向為水平方向,則itemExtent代表子widget的長度。在ListView中,指定itemExtent比讓子widget自己決定自身長度會更高效,這是因為指定itemExtent后,滾動系統可以提前知道列表的長度,而不是總是動態去計算,尤其是在滾動位置頻繁變化時(滾動系統需要頻繁去計算列表高度)。 - shrinkWrap:該屬性表示是否根據子widget的總長度來設置ListView的長度,默認值為`false` 。默認情況下,ListView的會在滾動方向盡可能多的占用空間。當ListView在一個無邊界(滾動方向上)的容器中時,shrinkWrap必須為`true`。 - addAutomaticKeepAlives:該屬性表示是否將列表項(子widget)包裹在AutomaticKeepAlive widget中;典型地,在一個懶加載列表中,如果將列表項包裹在AutomaticKeepAlive中,在該列表項滑出視口時該列表項不會被GC,它會使用KeepAliveNotification來保存其狀態。如果列表項自己維護其KeepAlive狀態,那么此參數必須置為`false`。 - addRepaintBoundaries:該屬性表示是否將列表項(子widget)包裹在RepaintBoundary中。當可滾動widget滾動時,將列表項包裹在RepaintBoundary中可以避免列表項重繪,但是當列表項重繪的開銷非常小(如一個顏色塊,或者一個較短的文本)時,不添加RepaintBoundary反而會更高效。和addAutomaticKeepAlive一樣,如果列表項自己維護其KeepAlive狀態,那么此參數必須置為`false`。 > 注意:上面這些參數并非ListView特有,在本章后面介紹的其它可滾動widget也可能會擁有這些參數,它們的含義是相同的。 ### 默認構造函數 默認構造函數有一個`children`參數,它接受一個Widget列表(List)。這種方式適合只有少量的子widget的情況,因為這種方式需要將所有`children`都提前創建好(這需要做大量工作),而不是等到子widget真正顯示的時候再創建。實際上通過此方式創建的ListView和使用SingleChildScrollView+Column的方式沒有本質的區別。下面是一個例子: ``` 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'), ], ); ``` > 注意:可滾動widget通過一個List來作為其children屬性時,只適用于子widget較少的情況,這是一個通用規律,并非ListView自己的特性,像GridView也是如此。 ### ListView.builder `ListView.builder`適合列表項比較多(或者無限)的情況,因為只有當子Widget真正顯示的時候才會被創建。下面看一下ListView.builder的核心參數列表: ``` ListView.builder({ // ListView公共參數已省略 ... @required IndexedWidgetBuilder itemBuilder, int itemCount, ... }) ``` - itemBuilder:它是列表項的構建器,類型為IndexedWidgetBuilder,返回值為一個widget。當列表滾動到具體的index位置時,會調用該構建器構建列表項。 - itemCount:列表項的數量,如果為null,則為無限列表。 看一個例子: ``` ListView.builder( itemCount: 100, itemExtent: 50.0, //強制高度為50.0 itemBuilder: (BuildContext context, int index) { return ListTile(title: Text("$index")); } ); ``` ![](https://box.kancloud.cn/1d35c6fcbf4e365020937c6c9fe5661d_320x409.png) ### ListView.separated `ListView.separated`可以生成列表項之間的分割器,它除了比`ListView.builder`多了一個`separatorBuilder`參數,該參數是一個分割器生成器。下面我們看一個例子:奇數行添加一條藍色下劃線,偶數行添加一條綠色下劃線。 ``` class ListView3 extends StatelessWidget { @override Widget build(BuildContext context) { //下劃線widget預定義以供復用。 Widget divider1=Divider(color: Colors.blue,); Widget divider2=Divider(color: Colors.green); return ListView.separated( itemCount: 100, //列表項構造器 itemBuilder: (BuildContext context, int index) { return ListTile(title: Text("$index")); }, //分割器構造器 separatorBuilder: (BuildContext context, int index) { return index%2==0?divider1:divider2; }, ); } } ``` ![](https://box.kancloud.cn/e023acf120ecc8bf1cb668002a2c0b4e_320x400.png) ### 實例:無限加載列表 假設我們要從數據源異步分批拉取一些數據,然后用ListView顯示,當我們滑動到列表末尾時,判斷是否需要再去拉取數據,如果是,則去拉取,拉取過程中在表尾顯示一個loading,拉取成功后將數據插入列表;如果不需要再去拉取,則在表尾提示"沒有更多"。代碼如下: ``` class InfiniteListView extends StatefulWidget { @override _InfiniteListViewState createState() => new _InfiniteListViewState(); } class _InfiniteListViewState extends State<InfiniteListView> { static const loadingTag = "##loading##"; //表尾標記 var _words = <String>[loadingTag]; @override void initState() { _retrieveData(); } @override Widget build(BuildContext context) { return ListView.separated( itemCount: _words.length, itemBuilder: (context, index) { //如果到了表尾 if (_words[index] == loadingTag) { //不足100條,繼續獲取數據 if (_words.length - 1 < 100) { //獲取數據 _retrieveData(); //加載時顯示loading return Container( padding: const EdgeInsets.all(16.0), alignment: Alignment.center, child: SizedBox( width: 24.0, height: 24.0, child: CircularProgressIndicator(strokeWidth: 2.0) ), ); } else { //已經加載了100條數據,不再獲取數據。 return Container( alignment: Alignment.center, padding: EdgeInsets.all(16.0), child: Text("沒有更多了", style: TextStyle(color: Colors.grey),) ); } } //顯示單詞列表項 return ListTile(title: Text(_words[index])); }, separatorBuilder: (context, index) => Divider(height: .0), ); } void _retrieveData() { Future.delayed(Duration(seconds: 2)).then((e) { _words.insertAll(_words.length - 1, //每次生成20個單詞 generateWordPairs().take(20).map((e) => e.asPascalCase).toList() ); setState(() { //重新構建列表 }); }); } } ``` ![](https://box.kancloud.cn/288d3ff1c18521e179b8b89a72b9396b_320x525.png)![](https://box.kancloud.cn/2947b7f7249be1a0449668e9c970e687_320x526.png) 代碼比較簡單,讀者可以參照代碼中的注釋理解,故不再贅述。需要說明的是,`_retrieveData()`的功能是模擬從數據源異步獲取數據,我們使用english\_words包的`generateWordPairs()`方法每次生成20個單詞。 ### 添加固定表頭 很多時候我們需要給列表添加一個固定表頭,比如我們想實現一個商品列表,需要在列表頂部添加一個“商品列表”標題,效果如下: ![](https://box.kancloud.cn/e48bfae5e6b97afc2368f1a0df327d1a_360x640.png) 我們按照之前經驗,寫出如下代碼: ``` @override Widget build(BuildContext context) { return Column(children: <Widget>[ ListTile(title:Text("商品列表")), ListView.builder(itemBuilder: (BuildContext context, int index) { return ListTile(title: Text("$index")); }), ]); } ``` 然后運行,發現并沒有出現我們期望的效果,相反觸發了一個異常; ``` Error caught by rendering library, thrown during performResize()。 Vertical viewport was given unbounded height ... ``` 從異常信息中我們可到是因為ListView高度邊界無法確定引起,所以解決的辦法也很明顯,我們需要給ListView指定邊界,我們通過`SizedBox`指定一個列表高度看看是否生效: ``` ... //省略無關代碼 SizedBox( height: 400, //指定列表高度為400 child: ListView.builder(itemBuilder: (BuildContext context, int index) { return ListTile(title: Text("$index")); }), ), ... ``` 運行效果如下: ![](../Users/duwen/Documents/resource/flutter%20in%20action/book/docs/imgs/20190115153643.png) 可以看到,現在沒有觸發異常并且列表已經顯示出來了,但是我們的手機屏幕高度要大于400,所以底部會有一些空白,那如果我們要實現列表鋪滿除過表頭以外的屏幕空間呢?直觀的方法是我們動態計算,用屏幕高度減去狀態欄、導航欄、表頭的高度即為剩余屏幕高度,代碼如下: ``` ... //省略無關代碼 SizedBox( //Material設計規范中狀態欄、導航欄、ListTile高度分別為24、56、56 height: MediaQuery.of(context).size.height-24-56-56, child: ListView.builder(itemBuilder: (BuildContext context, int index) { return ListTile(title: Text("$index")); }), ) ... ``` 運行效果如下: ![](https://box.kancloud.cn/e48bfae5e6b97afc2368f1a0df327d1a_360x640.png) 可以看到,我們期望的效果實現了,但是這種方法并不優雅,如果頁面布局發生變化,如表頭布局調整導致表頭高度改變,那么剩余空間的高度就得重新計算,那么有什么方法可以自動拉升ListView以填充屏幕剩余空間的方法嗎?當然有!答案就是Flex。前面已經介紹過在Flex布局中,可以使用Expanded自動拉伸組件大小的Widget,我們也說過Column是繼承自Flex的,所以我們可以直接使用Column+Expanded來實現,代碼如下: ``` @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")); }), ), ]); } ``` ### 總結 本節主要介紹了ListView的一些公共參數以及常用的構造函數。不同的構造函數對應了不同的列表項生成模型,如果需要自定義列表項生成模型,可以通過`ListView.custom`來自定義,它需要實現一個SliverChildDelegate用來給ListView生成列表項widget,更多詳情請參考API文檔。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看