Flutter ListView詳解

ListView常用構造

ListView

我們可以直接使用ListView 它的實現也是直接返回最簡單的列表結構,粗糙沒有修飾。

ListView 默認構建

效果

在這裏插入圖片描述

 ///默認構建
  Widget listViewDefault(List<BaseBean> list) {
    List<Widget> _list = new List();
    for (int i = 0; i < list.length; i++) {
      _list.add(new Center(
        child: new Text(list[i].age.toString()),
      ));
    }

// 添加分割線
    var divideList =
        ListTile.divideTiles(context: context, tiles: _list).toList();
    return new Scrollbar(
      child: new ListView(
        // 添加ListView控件
        children: _list, // 無分割線
//        children: divideList, // 添加分割線/
      ),
    );
  }

ListView ListTile

ListTileFlutter 給我們準備好的widget 提供非常常見的構造和定義方式,包括文字,icon,點擊事件,一般是能夠滿足基本需求,但是就不能自己定義了

ListTile 屬性

this.leading,              // item 前置圖標
this.title,                // item 標題
this.subtitle,             // item 副標題
this.trailing,             // item 後置圖標
this.isThreeLine = false,  // item 是否三行顯示
this.dense,                // item 直觀感受是整體大小
this.contentPadding,       // item 內容內邊距
this.enabled = true,
this.onTap,                // item onTap 點擊事件
this.onLongPress,          // item onLongPress 長按事件
this.selected = false,     // item 是否選中狀態
ListTile 使用
效果

在這裏插入圖片描述

  /// ListTile代碼
  Widget listViewListTile(List<BaseBean> list) {
    List<Widget> _list = new List();
    for (int i = 0; i < list.length; i++) {
      _list.add(new Center(
        child: ListTile(
          leading: new Icon(Icons.list),
          title: new Text(list[i].name),
          trailing: new Icon(Icons.keyboard_arrow_right),
        ),
      ));
    }
    return new ListView(
      children: _list,
    );
  }

ListView builder

builder 顧名思義 構造 可以非常方便的構建我們自己定義的child佈局,所以在Flutter中非常的常用。

builder屬性詳細介紹

   //設置滑動方向 Axis.horizontal 水平  默認 Axis.vertical 垂直
        scrollDirection: Axis.vertical,
        //內間距
        padding: EdgeInsets.all(10.0),
        //是否倒序顯示 默認正序 false  倒序true
        reverse: false,
        //false,如果內容不足,則用戶無法滾動 而如果[primary]爲true,它們總是可以嘗試滾動。
        primary: true,
        //確定每一個item的高度 會讓item加載更加高效
        itemExtent: 50.0,
        //內容適配
        shrinkWrap: true,
        //item 數量
        itemCount: list.length,
        //滑動類型設置
        physics: new ClampingScrollPhysics(),
         //cacheExtent  設置預加載的區域 
         cacheExtent: 30.0, 
        //滑動監聽
//        controller ,

分析幾個比較難理解的屬性

shrinkWrap特別推薦
child 高度會適配 item填充的內容的高度,我們非常的不希望child的高度固定,因爲這樣的話,如果裏面的內容超出就會造成佈局的溢出。
shrinkWrap多用於嵌套listView中 內容大小不確定 比如 垂直佈局中 先後放入文字 listView (需要Expend包裹否則無法顯示無窮大高度 但是需要確定listview高度 shrinkWrap使用內容適配不會有這樣的影響)

primary
If the [primary] argument is true, the [controller] must be null.
在構造中默認是false 它的意思就是爲主的意思,primary爲true時,我們的controller 滑動監聽就不能使用了

physics
這個屬性幾個滑動的選擇
AlwaysScrollableScrollPhysics() 總是可以滑動
NeverScrollableScrollPhysics禁止滾動
BouncingScrollPhysics 內容超過一屏 上拉有回彈效果
ClampingScrollPhysics 包裹內容 不會有回彈

cacheExtent
這個屬性的意思就是預加載的區域
設置預加載的區域 cacheExtent 強制設置爲了 0.0,從而關閉了“預加載”

controller
滑動監聽,我們多用於上拉加載更多,通過監聽滑動的距離來執行操作。

效果

在這裏插入圖片描述

 ///listView builder 構建
  Widget listViewLayoutBuilder(List<BaseBean> list) {
    return ListView.builder(
        scrollDirection: Axis.vertical,   
        padding: EdgeInsets.all(10.0),
        reverse: false,
        primary: true,
        itemExtent: 50.0,
        shrinkWrap: true,
        itemCount: list.length,     
        cacheExtent: 30.0, 
        physics: new ClampingScrollPhysics(),
//        controller ,
        itemBuilder: (context, i) => new Container(
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  new Text(
                    "${list[i].name}",
                    style: new TextStyle(fontSize: 18.0, color: Colors.red),
                  ),
                  new Text(
                    "${list[i].age}",
                    style: new TextStyle(fontSize: 18.0, color: Colors.green),
                  ),
                  new Text(
                    "${list[i].content}",
                    style: new TextStyle(fontSize: 18.0, color: Colors.blue),
                  ),
                ],
              ),
            ));
  }

builder模式來設置分割線

我們在正常的需求中大部分是需要item的分割線的,而在builder模式中使用divide 會有種情況(divide放在item的佈局中 通過Column),我們會發現divide並沒有直接延時到item兩端而是會有左右padding
所以我們可以通過另外一種方式去實現。

1.擴大list容積 爲什麼是兩倍,因爲我們給了divide的index
 Widget listView = new ListView.builder(
              itemCount: list.length * 2 ,
              itemBuilder: (context, index) => itemDividerRow(context, index));
2. 根據下標分配item類型
itemDividerRow(context, int i) {
if (i.isOdd) {//是奇數
        return new Divider( //返回分割線
          height: 1.0,
        );
      } else {
        i = i ~/ 2;
        return getRowWidget(context, orderList[i]);  //返回item 佈局
      }
     } 
     
這樣我們就可以去實現了,builder模式分割線 

ListView separated

separated 有分離的意思,其實它就相當於我們Android中的多類型adapter,那麼關鍵就是在我們的這個屬性上separatorBuilder

separatorBuilder

它和itemBuilder同時進行渲染,在同一個item下標中可以額外的修飾或者區分

  separatorBuilder: (content, index) 
  
  itemBuilder: (content, index) 
separated設置分割線
separated設置分割線就非常的簡單了,我們直接在separatorBuilder進行操作
 separatorBuilder: (content, index) {
   
        return new Divider()
        }
效果

在這裏插入圖片描述

///  listView separated 構建 用於多類型 分割
Widget listViewLayoutSeparated(List<BaseBean> list) {
  return ListView.separated(
    itemCount: list.length,
    separatorBuilder: (content, index) {
      //和itemBuilder 同級別的執行
      if (index == 2) {
        return new Container(
          height: 40.0,
          child: new Center(
            child: new Text("類型1"),
          ),
          color: Colors.red,
        );
      } else if (index == 7) {
        return new Container(
          height: 40.0,
          child: new Center(
            child: new Text("類型2"),
          ),
          color: Colors.blue,
        );
      } else if (index == 14) {
        return new Container(
          height: 40.0,
          child: new Center(
            child: new Text("類型3"),
          ),
          color: Colors.yellow,
        );
      } else {
        return new Container();
      }
    },
    itemBuilder: (content, i) {
      return new InkWell(
        child: new Container(
            height: 30.0,
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                new Text(
                  "${list[i].name}",
                  style: new TextStyle(fontSize: 18.0, color: Colors.red),
                ),
                new Text(
                  "${list[i].age}",
                  style: new TextStyle(fontSize: 18.0, color: Colors.green),
                ),
                new Text(
                  "${list[i].content}",
                  style: new TextStyle(fontSize: 18.0, color: Colors.blue),
                ),
              ],
            )),
        onTap: () {
          print("1111");
        },
      );
//      return ;
    },
  );
}

ListView custom

大家可能對前兩種比較熟悉,分別是傳入一個子元素列表或是傳入一個根據索引創建子元素的函數。
其實前兩種方式都是第三種方式的“快捷方式”。因爲 ListView 內部是靠這個 childrenDelegate 屬性動態初始化子元素的。
我們使用builderseparated比較多,這個custom相對來說就比較少了。但是我們是需要了解的。

  ///listView custom 構建
  Widget listViewLayoutCustom(list) {
//    return ListView.custom(childrenDelegate: new MyChildrenDelegate());
    return ListView.custom(
      itemExtent: 40.0,
      childrenDelegate: MyChildrenDelegate(
        (BuildContext context, int i) {
          return new Container(
              child: new Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              new Text(
                "${list[i].name}",
                style: new TextStyle(fontSize: 18.0, color: Colors.red),
              ),
              new Text(
                "${list[i].age}",
                style: new TextStyle(fontSize: 18.0, color: Colors.green),
              ),
              new Text(
                "${list[i].content}",
                style: new TextStyle(fontSize: 18.0, color: Colors.blue),
              ),
            ],
          ));
        },
        childCount: list.length,
      ),
      cacheExtent: 0.0,
    );
  }
}


childrenDelegate

自定義childrenDelegate 當然我們可以對ListView中的child進行自己需要的操作。最難定義的也就是這個而已。

// ignore: slash_for_doc_comments
/**
 * 繼承SliverChildBuilderDelegate  可以對列表的監聽
 */
class MyChildrenDelegate extends SliverChildBuilderDelegate {
  MyChildrenDelegate(
    Widget Function(BuildContext, int) builder, {
    int childCount,
    bool addAutomaticKeepAlive = true,
    bool addRepaintBoundaries = true,
  }) : super(builder,
            childCount: childCount,
            addAutomaticKeepAlives: addAutomaticKeepAlive,
            addRepaintBoundaries: addRepaintBoundaries);

  ///監聽 在可見的列表中 顯示的第一個位置和最後一個位置
  @override
  void didFinishLayout(int firstIndex, int lastIndex) {
    print('firstIndex: $firstIndex, lastIndex: $lastIndex');
  }

  ///可不重寫 重寫不能爲null  默認是true  添加進來的實例與之前的實例是否相同 相同返回true 反之false
  ///listView 暫時沒有看到應用場景 源碼中使用在 SliverFillViewport 中
  @override
  bool shouldRebuild(SliverChildBuilderDelegate oldDelegate) {
    // TODO: implement shouldRebuild
    print("oldDelegate$oldDelegate");
    return super.shouldRebuild(oldDelegate);
  }
}

文章示例代碼

ListViewDemo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章