網(wǎng)站首頁 編程語言 正文
前言
在Flutter
開發(fā)中官方提供了多平臺的下拉刷新組件供開發(fā)者使用,例如RefreshIndicator
和CupertinoSliverRefreshControl
分別適配Android
和iOS
下拉刷新交互形態(tài)。但實際情況中這兩者使用情況卻不太相同在使用場景就存在差異,RefreshIndicator
作為嵌套型下拉組件列表內(nèi)容作為它的child
使用而CupertinoSliverRefreshControl
是嵌入在Sliver
列表中使用。同時對于交互設(shè)計來說一般更偏好RefreshIndicator
下拉形式,通過下拉列表整體下移后透出拉下刷新組件樣式。
改造點
DIY下拉組件樣式
RefreshIndicator
下拉組件樣式可能會在交互上不符合設(shè)計師要求。例如下拉過程中l(wèi)oading樣式出現(xiàn)交互是會和列表重合,實際需求可能是希望下拉過程中l(wèi)oading樣式和列表一樣同步下移出現(xiàn)。
因此修改原有的下拉刷新組件樣式構(gòu)建,構(gòu)建方法入?yún)⒅饕莚efreshState、pulledExtent、refreshTriggerPullDistance、refreshIndicatorExtent。原邏輯中組件偏量是固定不變_kActivityIndicatorMargin
值,因此下拉組件樣式是直接顯示出來的。
調(diào)整方案根據(jù)pulledExtent下拉距離,默認(rèn)偏移下拉組件樣式自身高度加上下拉距離從而將偏移量從負(fù)方向向正方向展示。
Widget buildRefreshIndicator( BuildContext context, RefreshIndicatorMode refreshState, //下拉狀態(tài) double pulledExtent, // 下拉實時距離 double refreshTriggerPullDistance, // 下拉限制最大高度 double refreshIndicatorExtent, // 下拉組件最大高度 ) { return Container( color: Colors.deepOrange, child: Stack( clipBehavior: Clip.none, children: <Widget>[ Positioned( top: -refreshIndicatorExtent + pulledExtent, left: 0.0, right: 0.0, //簡易的下拉樣式 忽略refreshState狀態(tài) child: Container( child: Text("我是下拉呀~~~~",style: TextStyle(color: Colors.white,fontSize: 20,),textAlign: TextAlign.center,), ), ), ], ), ); }
刷新時機調(diào)整
RefreshIndicator
下拉組件另外刷新觸發(fā)交互點也不是設(shè)計交互期望的邏輯,它的刷新觸發(fā)機制是只要下拉超過設(shè)置下拉距離并會觸發(fā)。但實際開發(fā)中可能并不希望當(dāng)?shù)竭_對應(yīng)點就去做刷新操作而是下拉到一定距離松手后才會觸發(fā),因此需要改造下拉刷新組件內(nèi)部的刷新機制。
原下拉刷新邏輯如下關(guān)鍵代碼所示,只要當(dāng)RefreshIndicatorMode.drag
狀態(tài)下并且latestIndicatorBoxExtent > widget.refreshTriggerPullDistance
時就會觸發(fā)下拉刷新方法。
RefreshIndicatorMode transitionNextState() { RefreshIndicatorMode nextState; 。、、、 、、、 省略 drag: case RefreshIndicatorMode.drag: if (latestIndicatorBoxExtent == 0) { return RefreshIndicatorMode.inactive; } else if (latestIndicatorBoxExtent < widget.refreshTriggerPullDistance) { return RefreshIndicatorMode.drag; } else { // 當(dāng)latestIndicatorBoxExtent > widget.refreshTriggerPullDistance就執(zhí)行 if (widget.onRefresh != null) { //刷新邏輯執(zhí)行點 HapticFeedback.mediumImpact(); SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) { refreshTask = widget.onRefresh()..whenComplete(() { if (mounted) { setState(() => refreshTask = null); refreshState = transitionNextState(); } }); setState(() => hasSliverLayoutExtent = true); }); } return RefreshIndicatorMode.armed; } break; case RefreshIndicatorMode.armed: if (refreshState == RefreshIndicatorMode.armed && refreshTask == null) { goToDone(); continue done; } if (latestIndicatorBoxExtent > widget.refreshIndicatorExtent) { return RefreshIndicatorMode.armed; } else { nextState = RefreshIndicatorMode.refresh; } continue refresh; refresh: case RefreshIndicatorMode.refresh: if (refreshTask != null) { return RefreshIndicatorMode.refresh; } else { goToDone(); } continue done; 、、、、、省略 } return nextState; }
弱希望下拉松手后判斷是否觸發(fā)刷新只修改RefreshIndicator
下拉組件似乎無法直接滿足條件。因此需要結(jié)合手勢監(jiān)聽來完成,需要對整體框架代碼做一個調(diào)整。
增加Listener
嵌套監(jiān)聽手勢抬起操作,獲取MagicSliverRefreshControlState
(原是私有類放開為公有)判斷是否是超出下拉最小刷新間距,對內(nèi)部是否可刷新標(biāo)記進行賦值操作。
GlobalKey<MagicSliverRefreshControlState> key = GlobalKey<MagicSliverRefreshControlState>(); Listener( child: CustomScrollView( physics: BouncingScrollPhysics(), slivers: <Widget>[ MagicSliverRefreshControl( key: key, builder: buildRefreshIndicator, onRefresh: () async { print("<> SliverRefreshControl onRefresh start"); await Future.delayed(Duration(seconds: 2),(){}); print("<> SliverRefreshControl onRefresh end"); }, ), SliverList( delegate: SliverChildBuilderDelegate( (content, index) { return Common.getWidget(index); }, childCount: 100, ), ) ], ), onPointerUp: (event){ //判斷是否可刷新操作 if(key?.currentState?.isCanRefreshAction() ?? false){ key?.currentState?.canRefresh = true; }else{ key?.currentState?.canRefresh = false; } }, );
RefreshIndicator
組件內(nèi)部增加一種新狀態(tài)RefreshIndicatorMode.over
用來判斷是否刷新臨界狀態(tài),結(jié)合外部手勢抬手監(jiān)聽。當(dāng)下拉超出刷新最小間距且抬手放開判斷觸發(fā)刷新操作,over
恢復(fù)到drag
還是進入armed
都是通過以上條件來實現(xiàn)的,其他原邏輯保持不變。
switch (refreshState) { case RefreshIndicatorMode.inactive: if (latestIndicatorBoxExtent <= 0) { return RefreshIndicatorMode.inactive; } else { nextState = RefreshIndicatorMode.drag; } continue drag; drag: case RefreshIndicatorMode.drag: if (latestIndicatorBoxExtent == 0) { return RefreshIndicatorMode.inactive; } else if (latestIndicatorBoxExtent < widget.refreshTriggerPullDistance) { return RefreshIndicatorMode.drag; } else { return RefreshIndicatorMode.over; //增加一種狀態(tài) 表示下拉滿足刷新條件 } break; /// 進入新狀態(tài)后結(jié)合抬手后是否可刷新標(biāo)記為判斷是進入刷新方法還是回到拖拽狀態(tài) case RefreshIndicatorMode.over: if (latestIndicatorBoxExtent <= widget.refreshTriggerPullDistance) { if(canRefresh){ canRefresh = false; //將刷新標(biāo)記置空復(fù)位 if (widget.onRefresh != null) { HapticFeedback.mediumImpact(); SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) { refreshTask = widget.onRefresh()..whenComplete(() { if (mounted) { setState(() => refreshTask = null); refreshState = transitionNextState(); } }); setState(() => hasSliverLayoutExtent = true); }); } return RefreshIndicatorMode.armed; }else{ return RefreshIndicatorMode.drag; } } return RefreshIndicatorMode.over; break;
效果展示
具體代碼看這里
調(diào)整前
調(diào)整后
原文鏈接:https://juejin.cn/post/7135034406493749262
相關(guān)推薦
- 2022-10-18 Qt實現(xiàn)TCP客戶端和服務(wù)器通訊程序_C 語言
- 2022-05-13 Django-Cookies && Session
- 2022-11-25 ASP.NET?MVC使用異步Action的方法_實用技巧
- 2023-03-05 Python中ConfigParser模塊示例詳解_python
- 2022-08-01 Android開發(fā)之Flutter與webview通信橋梁實現(xiàn)_Android
- 2022-08-27 Terraform集成簡單Gitlab?CI方案詳解_其它綜合
- 2022-05-06 詳解go語言中sort如何排序_Golang
- 2022-08-07 Android?Gradle?插件自定義Plugin實現(xiàn)注意事項_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支