前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter-ListView组件下拉刷新+滚动底部加载+缓存封装

Flutter-ListView组件下拉刷新+滚动底部加载+缓存封装

作者头像
老猫-Leo
发布2023-12-11 20:50:17
3310
发布2023-12-11 20:50:17
举报
文章被收录于专栏:前端大全前端大全

Flutter的ListView组件,虽然很好用,但是数据量大的时候,在低配置的机器上会奇卡无比,所以我封装了一个ListView组件,对数据进行截断,并使用 keframe 插件进行流畅性优化,提升低配置设备的用户体验。

具体可直接查看代码示例,配有相关注释。

Sorry, your browser does not support the video tag.

大家也可以使用adb命令对设备进行录屏:adb shell screenrecord /sdcard/test.mp4,再转换为 gif 文件,推荐使用https://ezgif.com/video-to-gif。

组件代码

代码语言:javascript
复制
/*
 * @Author: hxb
 */
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';//后续去除
import 'package:keframe/size_cache_widget.dart';

/// 可缓存+下拉刷新+滚动到底部自动加载的ListView组件(返回数据定义复杂是方便局部刷新)
class CacheTableList extends StatefulWidget {
  List tableList;
  double height;
  ScrollController controller;
  String emptyMessage;
  String noMoreMessage;
  Function onRefresh; //需返回完整的待渲染tablelist,方便组件局部刷新。
  Function createList; //自定义的item建立事件
  Function getMoreData; //需返回完整的待渲染tablelist,返回null表示无更多数据,方便组件局部刷新。
  int pageCount;

  CacheTableList(
      {Key key,
      @required this.createList,
      this.tableList,
      this.onRefresh,
      this.controller,
      this.height,
      this.emptyMessage,
      this.noMoreMessage,
      this.pageCount,
      this.getMoreData})
      /// pageCount为静态数据懒加载,需配合tableList使用。getMoreData为动态加载数据,他与前者只能存在一种模式。
      : assert(getMoreData != null || pageCount != null),
        assert(pageCount == null || tableList != null),
        assert(pageCount != null || tableList == null), //tableList与pageCount同在
        super(key: key);
  @override
  _CacheTableListState createState() => _CacheTableListState();
}

class _CacheTableListState extends State<CacheTableList> {
  List orginalTableList = [];
  List _tableList = [];
  int _pageCount = 0;
  bool _hasMore = true;
  double _height;
  ScrollController _controller;

  @override
  void initState() {
    super.initState();
    orginalTableList = widget.tableList ?? [];
    _controller = widget.controller ?? new ScrollController();
    if (widget.getMoreData != null || widget.pageCount != 0) {
      _controller.addListener(() {
        if (_controller.position.pixels == _controller.position.maxScrollExtent) {
          _getMoreData();
        }
      });
    }
    _height = widget.height ?? 300; //double.infinity,
  }

  @override
  void dispose() {
    super.dispose();
    _controller.removeListener(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: _height,
      decoration: new BoxDecoration(color: Colors.white),
      child: SizeCacheWidget(
        estimateCount: _tableList.length,
        child: 
        widget.onRefresh != null
        ? RefreshIndicator(
            onRefresh: _onRefresh, //下拉刷新回调
            displacement: 10, //指示器显示时距顶部位置
            color: Colors.white, //指示器颜色,默认ThemeData.accentColor
            backgroundColor: Colors.blueAccent, //指示器背景颜色,默认ThemeData.canvasColor
            notificationPredicate: defaultScrollNotificationPredicate, //是否应处理滚动通知的检查(是否通知下拉刷新动作)
            child: Scrollbar(
              child: _createList(),
            ),
          )
        : Scrollbar(
            child: _createList(),
          ),
      ),
    );
  }

  /// 创建list
  Widget _createList() {
    if (_tableList.isEmpty && _pageCount == 0) {
      _getMoreData();
      return CupertinoActivityIndicator();
    }
    return _tableList.isEmpty
        ? Container(
            height: _height - 20,
            alignment: Alignment.topCenter,
            margin: EdgeInsets.only(top: 20),
            child: Text(
              widget.emptyMessage ?? '暂无数据',
              style: TextStyle(fontSize: 15, color: Colors.black38, fontWeight: FontWeight.bold),
            ),
          )
        : ListView.builder(
            controller: _controller,
            cacheExtent: 1000,
            itemCount: _tableList.length,
            itemBuilder: (_, i) {
              if (i == _tableList.length - 1 && _hasMore) {
                return Padding(
                  padding: EdgeInsets.all(10.0),
                  child: Center(
                    child: CupertinoActivityIndicator(),
                  ),
                );
              }
              return widget.createList(i);//自定义创建item事件
            },
          );
  }

  /// 加载更多数据
  void _getMoreData() async {
    if (!_hasMore) {
      EasyLoading.showToast(widget.noMoreMessage ?? '已经到底了');
      // ScaffoldMessenger.of(context).showSnackBar(
      //   SnackBar(
      //     content: Text(widget.noMoreMessage ?? '已经到底了'),
      //   ),
      // );
      return;
    }
    if (widget.getMoreData != null) {
      _pageCount = -1; //使用自定义方法加载数据,除第一次加载数据后则无需pageCount。
      var tempData = await widget.getMoreData();
      if (tempData == null) {
        _hasMore = false;
      } else {
        _tableList = tempData;
      }
      setState(() {});
      return;
    }
    int nowLength = orginalTableList.length;
    if (_pageCount <= nowLength) {
      _pageCount += widget.pageCount;
      Future.delayed(Duration(microseconds: 500)).then((e) {
        setState(() {
          if (_pageCount <= nowLength) {
            _tableList = orginalTableList.sublist(0, _pageCount);
          } else {
            _tableList = orginalTableList;
            _hasMore = false;
          }
        });
      });
    }
  }

  /// 刷新时候的事件,需返回Future。
  Future _onRefresh() {
    return Future.sync(() async {
      _tableList = await widget.onRefresh();
      _pageCount = 0;
      _hasMore = true;
      setState(() {});
    });
  }
}

使用示例

静态加载数据模式-简单示例
代码语言:javascript
复制
CacheTableList(
  tableList: ["列表内容1","列表内容2","列表内容3","列表内容4","列表内容5","列表内容6"],
  pageCount: 3,//上面6笔静态数据,会以每三笔的方式加载。注意高度设定需符合要求
  createList: (index) {
    return FrameSeparateWidget(//使用keframe进行单帧动画优化,提升低配置设备流畅性。
      index: index,
      placeHolder: Container(//占位组件,尽量简单。
        height: 36,
        width: double.infinity,
        margin: EdgeInsets.only(bottom: 5, top: 5),
        child: Text("......"),
        alignment: Alignment.center,
      ),
      child: ListTile(title: Text("列表第$index项"))
    );
  },
  onRefresh: () {
    //下拉事件->重新渲染列表
    return Future.delayed(Duration(milliseconds: 1000), () {
      return ["列表内容1","列表内容2","列表内容3","列表内容4","列表内容5","列表内容6"];
    });
  }
);
动态加载数据模式-简单示例
代码语言:javascript
复制
// _tableList = ["列表内容1","列表内容2","列表内容3","列表内容4","列表内容5","列表内容6"];
CacheTableList(
  createList: (index) {
    return FrameSeparateWidget(//使用 keframe 进行单帧动画优化,提升低配置设备流畅性。
      index: index,
      placeHolder: Container(//占位组件,尽量简单。
        height: 36,
        width: double.infinity,
        margin: EdgeInsets.only(bottom: 5, top: 5),
        child: Text("......"),
        alignment: Alignment.center,
      ),
      child: ListTile(title: Text(_tableList[index]))
    );
  },
  getMoreData: () {
    _tableList.addAll(_tableList);
    return Future.delayed(Duration(milliseconds: 1000), () {
      return _tableList.length > 100 ? null : _tableList; //为null表示数据到极限不再加载
    });
  }
  /// onRefresh不配置时则不会有下拉事件
  //  onRefresh: () {
  //    //下拉事件->重新渲染列表
  //    return Future.delayed(Duration(milliseconds: 1000), () {
  //      return ["列表内容1","列表内容2","列表内容3","列表内容4","列表内容5","列表内容6"];
  //    });
  //  }
);
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-07-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 组件代码
  • 使用示例
    • 静态加载数据模式-简单示例
      • 动态加载数据模式-简单示例
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档