Flutter學習之重新整理元件-pull_to_refresh

語言: CN / TW / HK

highlight: androidstudio

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第30天,點選檢視活動詳情

  • 本文主要介紹三方元件中重新整理元件,下拉重新整理,上拉載入更多。

1. 重新整理元件

對於重新整理元件一些經過時間的考驗,基本上都解決了問題,類似我們MJRefresh,使用的人比較多
比如:pull_to_refresh: ^2.0.0

image.png 或者 flutter_easyrefresh: ^2.2.1

image.png

基本上都是可以解決我們的需求,自定義上拉下拉動畫,文字描述等。這裡我使用的是pull_to_refresh,這裡作者適配了flutter 3.0 適配 我們依賴的時候新增git地址 dart pull_to_refresh: git: url: https://github.com/miquelbeltran/flutter_pulltorefresh

2. pull_to_refresh

這裡介紹下pull_to_refresh的使用。元件的特性如下: - 提供上拉載入和下拉重新整理 - 幾乎適合所有部件 - 提供全域性設定預設指示器和屬性 - 提供多種比較常用的指示器 - 支援Android和iOS預設滑動引擎,可限制越界距離,打造自定義彈性動畫,速度,阻尼等。 - 支援水平和垂直重新整理,同時支援翻轉列表(四個方向) - 提供多種重新整理指示器風格:跟隨,不跟隨,位於背部,位於前部, 提供多種載入更多風格 - 提供二樓重新整理,可實現類似淘寶二樓,微信二樓,攜程二樓 - 允許關聯指示器存放在Viewport外部,即朋友圈重新整理效果

這裡我們看下簡單的使用,這裡使用預設的上拉和下拉Widget ```Dart

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

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

class _RefreshPageState extends State {

List 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, ), ), ); } } ``` 下拉重新整理

image.png

上拉載入的時候enablePullUp設定為true,我們模擬往資料中載入2條

image.png

我們也可以自定義一些

對於下拉重新整理頭我們可以使用預設WaterDropHeader

上拉重新整理的footer我們可以像案列中一樣自定義 Dart 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), ); }, ); }

image.png

這樣一個簡單的下拉重新整理和上拉載入就完成了

3. 全域性配置RefreshConfiguration

我們實際開發肯定要對其進行二次封裝,首先我們知道官方說全域性配置子樹下的SmartRefresher

```js // 全域性配置子樹下的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 Dart Widget refreshScaffold({required Widget child}) => RefreshConfiguration( headerBuilder: () => const WaterDropHeader(), footerBuilder: () => const ClassicFooter(), child: child); 在入口包裹

image.png

這樣我們對一些頁面就不用在每個頁面配置header和footer等元件了,統一在RefreshConfiguration設定即可。

4. 自定義封裝資料請求邏輯

我們一般分頁的時候也是對資料進行判斷處理,我們可以抽出來這個類進行處理 ```Dart enum RefreshType { refresh, loadMore }

class PagingData { int count; List items; int current = 1;

PagingData(this.count, this.items);

factory PagingData.fromJson(Map json, T Function(Map) decoder) { final count = json['count']; final items = (json['results'] as List).map((e) => decoder(e)).toList(); return PagingData(count, items); }

@override String toString() => '分頁: $count';

bool get isEnd => items.length >= count;

void merge(PagingData 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`型別,進行對應的處理Dart mixin RefreshMixin { final refreshController = RefreshController(initialRefresh: true);

List get items => paging.items; final PagingData paging = PagingData(0, []);

Future 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> get refreshRequest => throw UnimplementedError('子類必須重寫'); } 我們處理重新整理請求,根據請求是否是重新整理還是載入更多進行判斷,同時判斷是否是載入完成,改變狀態。 對於請求子類必須重寫。 比如我們的帖子類實現該協議,進行上拉下拉Dart class PostListController extends GetxController with RefreshMixin, NetMixin { /// 重新整理 onRefresh() { startRefresh(RefreshType.refresh).then((value) => update()); }

/// 載入更多 onLoading() { startRefresh(RefreshType.loadMore).then((value) => update()); }

@override Future> 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.小結

我們在使用一些優秀的三方元件的時候可以多瞭解瞭解,有時間的話。看下其實現的思想,對於重新整理的邏輯基本就是那麼一套,我們可以根據自己開發經驗,抽出來做成公共類