Flutter ListView 下拉刷新,上拉加载更多

时间:2022-07-24
本文章向大家介绍Flutter ListView 下拉刷新,上拉加载更多,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

正常项目中使用ListView一定会涉及到分页加载的问题,此时无法避免地需要用到下拉刷新和上拉加载更多的功能。

本文就当前知识面对这两个知识点做简单的实际demo介绍。

本文“下拉刷新,上拉加载”效果图:

1、上拉加载更多

完整代码:

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

class RefreshListViewDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return RefreshListViewDemoState();
  }
}

class RefreshListViewDemoState extends State<RefreshListViewDemo> {
  static const loadingTag = "##loading##"; //表尾标记
  var _words = <String>[loadingTag];

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: AppBar(
          title: Text("ListView下拉刷新,上拉加载更多"),
        ),
        body: Stack(
          children: <Widget>[
            ListView.separated(
                itemBuilder: (context, index) {
                  if (_words[index] == loadingTag) {
                    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),
                itemCount: _words.length),
          ],
        ));
  }

  void _retrieveData() {
    Future.delayed(Duration(seconds: 2)).then((e) {
      _words.insertAll(
          _words.length - 1,
          //每次生成20个单词
          generateWordPairs().take(20).map((e) => e.asPascalCase).toList());
      setState(() {
        //重新构建列表
      });
    });
  }
}

void main() {
  runApp(new MaterialApp(
    title: "ListView下拉刷新,上拉加载更多",
    theme: ThemeData(primaryColor: Colors.deepOrangeAccent),
    home: RefreshListViewDemo(),
  ));
}

关键步骤分解:

  1. 先准备一个存放数据的String数组,设置一个结束标记到数组中。此标记始终在列表数据的末尾,是判断列表滑动是否到达尾部的标记。 static const loadingTag = "##loading##"; //表尾标记 var _words = <String>[loadingTag];
  2. 准备一个模拟网络请求下载数据的方法。 void _retrieveData() { Future.delayed(Duration(seconds: 2)).then((e) {//模拟2秒网络请求 _words.insertAll( //将数据插入到倒数第二个item的位置,因为最后一个是结束标记。 _words.length - 1, //每次生成20个单词 generateWordPairs().take(20).map((e) => e.asPascalCase).toList()); setState(() { //重新构建列表 }); }); }
  3. 根据条件展示上拉加载更多。 3.1. 当监测到最后一条数据,又满足在100条数据以下,显示loading动画布局,并去网络获取数据,获取到数据之后插入到结束标记之前。超过100条数据,显示没有更多了。 3.2. 如果不是最后一条数据,就正常展示该项的内容(随机英文单词)。 if (_words[index] == loadingTag) { if (_words.length - 1 < 100) { //获取数据 _retrieveData(); //加载时显示loading return Container(……); } else { //已经加载了100条数据,不再获取数据。 return Container( …… child: Text( "没有更多了", style: TextStyle(color: Colors.grey), )); } } //显示单词列表项 return ListTile(title: Text(_words[index]));

2、下拉刷新(包含上拉加载)

下拉刷新可以有很多种实现,这里只介绍如何使用原生下拉刷新控件。

完整代码:

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

class RefreshListViewDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return RefreshListViewDemoState();
  }
}

class RefreshListViewDemoState extends State<RefreshListViewDemo> {
  static const loadingTag = "##loading##"; //表尾标记
  var _words = <String>[loadingTag];
  bool showRefreshLoad = false; //控制何时展示下拉刷新loading

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: AppBar(
          title: Text("ListView下拉刷新,上拉加载更多"),
        ),
        body: RefreshIndicator(
          onRefresh: _toRefresh,
          child: ListView.separated(
            itemBuilder: (context, index) {
              if (_words[index] == loadingTag) {
                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),
            itemCount: _words.length,
          ),
        ));
  }

  void _retrieveData() {
    Future.delayed(Duration(seconds: 10)).then((e) {
      _words.insertAll(
          _words.length - 1,
          //每次生成20个单词
          generateWordPairs().take(15).map((e) => e.asPascalCase).toList());
      setState(() {
        //重新构建列表
      });
    });
  }

  Future _toRefresh() async {
    // 延迟3秒后添加新数据, 模拟网络加载
    await Future.delayed(Duration(seconds: 2), () {
      setState(() {
        _words.clear();
        _words.addAll(
            //每次生成20个单词
            generateWordPairs().take(15).map((e) => e.asPascalCase).toList());
        _words.add(loadingTag); //最后加上结束标记
      });
    });
  }
}

void main() {
  runApp(new MaterialApp(
    title: "ListView下拉刷新,上拉加载更多",
    theme: ThemeData(primaryColor: Colors.deepOrangeAccent),
    home: RefreshListViewDemo(),
  ));
}

关键步骤分解:

  1. 使用RefreshIndicator,包裹ListView。 body: RefreshIndicator( onRefresh: _toRefresh, child: ListView.separated(
  2. onRefresh里传入_toRefresh方法做网络请求(此处模拟网络)。

此处使用english_words库进行内容生成。

Future _toRefresh() async {
  // 延迟3秒后添加新数据, 模拟网络加载
  await Future.delayed(Duration(seconds: 2), () {
    setState(() {
      _words.clear();
      _words.addAll(
           //每次生成20个单词
          generateWordPairs().take(15).map((e) => e.asPascalCase).toList());
      _words.add(loadingTag); //最后加上结束标记
    });
  });
}

完成之后就实现了整个下拉刷新步骤。

效果图见本文开篇处。

注意:有些朋友在使用generateWordPairs()的时候可能会遇到找不到该方法的问题(我就遇到了)。事实上这是一个自动生成英文单词的第三方库。需要导入english_words库才能使用该方法。

具导入方法体在我Flutter系列文章中的《Flutter问题:import 'package:english_words/english_words.dart'失败》一文中有详细步骤。