網站首頁 編程語言 正文
這篇會深化View拖拽實例,利用Flutter Animation、插值器以及AnimatedBuilder教大家實現帶動畫的抽屜效果。先來看效果:
通過構思,我們可以設想到實現抽屜的方式就是用Stack控件將兩個Widget疊加顯示,用GestureDetector監聽手勢滑動,動態移動頂層的Widget,當監聽到手勢結束的時候根據手勢滑動的距離動態將頂部Widget利用動畫效果滑動到結束位置即可。
實現底部Widget
class DownDrawerWidget extends StatelessWidget { ? @override ? Widget build(BuildContext context) { ? ? return Container(child: Center(child: Text("底部Widget",),),); ? } }
這個Widget太簡單了,就不細說了。
實現頂部Widget
class UpDrawerWidget extends StatelessWidget { ? @override ? Widget build(BuildContext context) { ? ? return Container(child: Center(child: Text("頂部Widget",),),); ? } }
實現方式和底部是一樣的。
實現可以移動的容器
上面兩個Widget都是單純用來顯示的Widget,因此繼承了StatelessWidget。接下來我們需要根據手勢動態移動頂部的Widget,因此需要繼承StatefulWidget。
// 頂部Widget class HomePageWidget extends StatefulWidget { ? @override ? StatecreateState() => HomePageState(); } class HomePageState extends State ? ? with SingleTickerProviderStateMixin { ? @override ? void initState() {...} ? @override ? void dispose() {...} ? @override ? Widget build(BuildContext context) {...} ? void _onViewDragDown(DragDownDetails callback) {...} ? void _onViewDrag(DragUpdateDetails callback) {...} ? void _onViewDragUp(DragEndDetails callback) {...} }
初始化狀態initState()
這個方法是在Widget初始化的時候系統的回調函數,我們需要在該函數中初始化動畫
AnimationController controller; @override void initState() { ? ? // 初始化動畫控制器,這里限定動畫時常為200毫秒 ? ? controller = new AnimationController(vsync: this, duration: const Duration(milliseconds: 200)); ? ? // vsync對象會綁定動畫的定時器到一個可視的widget,所以當widget不顯示時,動畫定時器將會暫停,當widget再次顯示時,動畫定時器重新恢復執行,這樣就可以避免動畫相關UI不在當前屏幕時消耗資源。 ? ? // 當使用vsync: this的時候,State對象必須with SingleTickerProviderStateMixin或TickerProviderStateMixin;TickerProviderStateMixin適用于多AnimationController的情況。 ? ? // 設置動畫曲線,就是動畫插值器 ? ? // 通過這個鏈接可以了解更多差值器,https://docs.flutter.io/flutter/animation/Curves-class.html,我們這里使用帶回彈效果的bounceOut。 ? ? CurvedAnimation curve = ? ? ? ? new CurvedAnimation(parent: controller, curve: Curves.bounceOut); ? ? // 增加動畫監聽,當手勢結束的時候通過動態計算到達目標位置的距離實現動畫效果。curve.value為當前動畫的值,取值范圍0~1。 ? ? curve.addListener(() { ? ? ? double animValue = curve.value; ? ? ? double offset = dragUpDownX - dragDownX; ? ? ? double toPosition; ? ? ? // 右滑 ? ? ? if (offset > 0) { ? ? ? ? if (offset > maxDragX / 5) { ? ? ? ? ? // 打開 ? ? ? ? ? toPosition = maxDragX; ? ? ? ? ? isOpenState = true; ? ? ? ? } else { ? ? ? ? ? if (isOpenState) { ? ? ? ? ? ? toPosition = maxDragX; ? ? ? ? ? ? isOpenState = true; ? ? ? ? ? } else { ? ? ? ? ? ? toPosition = 0.0; ? ? ? ? ? ? isOpenState = false; ? ? ? ? ? } ? ? ? ? } ? ? ? } else { ? ? ? ? if (offset < (-maxDragX / 2.0)) { ? ? ? ? ? // 關 ? ? ? ? ? toPosition = 0.0; ? ? ? ? ? isOpenState = false; ? ? ? ? } else { ? ? ? ? ? if (isOpenState) { ? ? ? ? ? ? toPosition = maxDragX; ? ? ? ? ? ? isOpenState = true; ? ? ? ? ? } else { ? ? ? ? ? ? toPosition = 0.0; ? ? ? ? ? ? isOpenState = false; ? ? ? ? ? } ? ? ? ? } ? ? ? } ? ? ? dragOffset = (toPosition - dragUpDownX) * animValue + dragUpDownX; ? ? ? // 刷新位置 ? ? ? setState(() {}); ? ? }); ? }
結束Widget dispose()
當Widget不可用將被回收的時候,系統會回調dispose()方法,我們在這里回收動畫。
@override void dispose() { ? ? controller.dispose(); }
記錄按下的位置
double dragDownX = 0.0; ? void _onViewDragDown(DragDownDetails callback) { ? ? dragDownX = callback.globalPosition.dx; ? }
拖動的時候刷新View的位置
/** ? ?* 最大可拖動位置 ? ?*/ ? final double maxDragX = 230.0; ? double dragOffset = 0.0; ? void _onViewDrag(DragUpdateDetails callback) { ? ? double tmpOffset = callback.globalPosition.dx - dragDownX; ? ? if (tmpOffset < 0) { ? ? ? tmpOffset += maxDragX; ? ? } ? ? // 邊緣檢測 ? ? if (tmpOffset < 0) { ? ? ? tmpOffset = 0.0; ? ? } else if (tmpOffset >= maxDragX) { ? ? ? tmpOffset = maxDragX; ? ? } ? ? // 刷新 ? ? if (dragOffset != tmpOffset) { ? ? ? dragOffset = tmpOffset; ? ? ? setState(() {}); ? ? } ? }
離手的時候記錄位置并執行動畫
/** ? ?* 脫手時候的位置 ? ?*/ ? double dragUpDownX = 0.0; ? void _onViewDragUp(DragEndDetails callback) { ? ? dragUpDownX = dragOffset; ? ? // 執行動畫,每次都從第0幀開始執行 ? ? controller.forward(from: 0.0); ? }
支持移動的Widget
@override ? Widget build(BuildContext context) { ? ? return Transform.translate( ? ? ? offset: Offset(dragOffset, 0.0), ? ? ? child: Container( ? ? ? ? child: GestureDetector( ? ? ? ? ? ? ? onHorizontalDragDown: _onViewDragDown, ? ? ? ? ? ? ? onVerticalDragDown: _onViewDragDown, ? ? ? ? ? ? ? onHorizontalDragUpdate: _onViewDrag, ? ? ? ? ? ? ? onVerticalDragUpdate: _onViewDrag, ? ? ? ? ? ? ? onHorizontalDragEnd: _onViewDragUp, ? ? ? ? ? ? ? onVerticalDragEnd: _onViewDragUp, ? ? ? ? ? ? ? child: Container( ? ? ? ? ? ? ? ? child: new UpDrawerWidget(), ? ? ? ? ? ),),),);}
Flutter動畫
總結一下,想在Flutter中實現動畫,需要先創建一個AnimationController控制器;如果有特殊的插值要求,再創建一個插值器,調用controller.forward()方法執行動畫,通過addListener()的回調改變對應數值之后調用setState(() {})方法刷新位置即可。
Flutter API還提供AnimatedBuilder用來簡化實現動畫的復雜性,讓我們不用手動調用addListener()方法。
原文鏈接:https://blog.csdn.net/weixin_44339238/article/details/98969379
相關推薦
- 2022-06-23 C語言一看就懂的選擇與循環語句及函數介紹_C 語言
- 2023-01-12 pandas中的DataFrame數據遍歷解讀_python
- 2022-06-06 webpack4.0-解決webpack 報The 'mode' option has not be
- 2023-02-09 使用adb?or?fastboot命令進入高通的9008(edl)模式的兩種方法_Android
- 2022-03-09 c++中STL庫隊列詳細介紹_C 語言
- 2023-07-15 oracle 序列/自增ID
- 2022-11-07 React組件封裝中三大核心屬性詳細介紹_React
- 2022-08-05 python批量處理打開多個文件_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支