Flutter可滾動Widgets-ListView

前文

  1. 運行第一個Flutter App
  2. Flutter基礎Widgets-Text
  3. Flutter可滾動Widgets-ListView
  4. Flutter主題切換之flutter redux
  5. Flutter插件開發之APK自動安裝
  6. Flutter 開發一個 GitHub 客戶端OpenGit及學習總結

我的博客

ListView

先看下以下截圖 git

enter image description here
以上效果圖的代碼,是從 flutter官方demo flutter_gallery內copy的部分代碼。 首先,首先定義一個列表,代碼以下

List<String> items = <String>[
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
];
複製代碼

而後,經過上面的定義的列表數據,如今構建ListView的子Widget數據,代碼以下github

Iterable<Widget> listTiles = items
    .map<Widget>((String item) => buildListTile(context, item));

Widget buildListTile(BuildContext context, String item) {
    Widget secondary = const Text(
      'Even more additional list item information appears on line three.',
    );
    return ListTile(
      isThreeLine: true,
      leading: ExcludeSemantics(child: CircleAvatar(child: Text(item))),
      title: Text('This item represents $item.'),
      subtitle: secondary,
      trailing: Icon(Icons.info, color: Theme.of(context).disabledColor),
    );
}
複製代碼

最後,將生成的子Widget數據填充到ListView內,代碼以下redux

ListView(
    children: listTiles.toList(),
)
複製代碼

以上代碼,就能完成最上面截圖的效果。下面主要對ListTile作一下介紹app

ListTile

ListTileFlutter給咱們準備好的widget提供很是常見的構造和定義方式,包括文字,icon,點擊事件,通常是可以知足基本列表需求。async

構造函數

ListTile({
    Key key,
    this.leading,
    this.title,
    this.subtitle,
    this.trailing,
    this.isThreeLine = false,
    this.dense,
    this.contentPadding,
    this.enabled = true,
    this.onTap,
    this.onLongPress,
    this.selected = false,
 })
複製代碼

屬性

enter image description here

使用

ListTile(
    //展現三行
    isThreeLine: true,
    //前置圖標
    leading: ExcludeSemantics(child: CircleAvatar(child: Text(item))),
    //標題
    title: Text('This item represents $item.'),
    //副標題
    subtitle: secondary,
    //後置圖標
    trailing: Icon(Icons.info, color: Theme.of(context).disabledColor),
)
複製代碼

效果

enter image description here

ListView.builder

ListView.builder適合列表項比較多(或者無限)的狀況,由於只有當子Widget真正顯示的時候纔會被建立。 將上面列表填充的代碼修改成ListView.builder,代碼以下所示ide

ListView.builder(
    itemCount: items.length,
    itemBuilder: (BuildContext context, int index) {
        return buildListTile(context, items[index]);
})
複製代碼

運行結果以下圖所示 函數

enter image description here

ListView.separated

ListView.separated能夠生成列表項之間的分割器,它比ListView.builder多了一個separatorBuilder參數,該參數是一個分割器生成器。 將上面列表填充的代碼修改成ListView.separated,代碼以下所示post

ListView.separated(
    itemBuilder: (BuildContext context, int index) {
        return buildListTile(context, items[index]);
    },
    separatorBuilder: (BuildContext context, int index) {
        return index % 2 == 0 ? divider1 : divider2;
    },
    itemCount: items.length
)
複製代碼

運行結果以下圖所示 學習

enter image description here

實例:Listview下拉刷新 上拉加載更多

下面實現首次進入頁面,加載數據,下拉能刷新頁面數據,上拉能加載更多數據。ui

下拉刷新

下拉刷新,用到的是Flutter自帶的RefreshIndicatorWidget,ListView主要用ListView.builder進行實現。代碼以下所示

RefreshIndicator(
    key: refreshIndicatorKey,
    child: ListView.builder(
        itemCount: list.length,
        itemBuilder: (context, index) {
            return buildListTile(context, list[index]);
        },
    ),
    onRefresh: onRefresh)
複製代碼

實現下拉刷新,主要須要實現RefreshIndicatoronRefresh屬性,代碼以下所示

Future<Null> onRefresh() async {
    return Future.delayed(Duration(seconds: 2)).then((e) {
      list.addAll(items);
      setState(() {
        //從新構建列表
      });
    });
}
複製代碼

主要實現延遲2s加載數據,在從新刷新列表。 首次進入頁面,Loading狀態的實現實現以下面代碼所示

void showRefreshLoading() async {
    await Future.delayed(const Duration(seconds: 0), () {
      refreshIndicatorKey.currentState.show().then((e) {});
      return true;
    });
}
複製代碼

Loading完以後會觸發RefreshIndicatoronRefresh屬性,到此,下拉刷新已經實現完畢。 運行效果以下圖所示

enter image description here

上拉加載更多

上拉加載須要監聽ListView的滾動事件,當滾動事件與底部小於50而且有更多數據加載時,纔會觸發加載更多的邏輯,以下面代碼所示

scrollController.addListener(() {
    var position = scrollController.position;
    // 小於50px時,觸發上拉加載;
    if (position.maxScrollExtent - position.pixels < 50 &&
        !isNoMore) {
        loadMore();
    }
});

void loadMore() async {
    if (!isLoading) {
      //刷新加載狀態
      isLoading = true;
      setState(() {});

      Future.delayed(Duration(seconds: 2)).then((e) {
        list.addAll(items);
        //取消加載狀態,並提示暫無更多數據
        isLoading = false;
        isNoMore = true;
        setState(() {
          //從新構建列表
        });
      });
    }
}
複製代碼

視圖層的代碼,當須要處理加載更多的邏輯時,ListViewitemCount屬性須要進行加1,用來填充加載更多的視圖。以下面代碼所示

int getListItemCount() {
    int count = list.length;
    if (isLoading || isNoMore) {
      count += 1;
    }
    return count;
}
複製代碼

ListViewitemBuilder屬性,加載更多的視圖代碼以下所示

Widget builderMore() {
    return Center(
      child: Padding(
        padding: EdgeInsets.all(10.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            isNoMore
                ? Text("")
                : SizedBox(
                    width: 20.0,
                    height: 20.0,
                    child: CircularProgressIndicator(
                        strokeWidth: 4.0,
                        valueColor: AlwaysStoppedAnimation(Colors.black)),
                  ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 15.0),
              child: Text(
                isNoMore ? "沒有更多數據" : "加載中...",
                style: TextStyle(fontSize: 16.0),
              ),
            ),
          ],
        ),
      ),
    );
  }
複製代碼

RefreshIndicator代碼作以下修改

RefreshIndicator(
    key: refreshIndicatorKey,
    child: ListView.builder(
        controller: scrollController,
        itemCount: getListItemCount(),
        itemBuilder: (context, index) {
              return builderItem(context, index);
        },
    ),
    onRefresh: onRefresh)
          
Widget builderItem(BuildContext context, int index) {
    if (index < list.length) {
      return buildListTile(context, list[index]);
    }
    return builderMore();
}
複製代碼

運行代碼 加載中的效果以下圖所示

enter image description here
沒有更多數據的效果以下圖所示
enter image description here
相關文章
相關標籤/搜索