Flutter 下拉刷新、上拉加载

Flutter 下拉刷新、上拉加载有很多第三方插件,本文使用插件为:pull_to_refresh

目前pull_to_refresh在pub.dev上的使用情况:
Flutter 下拉刷新、上拉加载_第1张图片

刷新header的类型:
ClassicHeader

  const ClassicHeader({
    Key? key,
    RefreshStyle refreshStyle: RefreshStyle.Follow,
    double height: 60.0,
    Duration completeDuration: const Duration(milliseconds: 600),
    this.outerBuilder,
    this.textStyle: const TextStyle(color: Colors.grey),
    this.releaseText,//松手加载
    this.refreshingText,//正在加载
    this.canTwoLevelIcon,
    this.twoLevelView,
    this.canTwoLevelText,
    this.completeText,//加载完成
    this.failedText,
    this.idleText,//下拉加载
    this.iconPos: IconPosition.left,
    this.spacing: 15.0,
    this.refreshingIcon,
    this.failedIcon: const Icon(Icons.error, color: Colors.grey),
    this.completeIcon: const Icon(Icons.done, color: Colors.grey),
    this.idleIcon = const Icon(Icons.arrow_downward, color: Colors.grey),
    this.releaseIcon = const Icon(Icons.refresh, color: Colors.grey),
  })

WaterDropHeader

  const WaterDropHeader({
    Key? key,
    this.refresh,
    this.complete,
    Duration completeDuration: const Duration(milliseconds: 600),
    this.failed,
    this.waterDropColor: Colors.grey,
    this.idleIcon: const Icon(
      Icons.autorenew,
      size: 15,
      color: Colors.white,
    ),   

MaterialClassicHeader
Flutter 下拉刷新、上拉加载_第2张图片

  const MaterialClassicHeader({
    Key? key,
    double height: 80.0,
    this.semanticsLabel,
    this.semanticsValue,
    this.color,
    double offset: 0,
    this.distance: 50.0,
    this.backgroundColor,
  }) 

WaterDropMaterialHeader
Flutter 下拉刷新、上拉加载_第3张图片

  const WaterDropMaterialHeader({
    Key? key,
    String? semanticsLabel,
    double distance: 60.0,
    double offset: 0,
    String? semanticsValue,
    Color color: Colors.white,
    Color? backgroundColor,
  })

BezierHeader

  BezierHeader(
      {this.child: const Text(""),
      this.onOffsetChange,
      this.onModeChange,
      this.readyRefresh,
      this.enableChildOverflow: false,
      this.endRefresh,
      this.onResetValue,
      this.dismissType: BezierDismissType.RectSpread,
      this.rectHeight: 70,
      this.bezierColor})

TwoLevelHeader

  const TwoLevelHeader(
      {Key? key,
      this.height: 80.0,
      this.decoration,
      this.displayAlignment: TwoLevelDisplayAlignment.fromBottom,
      this.completeDuration: const Duration(milliseconds: 600),
      this.textStyle: const TextStyle(color: const Color(0xff555555)),
      this.releaseText,
      this.refreshingText,
      this.canTwoLevelIcon,
      this.canTwoLevelText,
      this.completeText,
      this.failedText,
      this.idleText,
      this.iconPos: IconPosition.left,
      this.spacing: 15.0,
      this.refreshingIcon,
      this.failedIcon: const Icon(Icons.error, color: Colors.grey),
      this.completeIcon: const Icon(Icons.done, color: Colors.grey),
      this.idleIcon = const Icon(Icons.arrow_downward, color: Colors.grey),
      this.releaseIcon = const Icon(Icons.refresh, color: Colors.grey),
      this.twoLevelWidget});

CustomHeader
Flutter 下拉刷新、上拉加载_第4张图片

const CustomHeader({
    Key? key,
    required this.builder,  //HeaderBuilder(BuildContext context, RefreshStatus? mode);第二个参数刷新的状态,根据状态显示对应的刷新内容
    this.readyToRefresh, //准备刷新的回调
    this.endRefresh,//结束刷新的回调
    this.onOffsetChange,//下拉距离改变的回调
    this.onModeChange,//下拉状态改变的回调--RefreshStatus
    this.onResetValue,
    double height: 60.0,
    Duration completeDuration: const Duration(milliseconds: 600),
    RefreshStyle refreshStyle: RefreshStyle.Follow, //设置下拉的样式
    })

刷新fotter的类型:
ClassicFooter

  const ClassicFooter({
    Key? key,
    VoidCallback? onClick,
    LoadStyle loadStyle: LoadStyle.ShowAlways,
    double height: 60.0,
    this.outerBuilder,
    this.textStyle: const TextStyle(color: Colors.grey),
    this.loadingText,
    this.noDataText,
    this.noMoreIcon,
    this.idleText,
    this.failedText,
    this.canLoadingText,
    this.failedIcon: const Icon(Icons.error, color: Colors.grey),
    this.iconPos: IconPosition.left,
    this.spacing: 15.0,
    this.completeDuration: const Duration(milliseconds: 300),
    this.loadingIcon,
    this.canLoadingIcon: const Icon(Icons.autorenew, color: Colors.grey),
    this.idleIcon = const Icon(Icons.arrow_upward, color: Colors.grey),
  })

CustomFooter

  const CustomFooter({
    Key? key,
    double height: 60.0,
    this.onModeChange,
    this.onOffsetChange,
    this.readyLoading,
    this.endLoading,
    LoadStyle loadStyle: LoadStyle.ShowAlways,//下拉加载样式
    required this.builder,
    Function? onClick,
  })

页面刷新、加载通用模块可以进行封装,封装header继承ClassicHeader,封装fotter继承ClassicFooter

class RefreshHeader extends ClassicHeader {}

class LoadingFotter extends ClassicFooter {}

代码示例

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

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

  @override
  _RefreshPageState createState() => _RefreshPageState();
}

class _RefreshPageState extends State {
  // 定义原始数据
  List items = ["1", "2", "3", "4", "5", "6", "7", "8"];
  // 定义刷新控制器
  RefreshController _refreshController =
      RefreshController(initialRefresh: false);

  void _onRefresh() async {
    // 模拟网络请求,此处延时1秒
    await Future.delayed(Duration(milliseconds: 1000));

    // 刷新成功,数据恢复原样
    items = ["1", "2", "3", "4", "5", "6", "7", "8"];
    if (mounted) {
      setState(() {});
    }
    // 重置获取数据LoadStatus
    _refreshController.refreshCompleted(resetFooterState: true);
  }

  void _onLoading() async {
    // 模拟网络请求,此处延时1秒
    await Future.delayed(Duration(milliseconds: 1000));
    // 每次模拟加载数据,等到数据加载到20个为止,模拟数据都获取完成, 并设置LoadStatus
    if (items.length >= 20) {
      _refreshController.loadNoData();
      return;
    }
    //每次加载两个数据
    items.add((items.length + 1).toString());
    items.add((items.length + 1).toString());
    if (mounted) {
      setState(() {});
    }
    _refreshController.loadComplete();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Scaffold'),
      ),
      body: SmartRefresher(
        enablePullDown: true, // 下拉刷新
        enablePullUp: true, // 上拉加载数据
        // header: ClassicHeader(
        //   refreshStyle: RefreshStyle.UnFollow,
        //     refreshingText:'正在加载...',
        //   releaseText: '松手加载...',
        //   completeText: '加载完成',
        //   idleText: '下拉加载',
        // ),
        // header: ClassicHeader(),
        // header: WaterDropHeader(),
        // header: MaterialClassicHeader(),
        // header: WaterDropMaterialHeader(),
        // header: BezierHeader(),
        // header: TwoLevelHeader(),
        header: CustomHeader(
          refreshStyle: RefreshStyle.Behind,
          builder: (BuildContext context, RefreshStatus? mode) {
            Widget headerBody;
            if(mode == RefreshStatus.idle) {
              headerBody = Text('刷新');
            }else if(mode == RefreshStatus.refreshing) {
              headerBody = Text('刷新中...');
            }else if(mode == RefreshStatus.failed) {
              headerBody = Text('刷新失败');
            }else if(mode == RefreshStatus.completed) {
              headerBody = Text('刷新完成');
            }else if(mode == RefreshStatus.canRefresh) {
              headerBody = Text('松手刷新');
            }else {
              headerBody = Text("完成");
            }
            return Container(
              height: 55.0,
              child: Center(child: headerBody),
            );
          },
        ),
        // footer: ClassicFooter(),
        footer: CustomFooter(
          // 设置上拉、下拉时的提示内容
          builder: (context, mode) {
            Widget body;
            if (mode == LoadStatus.idle) {
              body = Text("上拉加载");
            } else if (mode == LoadStatus.loading) {
              // body = CupertinoActivityIndicator();
              body = Text("加载中");
            } else if (mode == LoadStatus.failed) {
              body = Text("加载失败");
            } else if (mode == LoadStatus.canLoading) {
              body = Text("松手加载");
            } else {
              body = Text("没有更多数据");
            }
            return Container(
              height: 55.0,
              child: Center(child: body),
            );
          },
        ),
        controller: _refreshController,
        onRefresh: _onRefresh,
        onLoading: _onLoading,
        child: ListView.builder(
          itemBuilder: (c, i) => Card(child: Center(child: Text(items[i]))),
          itemExtent: 100.0,
          itemCount: items.length,
        ),
      ),
    );
  }
}

特殊情况:

当当前数据不满一个屏幕时,此时展示上拉加载会先加载,然后无数据提示紧跟列表后面,如果示:
Flutter 下拉刷新、上拉加载_第5张图片
当数据不足一个屏幕时,不进行上拉加载数据的操作
结果图:

代码示例:

import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';

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

  @override
  _RefreshPageState createState() => _RefreshPageState();
}

class _RefreshPageState extends State {
  // 定义原始数据
  List items = [
    "1",
    "2",
  ];
  bool isLoad = true;
  // 定义刷新控制器
  RefreshController _refreshController =
      RefreshController(initialRefresh: false);

  @override
  void initState() {
    super.initState();
  }

  void _onRefresh() async {
    // 模拟网络请求,此处延时1秒
    await Future.delayed(Duration(milliseconds: 1000));

    // 刷新成功,数据恢复原样
    items = [
      "1",
      "2",
      "3",
      "4",
      "3",
      "4",
      "3",
      "4",
    ];
    if (mounted) {
      setState(() {});
    }
    // 重置获取数据LoadStatus
    _refreshController.refreshCompleted(resetFooterState: true);
  }

  void _onLoading() async {
    // 模拟网络请求,此处延时1秒
    await Future.delayed(Duration(milliseconds: 1000));
    // 每次模拟加载数据,等到数据加载到20个为止,模拟数据都获取完成, 并设置LoadStatus
    if (items.length >= 2) {
      _refreshController.loadNoData();
      return;
    }
    //每次加载两个数据
    items.add((items.length + 1).toString());
    if (mounted) {
      setState(() {});
    }
    _refreshController.loadComplete();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Scaffold'),
      ),
      body: RefreshConfiguration(
        footerTriggerDistance: 80,
        //   dragSpeedRatio: 0.91,
        hideFooterWhenNotFull: true,
        headerBuilder: () => ClassicHeader(),
        footerBuilder: () => ClassicFooter(),
        child: SmartRefresher(
          dragStartBehavior: DragStartBehavior.down,
          enablePullDown: true, // 下拉刷新
          enablePullUp: true, // 上拉加载数据
          controller: _refreshController,
          onRefresh: _onRefresh,
          onLoading: _onLoading,
          child: ListView.builder(
            itemBuilder: (c, i) => Card(
                child: Center(
                    child: Text(
              items[i],
              style: TextStyle(fontSize: 10.0),
            ))),
            itemExtent: 100.0,
            itemCount: items.length,
          ),
        ),
      ),
    );
  }
}

fotter、header的位置,可以根据需求添加位置和定制样式

RefreshConfiguration说明:

  RefreshConfiguration(
      {Key? key,
      required this.child,  //Widget
      this.headerBuilder, //下拉刷新header显示内容
      this.footerBuilder, //上拉加载footer显示内容
      this.dragSpeedRatio: 1.0,//拖拽速度
      this.shouldFooterFollowWhenNotFull, //当数据不足一个屏幕,遵循的状态
      this.enableScrollWhenTwoLevel: true, 两屏时用户是否可以拖动视口
      this.enableLoadingWhenNoData: false,//当footer处于nomore状态时,是否通过达到footerDistance来触发负载
      this.enableBallisticRefresh: false,//是否通过BallisticScrollActivity触发刷新
      this.springDescription: const SpringDescription(
        mass: 2.2,
        stiffness: 150,
        damping: 16,
      ), //自定义弹簧动画
      this.enableScrollWhenRefreshCompleted: false,//当刷新完成并返回时,用户是否可以拖动viewport
      this.enableLoadingWhenFailed: true, //失败时,footer是否可以通过达到footerDistance来触发负载
      this.twiceTriggerDistance: 150.0,//触发器twoLevel的超滚动距离
      this.closeTwoLevelDistance: 80.0, //关闭二层的底部穿越距离,前提:enableScrollWhenTwoLevel为true
      this.skipCanRefresh: false, //如果到达triggerDistance时需要立即刷新
      this.maxOverScrollExtent, //超出边缘时的最大顶部滚动距离 --header
      this.enableBallisticLoad: true,//是否通过BallisticScrollActivity触发加载
      this.maxUnderScrollExtent, //超出边缘时最大底部滚动距离-- fotter
      this.headerTriggerDistance: 80.0,//触发刷新的超滚距离
      this.footerTriggerDistance: 15.0, //触发加载后的距离
      this.hideFooterWhenNotFull: false, //当listView数据很小(不够一页)时,它应该被隐藏
      this.enableRefreshVibrate: false, // 刷新 开关
      this.enableLoadMoreVibrate: false,//加载  loadmore开关
      this.topHitBoundary,//边界位于上边缘,当惯性滚动超过边界距离时停止
      this.bottomHitBoundary,//边界位于底部边缘,当惯性在边界距离以下滚动时停止
    })

你可能感兴趣的:(Flutter,flutter,ios,android)