網(wǎng)站首頁(yè) 編程語言 正文
為了追求更好的用戶體驗(yàn),有時(shí)候我們需要一個(gè)類似心跳一樣跳動(dòng)著的控件來吸引用戶的注意力,這是一個(gè)小小的優(yōu)化需求,但是在 Flutter 里動(dòng)畫兩件套就像裹腳布一樣臭長(zhǎng),所以需要像封裝一個(gè) AnimatedWidget,解放生產(chǎn)力。
實(shí)現(xiàn)動(dòng)畫
混入 SingleTickerProviderStateMixin
當(dāng)創(chuàng)建一個(gè) AnimationController 時(shí),需要傳遞一個(gè)vsync
參數(shù),存在vsync
時(shí)會(huì)防止動(dòng)畫的UI不在當(dāng)前屏幕時(shí)消耗不必要的資源。 通過混入 SingleTickerProviderStateMixin 。
class _MyHomePageState extends Statewith SingleTickerProviderStateMixin{}
創(chuàng)建動(dòng)畫
創(chuàng)建一個(gè)間隔將近一秒鐘的動(dòng)畫控制器:
late final AnimationController animController; @override void initState() { super.initState(); animController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); }
心跳動(dòng)畫是從小變大,再變小,所以需要一個(gè)值大小變化的動(dòng)畫:
late final Animationanimation; @override void initState() { super.initState(); animController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); animation = Tween ( begin: 0.9, end: 1.05, ); }
心跳是不間斷的,所以需要監(jiān)聽動(dòng)畫完成時(shí)恢復(fù)動(dòng)畫,再繼續(xù)開始動(dòng)畫:
animation = Tween( begin: 0.9, end: 1.05, ).animate(animController) ..addListener(() { setState(() {}); }) ..addStatusListener((status) { if (status == AnimationStatus.completed) { animController.reverse(); } else if (status == AnimationStatus.dismissed) { animController.forward(); } });
使用縮放控件:
Transform.scale( scale: animation.value, child: const FlutterLogo( size: 80, ), ),
為了跳動(dòng)效果,突出跳動(dòng)動(dòng)畫,把縮回去的時(shí)間改短:
animController = AnimationController( reverseDuration: const Duration(milliseconds: 700), duration: const Duration(milliseconds: 800), vsync: this, );
最后別忘了釋放資源:
@override void dispose() { animController.dispose(); super.dispose(); }
抽離成小組件
為了每次用到類似的動(dòng)畫只需引入即可,需要分離動(dòng)畫和顯示的組件。新建一個(gè)BounceWidget
,包含動(dòng)畫,然后可以傳入U(xiǎn)I組件:
class BounceWidget extends StatefulWidget { final Widget child; const BounceWidget({ Key? key, required this.child, }) : super(key: key); @override StatecreateState() => _BounceWidgetState(); }
繼續(xù)實(shí)現(xiàn)動(dòng)畫:
class _BounceWidgetState extends Statewith SingleTickerProviderStateMixin { late Animation animation; late AnimationController animController; @override void initState() { super.initState(); animController = AnimationController( reverseDuration: const Duration(milliseconds: 700), duration: const Duration(milliseconds: 800), vsync: this, ); animation = Tween ( begin: 0.9, end: 1.05, ).animate(animController) ..addListener(() { setState(() {}); }) ..addStatusListener((status) { if (status == AnimationStatus.completed) { animController.reverse(); } else if (status == AnimationStatus.dismissed) { animController.forward(); } }); animController.forward(); } @override Widget build(BuildContext context) { return Transform.scale( scale: animation.value, child: widget.child, ); } @override void dispose() { animController.dispose(); super.dispose(); } }
去引入動(dòng)畫:
Center( child: BounceWidget( child: FlutterLogo( size: 80, ), ),
完整代碼
void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override StatecreateState() => _MyHomePageState(); } class _MyHomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Padding( padding: const EdgeInsets.only(top: 80, left: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: const [ Text( "心動(dòng)的", style: TextStyle( fontSize: 28, color: Colors.black, ), ), Text( "感覺", style: TextStyle( fontSize: 48, color: Colors.black, ), ), Center( child: BounceWidget( child: FlutterLogo( size: 80, ), ), ), ], ), ), ); } }
原文鏈接:https://juejin.cn/post/7083418319159722020
相關(guān)推薦
- 2022-05-21 云原生技術(shù)kubernetes之volumes容器的使用_云其它
- 2022-06-20 C語言詳細(xì)講解qsort函數(shù)的使用_C 語言
- 2024-03-07 MyBatis快速入門案例
- 2022-10-01 Python類與實(shí)例的使用詳解_python
- 2021-12-20 Win10配置Hadoop環(huán)境變量
- 2022-02-21 django update_or_create 報(bào)錯(cuò)GPUModel.MultipleObjects
- 2022-12-12 pycharm?console?打印中文為亂碼問題及解決_python
- 2022-04-23 C#多線程系列之a(chǎn)sync和await用法詳解_C#教程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 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錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支