前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter 中的下拉刷新和上拉加载

Flutter 中的下拉刷新和上拉加载

作者头像
拉维
发布2019-09-05 18:16:17
4K0
发布2019-09-05 18:16:17
举报
文章被收录于专栏:iOS小生活iOS小生活

在Flutter的官方SDK中给我们提供了下拉刷新的组件RefreshIndicator,但是没有提供上拉分页加载更多的组件。不过不用担心,在Flutter的ListView组件中,有一个ScrollController属性,它就是专门用来控制ListView滑动事件,在这里我们可以根据ListView的位置来判断是否滑动到了底部来做加载更多的处理。

当然,我们是可以找一些第三方的库来实现上拉加载下拉刷新的效果的,比如flutter_easyrefresh这个第三方组件,但是我并不推荐flutter_easyrefresh,因为它有一些小Bug。

所以这篇文章,我们就聊一下,如何自己去实现上拉加载下拉刷新的效果

代码如下:

代码语言:javascript
复制
import 'dart:convert';

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

class RefreshPage extends StatefulWidget {
  RefreshPage({Key key}) : super(key: key);

  _RefreshPageState createState() => _RefreshPageState();
}

class _RefreshPageState extends State<RefreshPage> {
  List _dataSources = List();
  ScrollController _scrollController = ScrollController();
  int _page = 1; //请求第几页数据,用于分页请求数据
  bool _haveMore = true; //是否还有更多的数据可以请求

  //网络请求数据
  _requestData() async {
    String urlStr =
        "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=$_page";
    var response = await Dio().get(urlStr);
    if (response.statusCode == 200) {
      setState(() {
        List resultList = jsonDecode(response.data)["result"];
        if (this._page == 1) {
          //第一次加载或者下拉加载
          this._dataSources = resultList;
        } else {
          //上拉刷新(将新加载的数据拼接到原来的数据数组中)
          this._dataSources.addAll(resultList);
        }
        this._page++; //每请求成功一次,page都要加1
        /**
         * 这里根据当前返回的数组长度是否小于pagesize来判断接下来是否还有更多数据
         * 这里的pagesize是20
         */
        if (resultList.length < 20) {
          this._haveMore = false;
        }
      });
      // print(this._dataSources);
    } else {
      print("Request failed with status: ${response.statusCode}.");
    }
  }

  //下拉刷新
  /**
   * 注意,这里只是给大家演示一下下拉刷新组件,所以下拉刷新的逻辑写的比较简单
   * 如果真的在项目中使用的话,大家还是思考全面,不要简单拷贝如下代码!
   */
  Future<void> _refreshData() async {
    await Future.delayed(Duration(seconds: 2), () {
      print("请求数据完成");
      this._page = 1;
      _requestData();
    });
  }

  @override
  void initState() {
    super.initState();
    //页面一加载就执行网络请求
    this._requestData();

    //监听滚动条的滚动事件
    _scrollController.addListener(() {
      print(_scrollController.position.pixels); //滚动的距离
      print(_scrollController.position.maxScrollExtent); //最大滚动范围
      //当滚动到最底部的时候,加载新的数据
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        //当还有更多数据的时候才会进行加载新数据
        if (this._haveMore) {
          this._requestData();
        }
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("refreshDemo")),
      body: _dataSources.length == 0
          ? _loadMoreWidget()
          : RefreshIndicator(
              child: ListView.builder(
                controller: _scrollController,
                itemCount: this._dataSources.length,
                itemBuilder: (context, index) {
                  /**
                   * 当当前index等于数据源数据的长度减1的时候,
                   * 说明当前的ListTile是最后一个ListTile,
                   * 此时需要上拉加载新的数据,因此要在最底部显示一个加载中的圈圈
                   */
                  if (index == this._dataSources.length - 1) {
                    return Column(
                      children: <Widget>[
                        ListTile(
                            title: Text(this._dataSources[index]["title"],
                                maxLines: 1)),
                        Divider(),
                        _loadMoreWidget()
                      ],
                    );
                  } else {
                    return Column(
                      children: <Widget>[
                        ListTile(
                            title: Text(this._dataSources[index]["title"],
                                maxLines: 1)),
                        Divider()
                      ],
                    );
                  }
                },
              ),
              onRefresh: _refreshData,
            ),
    );
  }

  //加载中的圈圈
  Widget _loadMoreWidget() {
    if (this._haveMore) {
      //还有更多数据可以加载
      return Center(
        child: Padding(
          padding: EdgeInsets.all(10),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Text("加载中......"),
              CircularProgressIndicator(
                strokeWidth: 1,
              )
            ],
          ),
        ),
      );
    } else {
      //当没有更多数据可以加载的时候,
      return Center(
        child: Text("我是有底线的"),
      );
    }
  }
}

效果如下:

注:代码中已经将相关注意点做了详细说明,大家可以将该代码复制到你的工程中去直接运行,接口都是调通的。

关于我写的这个Demo,我有一点不明白,当页面滑到最底部的时候,_scrollController.position.pixels等于_scrollController.position.maxScrollExtent,此时会加载新的数据。

但是ListView是有弹簧效果的,当你把页面滚动到最底部之后,它不会立马停住,而是继续往下弹一下再返回来,也就是说,pixels会在等于maxScrollExtent之后继续往上涨(大于maxScrollExtent),然后再返回等于maxScrollExtent。

这样就会有两次_scrollController.position.pixels等于_scrollController.position.maxScrollExtent被监听到,按道理会进行两次网络请求,但是实际运行之后,我发现并没有出现我所担心的问题,我就很不解了。

各位如果有什么想法可以给我留言沟通,感谢大家。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iOS小生活 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档