Flutter listview如何实现下拉刷新上拉加载更多功能

 更新时间:2021年8月5日 16:00  点击:1466

下拉刷新

在Flutter中系统已经为我们提供了google material design的刷新功能 , 样式与原生Android一样.

我们可以使用RefreshIndicator组件来实现Flutter中的下拉刷新,下面们还是先来看下如何使用吧

RefreshIndicator

构造方法:

 const RefreshIndicator({
    Key key,
    @required this.child,
    this.displacement: 40.0,      //触发下拉刷新的距离
    @required this.onRefresh,     //下拉回调方法
    this.color,                   //进度指示器前景色 默认为系统主题色
    this.backgroundColor,         //背景色
    this.notificationPredicate: defaultScrollNotificationPredicate,
  })

然后我们看一下效果以及实现方式:

然后我们看一下代码:

class _MyHomePageState extends State<MyHomePage> {
  List list = new List(); //列表要展示的数据
 

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    getData();
  }

  /**
   * 初始化list数据 加延时模仿网络请求
   */
  Future getData() async {
    await Future.delayed(Duration(seconds: 2), () {
      setState(() {
        list = List.generate(15, (i) => '哈喽,我是原始数据 $i');
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: new Text(widget.title),
      ),
      body: RefreshIndicator(
        onRefresh: _onRefresh,
        child: ListView.builder(
          itemBuilder: _renderRow,
          itemCount: list.length,
        ),
      ),
    );
  }

  Widget _renderRow(BuildContext context, int index) {
    return ListTile(
      title: Text(list[index]),
    );
  }

  /**
   * 下拉刷新方法,为list重新赋值
   */
  Future<Null> _onRefresh() async {
    await Future.delayed(Duration(seconds: 3), () {
      print('refresh');
      setState(() {
        list = List.generate(20, (i) => '哈喽,我是新刷新的 $i');
      });
    });
  }
}

代码不复杂,我们一步步分析:

MyHomePage 只是返回一个State,这里省略了.

首先body里我们返回了一个RefreshIndicator,这个组件自带下拉回调,然后里面我们包裹了一个listview,

然后使用List.generate()方法来创建了一个长度为15的List,并把List里的值赋值给ListView Item中的ListTile。

下拉回调onRefresh 我们返回了一个改变list的方法 .

在上面的代码中我们使用_onRefresh()方法来处理下拉刷新的回调

/**
   * 下拉刷新方法,为list重新赋值
   */
  Future<Null> _onRefresh() async {
    await Future.delayed(Duration(seconds: 3), () {
      print('refresh');
      setState(() {
        list = List.generate(20, (i) => '哈喽,我是新刷新的 $i');
      });
    });
  }

其中 Future.delayed()方法可以选择延迟处理任务,这里我们假设网络的延迟是3秒.

这样一个简单的下拉刷新就实现了.

上拉加载更多

对于加载更多的组件在Flutter中是没有提供的,所以在这里我们就需要考虑如何实现的。

在ListView中有一个ScrollController属性,它就是专门来控制ListView滑动事件,在这里我们可以根据ListView的位置来判断是否滑动到了底部来做加载更多的处理。

在这里我们可以使用如下代码来判断ListView 是否滑动到了底部

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    getData();
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        print('滑动到了最底部');
        _getMore();
      }
    });
  }

_scrollController是我们初始化的ScrollController对象,通过监听我们可以判断现在的位置是否是最大的下滑位置来判断是否下滑到了底部。

看一下代码和效果:

class _MyHomePageState extends State<MyHomePage> {
  List list = new List(); //列表要展示的数据
  ScrollController _scrollController = ScrollController(); //listview的控制器
  int _page = 1; //加载的页数
  bool isLoading = false; //是否正在加载数据

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    getData();
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        print('滑动到了最底部');
        _getMore();
      }
    });
  }

  /**
   * 初始化list数据 加延时模仿网络请求
   */
  Future getData() async {
    await Future.delayed(Duration(seconds: 2), () {
      setState(() {
        list = List.generate(15, (i) => '哈喽,我是原始数据 $i');
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: new Text(widget.title),
      ),
      body: RefreshIndicator(
        onRefresh: _onRefresh,
        child: ListView.builder(
          itemBuilder: _renderRow,
          itemCount: list.length,
          controller: _scrollController,
        ),
      ),
      // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  Widget _renderRow(BuildContext context, int index) {
    return ListTile(
      title: Text(list[index]),
    );
  }

  /**
   * 下拉刷新方法,为list重新赋值
   */
  Future<Null> _onRefresh() async {
    await Future.delayed(Duration(seconds: 3), () {
      print('refresh');
      setState(() {
        list = List.generate(20, (i) => '哈喽,我是新刷新的 $i');
      });
    });
  }

  /**
   * 上拉加载更多
   */
  Future _getMore() async {
    if (!isLoading) {
      setState(() {
        isLoading = true;
      });
      await Future.delayed(Duration(seconds: 1), () {
        print('加载更多');
        setState(() {
          list.addAll(List.generate(5, (i) => '第$_page次上拉来的数据'));
          _page++;
          isLoading = false;
        });
      });
    }
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _scrollController.dispose();
  }
}

滑动到底部的时候,我们执行加载更多的方法,给list数据多加5条,这次我们把延迟改到了1秒:

/**
   * 上拉加载更多
   */
  Future _getMore() async {
    if (!isLoading) {
      setState(() {
        isLoading = true;
      });
      await Future.delayed(Duration(seconds: 1), () {
        print('加载更多');
        setState(() {
          list.addAll(List.generate(5, (i) => '第$_page次上拉来的数据'));
          _page++;
          isLoading = false;
        });
      });
    }
  }

是的,看着上面的效果我们已经实现了下拉加载更多,但是因为我们是滑动到底部触发的,如果在正在请求的过程中多次下拉就会造成多次加载更多的情况,所以我们还得对这个做下处理为了避免多次触发,我们加了一个isLoading,在上拉方法执行的过程中不会再次执行.

可以看到,我们仅仅在上面代码的基础上加上了一个isLoading的变量,当这个变量的值为true时,就不会触发加载更多的操作。

而因为是网络请求,可能需要分页,所以我们加了个page参数来查看是第几次触发上拉加载.

因为我们加了个监听,在组件卸载掉的时候记得移除这个监听,所以:

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _scrollController.dispose();
  }

这个一定不要忘记,养成好习惯,每次加了监听都跑到这个方法里移除掉.

这样,我们一个简单的上拉加载更多的功能就实现了.

但是还有个问题,没有用户交互啊,加载的时候要有个提示,于是我们尝试上拉的时候展示一个加载中的组件给用户:

首先我们创建加载更多时显示的Vidget

/**
   * 加载更多时显示的组件,给用户提示
   */
  Widget _getMoreWidget() {
    return Center(
      child: Padding(
        padding: EdgeInsets.all(10.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Text(
              '加载中...     ',
              style: TextStyle(fontSize: 16.0),
            ),
            CircularProgressIndicator(strokeWidth: 1.0,)
          ],
        ),
      ),
    );
  }

然后我们在listview的itemcount那里把count+1,相当于我们给listview加了个尾部的组件.

 body: RefreshIndicator(
        onRefresh: _onRefresh,
        child: ListView.builder(
          itemBuilder: _renderRow,
          itemCount: list.length + 1,   //这里!这里!这里!
          controller: _scrollController,
        ),

看一下效果是否满意:

嗯,基本符合要求,感觉那个刷新图标加的有点丑,画蛇添足了,不过功能都是ok了的.

当然, 大家可以根据自己的需要去自己实现想要的样式

看一下全部的代码:

/*
 * Created by 李卓原 on 2018/9/13.
 * email: zhuoyuan93@gmail.com
 *
 */
 
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List list = new List(); //列表要展示的数据
  ScrollController _scrollController = ScrollController(); //listview的控制器
  int _page = 1; //加载的页数
  bool isLoading = false; //是否正在加载数据

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    getData();
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        print('滑动到了最底部');
        _getMore();
      }
    });
  }

  /**
   * 初始化list数据 加延时模仿网络请求
   */
  Future getData() async {
    await Future.delayed(Duration(seconds: 2), () {
      setState(() {
        list = List.generate(15, (i) => '哈喽,我是原始数据 $i');
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: new Text(widget.title),
      ),
      body: RefreshIndicator(
        onRefresh: _onRefresh,
        child: ListView.builder(
          itemBuilder: _renderRow,
          itemCount: list.length + 1,
          controller: _scrollController,
        ),
      ),
      // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  Widget _renderRow(BuildContext context, int index) {
    if (index < list.length) {
      return ListTile(
        title: Text(list[index]),
      );
    }
    return _getMoreWidget();
  }

  /**
   * 下拉刷新方法,为list重新赋值
   */
  Future<Null> _onRefresh() async {
    await Future.delayed(Duration(seconds: 3), () {
      print('refresh');
      setState(() {
        list = List.generate(20, (i) => '哈喽,我是新刷新的 $i');
      });
    });
  }

  /**
   * 上拉加载更多
   */
  Future _getMore() async {
    if (!isLoading) {
      setState(() {
        isLoading = true;
      });
      await Future.delayed(Duration(seconds: 1), () {
        print('加载更多');
        setState(() {
          list.addAll(List.generate(5, (i) => '第$_page次上拉来的数据'));
          _page++;
          isLoading = false;
        });
      });
    }
  }

  /**
   * 加载更多时显示的组件,给用户提示
   */
  Widget _getMoreWidget() {
    return Center(
      child: Padding(
        padding: EdgeInsets.all(10.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Text(
              '加载中...',
              style: TextStyle(fontSize: 16.0),
            ),
            CircularProgressIndicator(
              strokeWidth: 1.0,
            )
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _scrollController.dispose();
  }
}

总结:

  • RefreshIndicator可以显示下拉刷新
  • 使用ScrollController可以监听滑动事件,判断当前view所处的位置
  • 可以根据item所处的位置来处理加载更多显示效果

到此这篇关于Flutter listview如何实现下拉刷新上拉加载更多功能的文章就介绍到这了,更多相关Flutter listview下拉刷新上拉加载更多内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • C#实现带进度条的ListView

    这篇文章主要介绍了C#实现带进度条的ListView 的相关资料,需要的朋友可以参考下...2020-06-25
  • C#获取鼠标在listview右键点击单元格的内容方法

    下面小编就为大家带来一篇C#获取鼠标在listview右键点击单元格的内容方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • 纯javascript实现简单下拉刷新功能

    代码很简单,实现的功能却很实用,直接奉上代码CSS:复制代码 代码如下: <meta charset="utf-8" /> <title>Pull to Refresh</title> <meta name="viewport" content="width=device-width,height=device-height,inital-scal...2015-03-15
  • C#实现读取DataSet数据并显示在ListView控件中的方法

    这篇文章主要介绍了C#实现读取DataSet数据并显示在ListView控件中的方法,涉及C#操作DataSet及ListView控件的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C#中WPF ListView绑定数据的实例详解

    这篇文章主要介绍了C#中WPF ListView绑定数据的实例详解的相关资料,希望通过本文能帮助到大家,让大家理解掌握这部分内容,需要的朋友可以参考下...2020-06-25
  • Ionic如何实现下拉刷新与上拉加载功能

    在日常项目开发中我们经常见到下拉刷新上拉加载的功能,接下来通过本文给大家介绍ionic如何实现下拉刷新与上拉加载的相关资料,需要的朋友可以参考下...2016-06-12
  • C# WPF ListView控件的实例详解

    这篇文章主要介绍了C# WPF ListView控件的实例详解的相关资料,希望通过本能帮助到大家,让大家掌握这部分内容,需要的朋友可以参考下...2020-06-25
  • C#中将ListView中数据导出到Excel的实例方法

    首先 你需要添加引用Microsoft Excel 11.0 Object Library...2020-06-25
  • C# listview添加combobox到单元格的实现代码

    从别处转来的,自己进行了一些小的修改,还不错,你自己先拖一个ListView1和一个ComboBox1,需要的朋友可以参考下...2020-06-25
  • Flutter悬浮按钮FloatingActionButton使用详解

    本文主要介绍了Flutter悬浮按钮FloatingActionButton使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-07-12
  • Flutter 开发一个登录页面

    登录页面在 App 开发中非常常见,本篇借登录页面的开发介绍了文本框 TextField组件的使用,同时使用文本框的装饰属性实现了个性化文本框设置。...2021-06-05
  • Flutter 底部弹窗ModelBottomSheet的使用示例

    在实际开发过程中,经常会用到底部弹窗来进行快捷操作,例如选择一个选项,选择下一步操作等等。在 Flutter 中提供了一个 showModelBottomSheet 方法用于弹出底部弹窗,本篇介绍如何使用底部弹窗。...2021-06-07
  • 安卓开发使用定制的ArrayAdapter制作ListView的Items

    下面我们来看一篇关于使用定制的ArrayAdapter制作ListView的Items的例子,希望这个例子能够给各位同学带来帮助的哦。 背景介绍   对于现实世界中的商业移动应用...2016-09-20
  • Flutter 如何封装文本输入框组件

    在实际开发过程中,往往开始是完成功能层面的开发,然而再考虑组件封装和代码优化。当然,组件封装越早做越好,因为这样会提高整个团队开发的规范性和效率。本文将介绍如何封装一个文本输入框组件...2021-06-05
  • ListView移动到指定位置的例子

    下面我们来看一篇关于ListView移动到指定位置的例子吧,希望文章能够给各位朋友带来帮助,具体的如下介绍. 关于ListView移动到指定位置有两种方法 listview.setSel...2016-11-01
  • 安卓开发之自定义下拉刷新头部

    下拉刷新在手机中我们常了,几乎所有的app都具备了一个下拉刷新页面的功能了,那么我们一起来看安卓中下拉刷新实现方法。 一直用的下拉刷新库就是android-Ultra-Pull...2016-09-20
  • Flutter项目在 iOS14 启动崩溃的解决方法

    这篇文章主要介绍了Flutter项目在 iOS14 启动崩溃的解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-09-24
  • C# ListView双击Item事件

    ListView没有Item的双击事件,只能通过变通的方法得到,以下是我的变通方法...2020-06-25
  • 关于ListView下隐藏控件的解决方案分享

    想实现一个功能,这个功能挺简单,就是当不是管理员用户登入的时候,那这个 删除 按钮就被隐藏掉;当是管理员用户的时候,就重新显示出来...2021-09-22
  • flutter实现点击事件

    这篇文章主要介绍了flutter实现点击事件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-26