持续创作,加速生长!这是我参加「日新方案 6 月更文挑战」的第30天,点击查看活动详情

  • 本文主要介绍三方组件中改写组件,下拉改写,上拉加载更多。

1. 改写组件

关于改写组件一些通过时间的检测,根本上都处理了问题,相似咱们MJRefresh,运用的人比较多
比如:pull_to_refresh: ^2.0.0

Flutter学习之刷新组件-pull_to_refresh
或许 flutter_easyrefresh: ^2.2.1

Flutter学习之刷新组件-pull_to_refresh

根本上都是能够处理咱们的需求,自定义上拉下拉动画,文字描述等。这儿我运用的是pull_to_refresh,这儿作者适配了flutter 3.0 适配 咱们依靠的时分增加git地址

pull_to_refresh:
  git:
    url: https://github.com/miquelbeltran/flutter_pulltorefresh

2. pull_to_refresh

这儿介绍下pull_to_refresh的运用。组件的特性如下:

  • 供给上拉加载和下拉改写
  • 几乎合适一切部件
  • 供给大局设置默许指示器和特点
  • 供给多种比较常用的指示器
  • 支撑Android和iOS默许滑动引擎,可限制越界距离,打造自定义弹性动画,速度,阻尼等。
  • 支撑水平和笔直改写,同时支撑翻转列表(四个方向)
  • 供给多种改写指示器风格:跟随,不跟随,坐落背部,坐落前部, 供给多种加载更多风格
  • 供给二楼改写,可完成相似淘宝二楼,微信二楼,携程二楼
  • 答应相关指示器存放在Viewport外部,即朋友圈改写效果

这儿咱们看下简略的运用,这儿运用默许的上拉和下拉Widget


class RefreshPage extends StatefulWidget {
  const RefreshPage({Key? key}) : super(key: key);
  @override
  State<RefreshPage> createState() => _RefreshPageState();
}
class _RefreshPageState extends State<RefreshPage> {
  List<String> items = ["1", "2", "3", "4", "5", "6", "7", "8"];
  final RefreshController _refreshController =
  RefreshController(initialRefresh: false);
  void _onRefresh() async{
    // monitor network fetch
    await Future.delayed(const Duration(milliseconds: 1000));
    // if failed,use refreshFailed()
    items = ["1", "2", "3", "4", "5", "6", "7", "8"];
    _refreshController.refreshCompleted();
    if(mounted) {
      setState(() {
      });
    }
  }
  void _onLoading() async{
    // monitor network fetch
    await Future.delayed(const Duration(milliseconds: 1000));
    // if failed,use loadFailed(),if no data return,use LoadNodata()
    items.add((items.length+1).toString());
    if(mounted) {
      setState(() {
      });
    }
    _refreshController.loadComplete();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(Get.arguments['title']),),
      body: SmartRefresher(
        enablePullUp: true,
        controller: _refreshController,
        onRefresh: _onRefresh,
        onLoading: _onLoading,
        child: ListView.builder(
          physics: const ClampingScrollPhysics(),
          itemBuilder: (c, i) => Card(child: Center(child: Text(items[i],style: const TextStyle(color: Colors.black),))),
          itemExtent: 100.0,
          itemCount: items.length,
        ),
      ),
    );
  }
}

下拉改写

Flutter学习之刷新组件-pull_to_refresh

上拉加载的时分enablePullUp设置为true,咱们模仿往数据中加载2条

Flutter学习之刷新组件-pull_to_refresh

咱们也能够自定义一些

关于下拉改写头咱们能够运用默许WaterDropHeader

上拉改写的footer咱们能够像案列中一样自定义

CustomFooter get buildCustomFooter {
  return CustomFooter(
    builder: (BuildContext context, LoadStatus? mode) {
      Widget body;
      if (mode == LoadStatus.idle) {
        body = const Text("pull up load");
      } else if (mode == LoadStatus.loading) {
        body = const CupertinoActivityIndicator();
      } else if (mode == LoadStatus.failed) {
        body = const Text("Load Failed!Click retry!");
      } else if (mode == LoadStatus.canLoading) {
        body = const Text("Release to Load more");
      } else {
        body = const Text("No more Data");
      }
      return Container(
        height: 55.0,
        child: Center(child: body),
      );
    },
  );
}

Flutter学习之刷新组件-pull_to_refresh

这样一个简略的下拉改写和上拉加载就完成了

3. 大局装备RefreshConfiguration

咱们实践开发肯定要对其进行二次封装,首先咱们知道官方说大局装备子树下的SmartRefresher

    // 大局装备子树下的SmartRefresher,下面列举几个特别重要的特点
     RefreshConfiguration(
         headerBuilder: () => WaterDropHeader(),        // 装备默许头部指示器,假设你每个页面的头部指示器都一样的话,你需求设置这个
         footerBuilder:  () => ClassicFooter(),        // 装备默许底部指示器
         headerTriggerDistance: 80.0,        // 头部触发改写的越界距离
         springDescription:SpringDescription(stiffness: 170, damping: 16, mass: 1.9),         // 自定义回弹动画,三个特点值意义请查询flutter api
         maxOverScrollExtent :100, //头部最大能够拖动的规模,假如发生冲出视图规模区域,请设置这个特点
         maxUnderScrollExtent:0, // 底部最大能够拖动的规模
         enableScrollWhenRefreshCompleted: true, //这个特点不兼容PageView和TabBarView,假如你特别需求TabBarView左右滑动,你需求把它设置为true
         enableLoadingWhenFailed : true, //在加载失利的状态下,用户仍然能够通过手势上拉来触发加载更多
         hideFooterWhenNotFull: false, // Viewport不满一屏时,禁用上拉加载更多功用
         enableBallisticLoad: true, // 能够通过惯性滑动触发加载更多
        child: MaterialApp(
            ........
        )
    );

咱们创建一个Widget

Widget refreshScaffold({required Widget child}) => RefreshConfiguration(
    headerBuilder: () => const WaterDropHeader(), footerBuilder: () => const ClassicFooter(), child: child);

在进口包裹

Flutter学习之刷新组件-pull_to_refresh

这样咱们对一些页面就不用在每个页面装备header和footer等组件了,统一在RefreshConfiguration设置即可。

4. 自定义封装数据恳求逻辑

咱们一般分页的时分也是对数据进行判别处理,咱们能够抽出来这个类进行处理

enum RefreshType { refresh, loadMore }
class PagingData<T> {
  int count;
  List<T> items;
  int current = 1;
  PagingData(this.count, this.items);
  factory PagingData.fromJson(Map<String, dynamic> json, T Function(Map<String, dynamic>) decoder) {
    final count = json['count'];
    final items = (json['results'] as List<dynamic>).map((e) => decoder(e)).toList();
    return PagingData(count, items);
  }
  @override
  String toString() => '分页: $count';
  bool get isEnd => items.length >= count;
  void merge(PagingData<T> other, RefreshType refreshType) {
    if (refreshType == RefreshType.refresh) items.clear();
    count = other.count;
    items.addAll(other.items);
  }
  void prepare(RefreshType refreshType) => current = refreshType == RefreshType.refresh ? 1 : (current + 1);
}

依据RefreshType类型,进行对应的处理

mixin RefreshMixin<T> {
  final refreshController = RefreshController(initialRefresh: true);
  List<T> get items => paging.items;
  final PagingData<T> paging = PagingData(0, []);
  Future<PagingData> startRefresh(RefreshType refreshType) {
    paging.prepare(refreshType);
    return refreshRequest.then((value) {
      paging.merge(value, refreshType);
      if (refreshType == RefreshType.refresh) {
        refreshController.refreshCompleted(resetFooterState: true);
        if (paging.isEnd) refreshController.loadNoData();
      } else {
        paging.isEnd ? refreshController.loadNoData() : refreshController.loadComplete();
      }
      return paging;
    });
  }
  Future<PagingData<T>> get refreshRequest => throw UnimplementedError('子类有必要重写');
}

咱们处理改写恳求,依据恳求是否是改写还是加载更多进行判别,同时判别是否是加载完成,改变状态。 关于恳求子类有必要重写。 比如咱们的帖子类完成该协议,进行上拉下拉

class PostListController extends GetxController with RefreshMixin<Post>, NetMixin {
  /// 改写
  onRefresh() {
    startRefresh(RefreshType.refresh).then((value) => update());
  }
  /// 加载更多
  onLoading() {
    startRefresh(RefreshType.loadMore).then((value) => update());
  }
  @override
  Future<PagingData<Post>> get refreshRequest => get('post/', (data) => PagingData.fromJson(data, Post.fromJson),
      query: {'page': paging.current.toString(), 'start_id': items.isNotEmpty ? items.first.id.toString() : ''});
}

重写咱们的refreshRequest这样一个恳求就处理好了

4.小结

咱们在运用一些优异的三方组件的时分能够多了解了解,有时间的话。看下其完成的思维,关于改写的逻辑根本就是那么一套,咱们能够依据自己开发经验,抽出来做成公共类