flutter 上拉加载更多下拉刷新通用widget,child可以是任何widget,支持自定义多种状态显示


更新:

XBRefresh

安装

flutter pub add xb_refresh

使用

// ignore_for_file: library_private_types_in_public_api

import 'package:xb_scaffold/xb_scaffold.dart';
import 'xb_refresh.dart';

class XBRefreshDemo extends XBPage<XBRefreshDemoVM> {
  const XBRefreshDemo({super.key});

  @override
  generateVM(BuildContext context) {
    return XBRefreshDemoVM(context: context);
  }

  @override
  bool needShowContentFromScreenTop(XBRefreshDemoVM vm) {
    return true;
  }

  @override
  List<Widget>? actions(XBRefreshDemoVM vm) {
    return [
      XBButton(
          onTap: () {
            vm.xbRefreshController.refresh();
          },
          child: Container(
            color: Colors.transparent,
            child: const Padding(
              padding: EdgeInsets.all(8.0),
              child: Text("开始刷新"),
            ),
          )),
    ];
  }

  @override
  Widget buildPage(vm, BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(top: topBarH),
      child: Container(
        height: screenH * 0.8,
        width: screenW * 0.8,
        color: colors.randColor,
        child: XBRefresh(
          controller: vm.xbRefreshController,
          needLoadMore: true,
          needRefresh: true,
          initRefresh: true,

          ///开始加载更多的回调
          onLoadMore: () {
            Future.delayed(const Duration(seconds: 2), () {
              bool hasMore = false;
              if (vm.itemCount < 20) {
                hasMore = true;
                vm.itemCount += 2;
                vm.notify();
              }

              ///结束加载更多,传是否有新数据
              vm.xbRefreshController.endLoadMore(hasMore);
            });
          },
          onRefresh: () {
            Future.delayed(const Duration(seconds: 1), () {
              vm.itemCount = 10;
              vm.xbRefreshController.endRefresh();
              vm.notify();
            });
          },
          headerCompleteBuilder: (height) {
            return Container(
              height: height,
              color: Colors.red,
              child: const Center(
                child: Text("完成刷新"),
              ),
            );
          },
          footerHasMoreBuilder: (height) {
            return Container(
              height: height,
              color: Colors.green,
              child: const Center(
                child: Text("拉取新数据完成"),
              ),
            );
          },
          child: ListView.builder(
            itemCount: vm.itemCount,
            itemBuilder: (context, index) {
              return Cell("$index", () {});
            },
          ),
        ),
      ),
    );
  }
}

class XBRefreshDemoVM extends XBPageVM<XBRefreshDemo> {
  XBRefreshDemoVM({required super.context}) {
    controller.addListener(listenFun);
  }
  final ScrollController controller = ScrollController();
  final XBRefreshController xbRefreshController = XBRefreshController();

  int itemCount = 10;

  void listenFun() {
    xbError("controller.offset:${controller.offset}");
  }

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

class Cell extends StatelessWidget {
  static const height = 44.0;
  final String title;
  final VoidCallback onPressed;

  const Cell(this.title, this.onPressed, {super.key});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onPressed,
      child: Container(
        height: height,
        color: Colors.black38,
        alignment: Alignment.center,
        child: Column(
          children: <Widget>[
            Expanded(
                child: Center(
                    child: Text(title,
                        style: const TextStyle(color: Colors.white)))),
            Container(
              height: 1,
              color: Colors.white,
            )
          ],
        ),
      ),
    );
  }
}


原文

1,通用性,child可以是任何widget

2,支持多种状态:

上拉加载更多:继续上拉加载更多、松手开始加载、正在加载、加载到了新数据、没有新数据
下拉刷新:继续下拉刷新、松手开始刷新、正在刷新、刷新完成

3,支持自定义每种状态的widget


源码:

XBRefresh

效果:

XBRefreshGif.gif


思路:

下拉和上拉类似的,这里就说上拉。
借住Stack,底层是Column,上层是外部传入的child。


image.png

流程图(仅上拉):

上拉加载更多流程图.jpg


demo:

下载源码后,跳转到下图指向的page查看效果。


XBRefreshDemoJpg.png
class _XBRefreshDemoState extends State<XBRefreshDemo> {
  ScrollController _controller = ScrollController();
  GlobalKey<XBRefreshState> _refreshKey = GlobalKey();

  int _itemCount = 10;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _controller.addListener(() {
      _refreshKey.currentState.receiveOffset(
          _controller.offset, _controller.position.maxScrollExtent);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("xb refresh demo"),
        ),
        body: XBRefresh(
            key: _refreshKey,
            needLoadMore: true,
            needRefresh: true,

            ///开始加载更多的回调
            onBeginLoadMore: () {
              Future.delayed(Duration(seconds: 2), () {
                bool hasMore = false;
                if (_itemCount < 20) {
                  hasMore = true;
                  setState(() {
                    _itemCount += 5;
                  });
                }

                ///结束加载更多,传是否有新数据
                _refreshKey.currentState.endLoadMore(hasMore);
              });
            },
            onBeginRefresh: () {
              Future.delayed(Duration(seconds: 1), () {
                setState(() {
                  _itemCount = 10;
                });
                _refreshKey.currentState.endRefresh();
              });
            },
            child: CustomScrollView(
              controller: _controller,
              physics: AlwaysScrollableScrollPhysics(
                  parent: BouncingScrollPhysics()),
              slivers: <Widget>[
                SliverList(
                    delegate: SliverChildBuilderDelegate((ctx, index) {
                  return Cell("$index", () {});
                }, childCount: _itemCount))
              ],
            )));
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容