網(wǎng)站建設(shè)重點(diǎn)成品短視頻app下載有哪些
一、動(dòng)畫基本原理以及Flutter動(dòng)畫簡(jiǎn)介
1、動(dòng)畫原理:
在任何系統(tǒng)的Ul框架中,動(dòng)畫實(shí)現(xiàn)的原理都是相同的,即:在一段時(shí)間內(nèi),快速地多次改變Ul外觀;由于人眼會(huì)產(chǎn)生視覺暫留,所以最終看到的就是一個(gè)“連續(xù)”的動(dòng)畫,這和電影的原理是一樣的。
我們將UI的一次改變稱為一個(gè)動(dòng)畫幀,對(duì)應(yīng)一次屏幕刷新,而決定動(dòng)畫流暢度的一個(gè)重要指標(biāo)就是幀率FPS(Frame Per Second),即每秒的動(dòng)畫幀數(shù)。
很明顯,幀率越高則動(dòng)畫就會(huì)越流暢!一般情況下,對(duì)于人眼來(lái)說(shuō),動(dòng)畫幀率超過(guò)16FPS,就基本能看了,超過(guò)32FPS就會(huì)感覺相對(duì)平滑,而超過(guò)32FPS,大多數(shù)人基本上就感受不到差別了。
由于動(dòng)畫的每一幀都是要改變Ul輸出,所以在一個(gè)時(shí)間段內(nèi)連續(xù)的改變Ul輸出是比較耗資源的,對(duì)設(shè)備的軟硬件系統(tǒng)要求都較高,所以在Ul系統(tǒng)中,動(dòng)畫的平均幀率是重要的性能指標(biāo),而在Flutter中,理想情況下是可以實(shí)現(xiàn)60FPS的,這和原生應(yīng)用能達(dá)到的幀率是基本是持平的。
2、FLutter中的動(dòng)畫主要分為:
隱式動(dòng)畫、顯式動(dòng)畫、自定義隱式動(dòng)畫、自定義顯式動(dòng)畫、Hero動(dòng)畫;
二、隱式動(dòng)畫:
1、說(shuō)明:
通過(guò)幾行代碼就可以實(shí)現(xiàn)隱式動(dòng)畫,由于隱式動(dòng)畫背后的實(shí)現(xiàn)原理和繁瑣的操作細(xì)節(jié)都被隱去了,所以叫隱式動(dòng)畫,FLutter中提供的 AnimatedContainer、AnimatedPadding、AnimatedPositioned、AnimatedOpacity、AnimatedDefaultTextStyle、AnimatedSwitcher都屬于隱式動(dòng)畫。
隱式動(dòng)畫中可以通過(guò) duration配置動(dòng)畫時(shí)長(zhǎng)、可以通過(guò) Curve(曲線)來(lái)配置動(dòng)畫過(guò)程。
2、Curves曲線值:
linear 勻速的;
decelerate 勻減速;
ease 開始加速,后面減速;
easeln 開始慢,后面快;
easeOut 開始快,后面慢;
easelnOut 開始慢,然后加速,最后再減速;
3、AnimatedContainer:
AnimatedContainer的屬性和Container屬性基本是一樣的,當(dāng)AnimatedContainer屬性改變的時(shí)候就會(huì)觸發(fā)動(dòng)畫。
代碼示例:放大縮小動(dòng)畫
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.animation),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text("AnimatedContainer Demo"),),body: Center(child: AnimatedContainer(duration: const Duration(milliseconds: 500),// 動(dòng)畫時(shí)長(zhǎng)500 ms width: flag?100:300,height: flag ? 100 : 300,color: Colors.blue,),),);}
}
4、AnimatedPadding:
代碼示例:上下移動(dòng)動(dòng)畫
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.animation),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text("AnimatedContainer Demo"),),body: AnimatedPadding(duration: const Duration(milliseconds: 2000),// 動(dòng)畫時(shí)長(zhǎng)500 ms curve: Curves.bounceInOut,padding: EdgeInsets.fromLTRB(10, flag ? 10 : 500, 0, 0),child: Container(width: 100,height: 100,color: Colors.red,),),);}
}
5、AnimatedPositioned:
代碼示例:對(duì)角線移動(dòng)動(dòng)畫
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({Key? key}) : super(key: key);@overrideMyHomePageState createState() => MyHomePageState();
}class MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("AnimatedPositioned Demo"),),body: Stack(children: [AnimatedPositioned(curve: Curves.easeInOut,duration: const Duration(seconds: 1),top: flag ? 10 : 500,left: flag ? 10 : 300,child: Container(width: 60,height: 60,color: Colors.blue,),),Align(alignment: const Alignment(0, 0.8),child: ElevatedButton(child: const Text("Transform"),onPressed: () {setState(() {flag = !flag;});},),),],),);}
}
6、AnimatedOpacity:
代碼示例:淡入淡出動(dòng)畫
import 'dart:isolate';
import 'dart:math';import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({Key? key}) : super(key: key);@overrideMyHomePageState createState() => MyHomePageState();
}class MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(onPressed: (){setState(() {flag= !flag;});},child: const Icon(Icons.opacity),),appBar: AppBar(title: const Text("AnimatedPositioned Demo"),),body: Center(child: AnimatedOpacity(curve: Curves.linear,duration: const Duration(seconds: 1),opacity: flag?1:0,child: Container(width: 300,height: 300,color: Colors.blue,),),),);}
}
7、AnimatedDefaultTextStyle:
代碼示例:字體放大縮小動(dòng)畫
import 'dart:isolate';
import 'dart:math';import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({Key? key}) : super(key: key);@override_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(onPressed: () {setState(() {flag = !flag;});},child: const Icon(Icons.opacity),),appBar: AppBar(title: const Text("AnimatedPositioned Demo"),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 300,color: Colors.blue,child: AnimatedDefaultTextStyle(duration: const Duration(seconds: 1),style: TextStyle(fontSize: flag ? 20 : 50,),child: const Text("你好Flutte"),),),),);}
}
8、AnimatedSwitcher 和 transitionBuilder:
AnimatedContainer、AnimatedPadding、AnimatedPositioned、AnimatedOpacity、AnimatedDefaultTextStyle都是在屬性改變的時(shí)候執(zhí)行動(dòng)畫,AnimatedSwitcher則是在子元素改變的時(shí)候執(zhí)行動(dòng)畫。
相比上面的動(dòng)畫組件AnimatedSwitcher多了transitionBuilder參數(shù),可以在transitionBuilder中自定義動(dòng)畫
代碼示例1、面前顯示一個(gè)loading動(dòng)畫,加載完畢后顯示對(duì)于的內(nèi)容:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({Key? key}) : super(key: key);@override_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(onPressed: () {setState(() {flag = !flag;});},child: const Icon(Icons.opacity),),appBar: AppBar(title: const Text("AnimatedPositioned Demo"),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 180,color: Colors.yellow,child: AnimatedSwitcher(duration: const Duration(milliseconds: 1000),child: flag? const CircularProgressIndicator(): Image.network("https://pics2.baidu.com/feed/b7003af33a87e9502b64d86f4c2e9544fbf2b45b.jpeg@f_auto?token=8c6557279177a75d44029840f0db0daa&s=C8AA67D91C0090457095310903005057",fit: BoxFit.contain,),),),),);}
}
代碼示例2、通過(guò)transitionBuilder自定義動(dòng)畫效果:
import 'dart:isolate';
import 'dart:math';import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({Key? key}) : super(key: key);@override_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(onPressed: () {setState(() {flag = !flag;});},child: const Icon(Icons.opacity),),appBar: AppBar(title: const Text("AnimatedPositioned Demo"),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 180,color: Colors.yellow,child: AnimatedSwitcher(transitionBuilder: ((child, animation) {return ScaleTransition(scale: animation,child: FadeTransition(opacity: animation,child: child,),);}),duration: const Duration(milliseconds: 400),child: flag? const CircularProgressIndicator(): Image.network("https://pics2.baidu.com/feed/b7003af33a87e9502b64d86f4c2e9544fbf2b45b.jpeg@f_auto?token=8c6557279177a75d44029840f0db0daa&s=C8AA67D91C0090457095310903005057",fit: BoxFit.contain,),),),),);}
}
代碼示例3、通過(guò)transitionBuilder改變子元素執(zhí)行動(dòng)畫:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({Key? key}) : super(key: key);@override_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(onPressed: () {setState(() {flag = !flag;});},child: const Icon(Icons.opacity),),appBar: AppBar(title: const Text("AnimatedPositioned Demo"),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 180,color: Colors.yellow,child: AnimatedSwitcher(transitionBuilder: ((child, animation) {return ScaleTransition(scale: animation,child: FadeTransition(opacity: animation,child:child ,),);}),duration: const Duration(milliseconds: 400),child: Text(key:UniqueKey(),flag?"你好Flutter":"你好世界",style: const TextStyle(fontSize: 30),),),),),);}
}
三、顯式動(dòng)畫:
1、說(shuō)明:
常見的顯式動(dòng)畫有RotationTransition、FadeTransition、 ScaleTransition、SlideTransition、Animatedlcon。在顯示動(dòng)畫中開發(fā)者需要?jiǎng)?chuàng)建一個(gè)AnimationController,通過(guò)AnimationController控制動(dòng)畫的開始、暫停、重置、跳轉(zhuǎn)、倒播等;
2、RotationTransition旋轉(zhuǎn)?和 AnimationController
1)AnimationController普通用法:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => MyHomePageState();
}class MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {super.initState();// TODO: implement initState_controller = AnimationController(// Vsync 機(jī)制可以理解為是顯卡與顯示器的通信橋梁,顯卡在渲染每一幀之前會(huì)等待垂直同步信號(hào),// 只有顯示器完成了一次刷新時(shí),發(fā)出垂直同步信號(hào),顯卡才會(huì)渲染下一幀,確保刷新率和幀率保// 持同步,以達(dá)到供需平衡的效果,防止卡頓現(xiàn)象。vsync:this,duration: const Duration(seconds: 1),);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [RotationTransition(turns: _controller,child: const FlutterLogo(size: 100,),),const SizedBox(height: 40,),Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Wrap(spacing: 10,alignment: WrapAlignment.center,children: [ElevatedButton(onPressed: () {_controller.forward(); //正序播放一次}, child: const Text("Forward")),ElevatedButton(onPressed: () {_controller.reverse(); //倒序播放一次}, child: const Text("Reverse")),ElevatedButton(onPressed: () {_controller.stop(); //停止播放}, child: const Text("Stop")),ElevatedButton(onPressed: () {_controller.reset(); //重置}, child: const Text("rest")),ElevatedButton(onPressed: () {_controller.repeat(); //重復(fù)播放}, child: const Text("repeat"))],),)],),);}
}
2)IowerBound 和 upperBound:
AnimationController用于控制動(dòng)畫,它包含動(dòng)畫的啟動(dòng)forward()、停止 stop()、反向播放reverse()等方法。AnimationController 會(huì)在動(dòng)畫的每一幀,就會(huì)生成一個(gè)新的值。默認(rèn)情況下,AnimationController 在給定的時(shí)間段內(nèi)線性的生成從 0.0到1.0(默認(rèn)區(qū)間)的數(shù)字,我們也可以通過(guò) lowerbound 和 upperBound 來(lái)修改 AnimationController 生成數(shù)字的區(qū)間。
代碼示例:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {
// TODO: implement initState_controller = AnimationController(vsync: this,duration: const Duration(seconds: 1),lowerBound: 3, //第三圈轉(zhuǎn)到第五圈upperBound: 5);_controller.addListener(() {print(_controller.value);});}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [RotationTransition(turns: _controller,child: const FlutterLogo(size: 100,),),const SizedBox(height: 40,),Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),child: Wrap(spacing: 10,alignment: WrapAlignment.center,children: [ElevatedButton(onPressed: () {_controller.forward(); //正序播放一次},child: const Text("Forward")),ElevatedButton(onPressed: () {_controller.reverse(); //倒序播放一次},child: const Text("Reverse")),ElevatedButton(onPressed: () {_controller.stop(); //停止播放},child: const Text("Stop")),ElevatedButton(onPressed: () {_controller.reset(); //重置},child: const Text("rest")),ElevatedButton(onPressed: () {_controller.repeat(); //重復(fù)播放},child: const Text("repeat"))],),)],),);}
}
3、FadeTransition?淡入淡出
代碼示例:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {_controller = AnimationController(vsync: this,duration: const Duration(seconds: 1),);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [FadeTransition(opacity: _controller,child: const FlutterLogo(size: 80),),const SizedBox(height: 40,),Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [ElevatedButton(onPressed: () {_controller.forward(); //正序播放一次},child: const Text("Forward")),ElevatedButton(onPressed: () {_controller.reverse(); //倒序播放一次},child: const Text("Reverse")),],),)],),);}
}
4、ScaleTransition縮放?和 Tween
默認(rèn)情況下,AnimationController對(duì)象值的范圍是[0.0,1.0]。如果我們需要構(gòu)建Ul的動(dòng)畫值在不同的范圍或不同的數(shù)據(jù)類型,則可以使用Tween來(lái)添加映射以生成不同的范圍或數(shù)據(jù)類型的值
代碼示例1、AnimationController控制動(dòng)畫:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}
//示例1:AnimationController控制動(dòng)畫
class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {
// TODO: implement initState_controller = AnimationController(vsync: this,duration: const Duration(seconds: 1),);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [ScaleTransition(scale: _controller,child: const FlutterLogo(size: 80),),const SizedBox(height: 40,),Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [ElevatedButton(onPressed: () {_controller.forward(); //正序播放一次},child: const Text("Forward")),ElevatedButton(onPressed: () {_controller.reverse(); //倒序播放一次},child: const Text("Reverse")),],),)],),);}
}
代碼示例2、AnimationController結(jié)合Tween控制動(dòng)畫:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}
//示例2:AnimationController結(jié)合Tween控制動(dòng)畫
class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {// TODO: implement initState_controller = AnimationController(vsync: this,duration: const Duration(seconds: 1),);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [ScaleTransition(scale: _controller.drive(Tween(begin: 1, end: 2)),child: const FlutterLogo(size: 80),),const SizedBox(height: 40,),Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [ElevatedButton(onPressed: () {_controller.forward(); //正序播放一次},child: const Text("Forward")),ElevatedButton(onPressed: () {_controller.reverse(); //倒序播放一次},child: const Text("Reverse")),],),)],),);}
}
5、SlideTransition平移
這是一負(fù)責(zé)平移的顯示動(dòng)畫組件,使用時(shí)需要通過(guò)position屬性傳入一個(gè)Animated表示位移程度,通常借助Tween實(shí)現(xiàn)
代碼示例1、_controller.drive驅(qū)動(dòng)動(dòng)畫:左右移動(dòng)
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {_controller = AnimationController(vsync: this,duration: const Duration(seconds: 1),);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [SlideTransition(position: _controller.drive(Tween(begin: const Offset(0, 0),end: const Offset(1.2, 0),),),child: const FlutterLogo(size: 80),),const SizedBox(height: 40,),Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [ElevatedButton(onPressed: () {_controller.forward(); //正序播放一次},child: const Text("Forward")),ElevatedButton(onPressed: () {_controller.reverse(); //倒序播放一次},child: const Text("Reverse")),],),),],),);}
}
代碼示例2、Tween. animate 驅(qū)動(dòng)動(dòng)畫:左右移動(dòng)
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {super.initState();_controller = AnimationController(vsync: this,duration: const Duration(seconds: 1),);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [SlideTransition(position: Tween(begin: const Offset(0, 0),end: const Offset(1.2, 0) //表示實(shí)際的位置向右移動(dòng)自身寬度的1.2倍).animate(_controller),child: const FlutterLogo(size: 80),),const SizedBox(height: 40,),Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [ElevatedButton(onPressed: () {_controller.forward(); //正序播放一次},child: const Text("Forward")),ElevatedButton(onPressed: () {_controller.reverse(); //倒序播放一次},child: const Text("Reverse")),],),)],),);}
}
代碼示例3、鏈?zhǔn)讲僮餍薷膭?dòng)畫效果:彈性上下移動(dòng)
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {
// TODO: implement initState_controller = AnimationController(vsync: this,duration: const Duration(seconds: 1),);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [SlideTransition(position:Tween(begin: const Offset(0, -1), end: const Offset(0, 0.8)).chain(CurveTween(curve: Curves.bounceIn)).animate(_controller),child: const FlutterLogo(size: 80),),const SizedBox(height: 40,),Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [ElevatedButton(onPressed: () {_controller.forward(); //正序播放一次},child: const Text("Forward")),ElevatedButton(onPressed: () {_controller.reverse(); //倒序播放一次},child: const Text("Reverse")),],),)],),);}
}
代碼示例4、鏈?zhǔn)讲僮餍薷膭?dòng)動(dòng)畫執(zhí)行時(shí)間:彈性上下移動(dòng)
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {
// TODO: implement initState_controller = AnimationController(vsync: this,duration: const Duration(seconds: 3),);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [SlideTransition(position:Tween(begin: const Offset(0, -1), end: const Offset(0, 0.8)).chain(CurveTween(curve: Curves.bounceIn)).chain(CurveTween(curve: const Interval(0.8, 1.0))).animate(_controller),child: const FlutterLogo(size: 80),),const SizedBox(height: 40,),Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [ElevatedButton(onPressed: () {_controller.forward(); //正序播放一次},child: const Text("Forward")),ElevatedButton(onPressed: () {_controller.reverse(); //倒序播放一次},child: const Text("Reverse")),],),)],),);}
}
四、Animatedlcon:
Animatedlcon顧名思義,是一個(gè)用于提供動(dòng)畫圖標(biāo)的組件,它的名字雖然是以Animated開頭,但是他是一個(gè)顯式動(dòng)畫組件,需要通過(guò)progress屬性傳入動(dòng)畫控制器,另外需要由lcon屬性傳入動(dòng)畫圖標(biāo)數(shù)據(jù);
代碼示例:旋轉(zhuǎn)改變圖標(biāo)
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {super.initState();_controller =AnimationController(vsync: this, duration: const Duration(seconds: 1));}@overridevoid dispose() {super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.add),onPressed: () {_controller.forward();},),appBar: AppBar(title: const Text('Title'),),body: Center(child: AnimatedIcon(//圖標(biāo)效果列表:https://api.flutter.dev/flutter/material/AnimatedIcons-class.htmlicon: AnimatedIcons.menu_close, progress: _controller, size: 40),),);}
}
五、交錯(cuò)動(dòng)畫:
代碼示例:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;bool flag = true;@overridevoid initState() {super.initState();_controller = AnimationController(vsync: this,duration: const Duration(seconds: 6),)..repeat(reverse: true);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {flag ? _controller.forward() : _controller.reverse();flag = !flag;},),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [SlidingBox(controller: _controller,color: Colors.blue[200],curve: const Interval(0, 0.2),),SlidingBox(controller: _controller,color: Colors.blue[400],curve: const Interval(0.2, 0.4),),SlidingBox(controller: _controller,color: Colors.blue[600],curve: const Interval(0.4, 0.6),),SlidingBox(controller: _controller,color: Colors.blue[800],curve: const Interval(0.6, 0.8),),SlidingBox(controller: _controller,color: Colors.blue[900],curve: const Interval(0.8, 1.0),),],),),);}
}class SlidingBox extends StatelessWidget {final AnimationController controller;final Color? color;final Curve curve;const SlidingBox({super.key,required this.controller,required this.color,required this.curve});@overrideWidget build(BuildContext context) {return SlideTransition(position: Tween(begin: const Offset(0, 0), end: const Offset(0.3, 0)).chain(CurveTween(curve: Curves.bounceInOut)).chain(CurveTween(curve: curve)).animate(controller),child: Container(width: 220,height: 60,color: color,),);}
}
六、自定義動(dòng)畫:
1、TweenAnimationBuilder自定義隱式動(dòng)畫
每當(dāng)Tween的end發(fā)生變化的時(shí)候就會(huì)觸發(fā)動(dòng)畫
代碼示例1、大小變化:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),body: Center(child: TweenAnimationBuilder(tween: Tween(begin: 100.0, end: flag ? 100.0 : 200.0),duration: const Duration(seconds: 1),builder: ((context, value, child) {return Icon(Icons.star,size: value,);})),),);}
}
代碼示例2、透明度變化:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),body: Center(child: TweenAnimationBuilder(tween: Tween(begin: 0.0, end: flag ? 0.0 : 1.0),duration: const Duration(seconds: 1),builder: ((context, value, child) {return Opacity(opacity: value,child: Container(color: Colors.red,width: 200,height: 200,),);})),),);}
}
2、AnimatedBuilder 自定義顯式動(dòng)畫
代碼示例1、透明度動(dòng)畫:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {super.initState();_controller =AnimationController(vsync: this, duration: const Duration(seconds: 1))..repeat(reverse: true);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Center(child: AnimatedBuilder(animation: _controller,builder: (BuildContext context, Widget? child) {return Opacity(opacity: _controller.value, //從0到1的變化child: Container(width: 200,height: 200,color: Colors.red,child: const Text("我是一個(gè)text組件"),),);},),),);}
}
代碼示例2、自定義變化范圍:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {super.initState();_controller =AnimationController(vsync: this, duration: const Duration(seconds: 1))..repeat(reverse: true);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Center(child: AnimatedBuilder(animation: _controller,builder: (BuildContext context, Widget? child) {return Opacity(opacity: Tween(begin: 0.5, end: 1.0).animate(_controller).value, //從0.5到1的變化child: Container(width: 200,height: 200,color: Colors.red,child: const Text("我是一個(gè)text組件"),),);},),),);}
}
代碼示例3、位置變化:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {super.initState();_controller =AnimationController(vsync: this, duration: const Duration(seconds: 1))..repeat(reverse: true);}@overrideWidget build(BuildContext context) {Animation x = Tween(begin: -100.0, end: 100.0).chain(CurveTween(curve: Curves.easeIn)).chain(CurveTween(curve: const Interval(0.2, 0.8))).animate(_controller);return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Center(child: AnimatedBuilder(animation: _controller,builder: (BuildContext context, Widget? child) {return Container(width: 200,height: 200,color: Colors.red,transform: Matrix4.translationValues(x.value, 0, 0),child: const Text("我是一個(gè)text組件"),);},),),);}
}
代碼示例4、child優(yōu)化:
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage>with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {super.initState();_controller =AnimationController(vsync: this, duration: const Duration(seconds: 1))..repeat(reverse: true);}@overrideWidget build(BuildContext context) {Animation x = Tween(begin: -100.0, end: 100.0).chain(CurveTween(curve: Curves.easeIn)).chain(CurveTween(curve: const Interval(0.2, 0.4))).animate(_controller);return Scaffold(appBar: AppBar(title: const Text('Title'),),body: Center(child: AnimatedBuilder(animation: _controller,builder: (BuildContext context, Widget? child) {return Container(width: 200,height: 200,color: Colors.red,transform: Matrix4.translationValues(x.value, 0, 0),child: child,);},child: const Text("我是一個(gè)text組件"),),),);}
}
七、Hero 動(dòng)畫:
1、Hero動(dòng)畫的使用:
微信朋友圈點(diǎn)擊小圖片的時(shí)候會(huì)有一個(gè)動(dòng)畫效果到大圖預(yù)覽,這個(gè)動(dòng)畫效果就可以使用Hero動(dòng)畫實(shí)現(xiàn)。
Hero 指的是可以在路由(頁(yè)面)之間“飛行”的widget,簡(jiǎn)單來(lái)說(shuō) Hero 動(dòng)畫就是在路由切換時(shí),有一個(gè)共享的widget 可以在新舊路由間切換。
代碼示例:
home.dart:
import 'package:flutter/material.dart';
import '../../res/listData.dart';
import 'hero.dart';
import 'hero2.dart';class HomePage extends StatefulWidget {const HomePage({super.key});@overrideState<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {List<Widget> _getListData() {var tempList = listData2.map((value) {return GestureDetector(onTap: () {Navigator.push(context, MaterialPageRoute(builder: (setting) {// return HeroPage(arguments: {// "imageUrl": value['imageUrl'],// "description": value['title'],// });return HeroPage(arguments: {"imageUrl": value['imageUrl'],"description": value['title'],});}));},child: Container(decoration: BoxDecoration(border: Border.all(color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),child: Column(children: <Widget>[Hero(tag: value['title'],// child: Image.asset(value['imageUrl'])),child: Image.network(value['imageUrl'],width: 320,height: 120,fit: BoxFit.cover,),),const SizedBox(height: 12),Text(value['title'],textAlign: TextAlign.center,style: const TextStyle(fontSize: 20),)],)),);});return tempList.toList();}@overrideWidget build(BuildContext context) {return GridView.count(crossAxisSpacing: 10.0,//水平子 Widget 之間間距mainAxisSpacing: 10.0,//垂直子 Widget 之間間距padding: const EdgeInsets.all(10),crossAxisCount: 2,//一行的 Widget 數(shù)量// childAspectRatio:0.7,// 寬度和高度的比例children: _getListData(),);}
}
hero.dart:
import 'package:flutter/material.dart';class HeroPage extends StatefulWidget {final Map arguments;const HeroPage({super.key, required this.arguments});@overrideState<HeroPage> createState() => _HeroPageState();
}class _HeroPageState extends State<HeroPage> {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("詳情頁(yè)面"),),body: ListView(children: [Hero(tag: widget.arguments["imageUrl"],child: Image.network(widget.arguments["imageUrl"])),const SizedBox(height: 20),Padding(padding: const EdgeInsets.all(5),child: Text(widget.arguments["description"],style: const TextStyle(fontSize: 22)),)],),);}
}
2、Hero +photo_view 實(shí)現(xiàn)微信朋友圈圖片預(yù)覽:
photo_view預(yù)覽圖片:?
?
代碼示例:
home.dart:
import 'package:flutter/material.dart';
import '../../res/listData.dart';
import 'hero.dart';
import 'hero2.dart';class HomePage extends StatefulWidget {const HomePage({super.key});@overrideState<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {List<Widget> _getListData() {var tempList = listData2.map((value) {return GestureDetector(onTap: () {Navigator.push(context, MaterialPageRoute(builder: (setting) {// return HeroPage(arguments: {// "imageUrl": value['imageUrl'],// "description": value['title'],// });return HeroPage2(arguments: {"imageUrl": value['imageUrl'],"description": value['title'],});}));},child: Container(decoration: BoxDecoration(border: Border.all(color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),child: Column(children: <Widget>[Hero(tag: value['title'],// child: Image.asset(value['imageUrl'])),child: Image.network(value['imageUrl'],width: 320,height: 120,fit: BoxFit.cover,),),const SizedBox(height: 12),Text(value['title'],textAlign: TextAlign.center,style: const TextStyle(fontSize: 20),)],)),);});return tempList.toList();}@overrideWidget build(BuildContext context) {return GridView.count(crossAxisSpacing: 10.0,//水平子 Widget 之間間距mainAxisSpacing: 10.0,//垂直子 Widget 之間間距padding: const EdgeInsets.all(10),crossAxisCount: 2,//一行的 Widget 數(shù)量// childAspectRatio:0.7,// 寬度和高度的比例children: _getListData(),);}
}
hero2.dart:
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';class HeroPage2 extends StatefulWidget {final Map arguments;const HeroPage2({super.key, required this.arguments});@overrideState<HeroPage2> createState() => _HeroPageState();
}class _HeroPageState extends State<HeroPage2> {@overrideWidget build(BuildContext context) {return GestureDetector(onTap: () {Navigator.pop(context);},child: Hero(tag: widget.arguments["imageUrl"],child: Scaffold(backgroundColor: Colors.black,body: Center(child: AspectRatio(aspectRatio: 16 / 9,child: PhotoView(imageProvider: NetworkImage(widget.arguments["imageUrl"]),),),),),),);}
}