开篇寄语
这一篇文章主要是学习flutter中各种Animation,也就是各种动画效果,比如旋转图片,左旋转,右旋转,上下颠倒,放大,缩小,隐藏,显示,旋转的圆等等之类的,会让整个App在运行过程中显得更加流畅,生动,可以达到增加用户留存度,常驻时间可以延长等作用。
前情提要
- 《Flutter制作自己的第一个全平台APP学习之Widgets篇》
- 《Flutter制作自己的第一个全平台APP学习之Layouts篇》
- 《Flutter制作自己的第一个全平台APP学习之Navigation篇》
- 《Flutter制作自己的第一个全平台APP学习之Popup篇》
- 《Flutter制作自己的第一个全平台APP学习之多媒体篇》
内容详情
以下内容大多是更改lib下的main.dart文件内容,删掉里面的内容,复制粘贴代码,开启调试就可以出现了。
hero
- 从源路线跳转到目标地路线的小部件。为源路由定义一个Hero,为目标路由定义另一个,并为每个角色分配相同的标签,试举例如下:
import 'package:flutter/material.dart'; void main() => runApp(HeroApp()); class HeroApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Transition Demo', home: MainScreen(), ); } } class MainScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Main Screen'), ), body: GestureDetector( onTap: () { Navigator.push(context, MaterialPageRoute(builder: (_) { return DetailScreen(); })); }, child: Hero( tag: 'imageHero', child: Image.network( 'https://picsum.photos/250?image=9', ), ), ), ); } } class DetailScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: GestureDetector( onTap: () { Navigator.pop(context); }, child: Center( child: Hero( tag: 'imageHero', child: Image.network( 'https://picsum.photos/250?image=9', ), ), ), ), ); } }
生成效果如下图所示:
AnimateOpacity
- 透明度变低,变高,任意变换,试举例如下:
import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Woolha.com Flutter Tutorial', home: _AnimatedOpacityExample(), ); } } class _AnimatedOpacityExample extends StatefulWidget { @override State<StatefulWidget> createState() { return _AnimatedOpacityExampleState(); } } class _AnimatedOpacityExampleState extends State<_AnimatedOpacityExample> { double _opacity = 0.1; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Woolha.com Flutter Tutorial'), ), body: SizedBox( width: double.infinity, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ AnimatedOpacity( opacity: _opacity, duration: const Duration(seconds: 3), curve: Curves.bounceInOut, onEnd: () { print('Animation ends'); }, child: Container( width: 300, height: 50, color: Colors.redAccent, child: const Center( child: const Text('luckydesigner.space', style: const TextStyle(fontSize: 24, color: Colors.white)), ), ), ), ElevatedButton( child: const Text('Change opacity'), onPressed: () { setState(() { _opacity = _opacity == 0.1 ? 1 : 0.1; }); }, ), ], ), ), ); } }
生成的效果如下图所示:
点击前 点击后
AnimatedContainer
- Container容器的动画效果,效果复杂多变,试举例如下:
/// Flutter code sample for AnimatedContainer // The following example (depicted above) transitions an AnimatedContainer // between two states. It adjusts the `height`, `width`, `color`, and // [alignment] properties when tapped. import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); /// This is the main application widget. class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); static const String _title = 'Flutter Code Sample'; @override Widget build(BuildContext context) { return MaterialApp( title: _title, home: Scaffold( appBar: AppBar(title: const Text(_title)), body: const MyStatefulWidget(), ), ); } } /// This is the stateful widget that the main application instantiates. class MyStatefulWidget extends StatefulWidget { const MyStatefulWidget({Key? key}) : super(key: key); @override State<MyStatefulWidget> createState() => _MyStatefulWidgetState(); } /// This is the private State class that goes with MyStatefulWidget. class _MyStatefulWidgetState extends State<MyStatefulWidget> { bool selected = false; @override Widget build(BuildContext context) { return GestureDetector( onTap: () { setState(() { selected = !selected; }); }, child: Center( child: AnimatedContainer( width: selected ? 200.0 : 100.0, height: selected ? 100.0 : 200.0, color: selected ? Colors.red : Colors.blue, alignment: Alignment.center, duration: const Duration(seconds: 2), curve: Curves.fastOutSlowIn, child: const FlutterLogo(size: 75), ), ), ); } }
生成效果如下图所示:
点击前 点击后
Transform
- 包括旋转,缩放和移动,试举例如下:
旋转,代码如下:
import 'dart:math' as math; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: _AnimatedWidgetExample(), ); } } class _AnimatedWidgetExample extends StatefulWidget { @override State<StatefulWidget> createState() => new _AnimatedWidgetExampleState(); } class _AnimatedWidgetExampleState extends State<_AnimatedWidgetExample> with TickerProviderStateMixin { late AnimationController _controller; void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, )..repeat(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Demo'), ), body: Center( child: RotatingSquare(controller: _controller), ), ); } } class RotatingSquare extends AnimatedWidget { const RotatingSquare({Key? key, required AnimationController controller}) : super(key: key, listenable: controller); Animation<double> get _progress => listenable as Animation<double>; @override Widget build(BuildContext context) { return Transform.rotate( angle: _progress.value * 2.0 * math.pi, child: Container( width: 300.0, height: 300.0, color: Colors.teal, child: const Center( child: Text( 'Luckydesigner.space', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 24, ), ), ), ), ); } }
缩放,代码如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: _AnimatedWidgetExample(), ); } } class _AnimatedWidgetExample extends StatefulWidget { @override State<StatefulWidget> createState() => new _AnimatedWidgetExampleState(); } class _AnimatedWidgetExampleState extends State<_AnimatedWidgetExample> with TickerProviderStateMixin { late AnimationController _controller; void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, )..repeat(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Demo'), ), body: Center( child: RotatingSquare(controller: _controller), ), ); } } class RotatingSquare extends AnimatedWidget { const RotatingSquare({Key? key, required AnimationController controller}) : super(key: key, listenable: controller); Animation<double> get _progress => listenable as Animation<double>; @override Widget build(BuildContext context) { return Transform.scale( scale: _progress.value, child: Container( width: 300.0, height: 300.0, color: Colors.teal, child: const Center( child: Text( 'Luckydesigner.space', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 24, ), ), ), ), ); } }
移动,代码如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: _AnimatedWidgetExample(), ); } } class _AnimatedWidgetExample extends StatefulWidget { @override State<StatefulWidget> createState() => new _AnimatedWidgetExampleState(); } class _AnimatedWidgetExampleState extends State<_AnimatedWidgetExample> with TickerProviderStateMixin { late AnimationController _controller; void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, )..repeat(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Demo'), ), body: Center( child: RotatingSquare(controller: _controller), ), ); } } class RotatingSquare extends AnimatedWidget { const RotatingSquare({Key? key, required AnimationController controller}) : super(key: key, listenable: controller); Animation<double> get _progress => listenable as Animation<double>; @override Widget build(BuildContext context) { return Transform.translate( offset: Offset(0.0, _progress.value * 100), child: Container( width: 300.0, height: 300.0, color: Colors.teal, child: const Center( child: Text( 'Luckydesigner.space', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 24, ), ), ), ), ); } }
AnimatedList
- 此小部件的 AnimatedListState 可用于动态插入或删除项目。要引用 AnimatedListState,请提供 GlobalKey 或使用项目输入回调中的静态方法,试举例如下:
import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'ListView Example', home: AnimatedListExample(), ); } } class AnimatedListExample extends StatefulWidget { @override AnimatedListExampleState createState() => new AnimatedListExampleState(); } class AnimatedListExampleState extends State<AnimatedListExample> { final GlobalKey<AnimatedListState> _listKey = GlobalKey(); List<String> _data = [ WordPair.random().toString(), WordPair.random().toString(), WordPair.random().toString(), ]; @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('Animated List'), backgroundColor: Colors.blueAccent, ), persistentFooterButtons: <Widget>[ ElevatedButton( child: Text( 'Add an item', style: TextStyle(fontSize: 20, color: Colors.white), ), onPressed: () { _addAnItem(); }, ), ElevatedButton( child: Text( 'Remove last', style: TextStyle(fontSize: 20, color: Colors.white), ), onPressed: () { _removeLastItem(); }, ), ElevatedButton( child: Text( 'Remove all', style: TextStyle(fontSize: 20, color: Colors.white), ), onPressed: () { _removeAllItems(); }, ), ], body: AnimatedList( key: _listKey, initialItemCount: _data.length, itemBuilder: (context, index, animation) => _buildItem(context, _data[index], animation), ), ); } Widget _buildItem( BuildContext context, String item, Animation<double> animation) { TextStyle textStyle = new TextStyle(fontSize: 20); return Padding( padding: const EdgeInsets.all(2.0), child: SizeTransition( sizeFactor: animation, axis: Axis.vertical, child: SizedBox( height: 50.0, child: Card( child: Center( child: Text(item, style: textStyle), ), ), ), ), ); } void _addAnItem() { _data.insert(0, WordPair.random().toString()); _listKey.currentState?.insertItem(0); } void _removeLastItem() { String itemToRemove = _data[0]; _listKey.currentState?.removeItem( 0, (BuildContext context, Animation<double> animation) => _buildItem(context, itemToRemove, animation), duration: const Duration(milliseconds: 250), ); _data.removeAt(0); } void _removeAllItems() { final int itemCount = _data.length; for (var i = 0; i < itemCount; i++) { String itemToRemove = _data[0]; _listKey.currentState?.removeItem( 0, (BuildContext context, Animation<double> animation) => _buildItem(context, itemToRemove, animation), duration: const Duration(milliseconds: 250), ); _data.removeAt(0); } } }
生成效果如下图所示:
AnimatedCrossFade
- 一个小部件,它在两个给定的子页面交叉淡入淡出并在它们的大小之间进行动画处理,试举例如下:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: _AnimatedCrossFadeExample(), ); } } class _AnimatedCrossFadeExample extends StatefulWidget { @override _AnimatedCrossFadeExampleState createState() => new _AnimatedCrossFadeExampleState(); } class _AnimatedCrossFadeExampleState extends State<_AnimatedCrossFadeExample> { CrossFadeState _crossFadeState = CrossFadeState.showFirst; @override void initState() { super.initState(); Future.delayed(const Duration(seconds: 3), () { setState(() { _crossFadeState = CrossFadeState.showSecond; }); }); Future.delayed(const Duration(seconds: 5), () { setState(() { _crossFadeState = CrossFadeState.showFirst; }); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Demo'), ), body: Center( child: AnimatedCrossFade( crossFadeState: _crossFadeState, duration: const Duration(seconds: 2), reverseDuration: const Duration(seconds: 3), firstCurve: Curves.bounceInOut, secondCurve: Curves.easeInBack, firstChild: const Icon(Icons.text_rotate_up, size: 100), secondChild: const Icon(Icons.text_rotate_vertical, size: 200), layoutBuilder: (Widget topChild, Key topChildKey, Widget bottomChild, Key bottomChildKey) { return Stack( children: <Widget>[ Positioned( key: bottomChildKey, left: 100.0, top: 100.0, child: bottomChild, ), Positioned( key: topChildKey, child: topChild, ), ], ); }, ), ), ); } }
生成效果如下图所示:
AnimatedPadding
- Padding 的动画版本,只要给定的插图发生变化,它就会在给定的持续时间内自动转换缩进,官方版本给出的源代码,试举例如下:
/// Flutter code sample for AnimatedPadding // The following code implements the [AnimatedPadding] widget, using a [curve] of // [Curves.easeInOut]. import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); /// This is the main application widget. class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); static const String _title = 'Flutter Code Sample'; @override Widget build(BuildContext context) { return MaterialApp( title: _title, home: Scaffold( appBar: AppBar(title: const Text(_title)), body: const MyStatefulWidget(), ), ); } } /// This is the stateful widget that the main application instantiates. class MyStatefulWidget extends StatefulWidget { const MyStatefulWidget({Key? key}) : super(key: key); @override State<MyStatefulWidget> createState() => _MyStatefulWidgetState(); } /// This is the private State class that goes with MyStatefulWidget. class _MyStatefulWidgetState extends State<MyStatefulWidget> { double padValue = 0.0; void _updatePadding(double value) { setState(() { padValue = value; }); } @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ AnimatedPadding( padding: EdgeInsets.all(padValue), duration: const Duration(seconds: 2), curve: Curves.easeInOut, child: Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height / 5, color: Colors.blue, ), ), Text('Padding: $padValue'), ElevatedButton( child: const Text('Change padding'), onPressed: () { _updatePadding(padValue == 0.0 ? 100.0 : 0.0); }), ], ); } }
生成效果如下图所示:
SlideTransition
- 变速加载动作特效,可以使元素以不同速度展示,试以官方文档给出的例子以演示:
/// Flutter code sample for SlideTransition // The following code implements the [SlideTransition] as seen in the video // above: import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); /// This is the main application widget. class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); static const String _title = 'Flutter Code Sample'; @override Widget build(BuildContext context) { return MaterialApp( title: _title, home: Scaffold( appBar: AppBar(title: const Text(_title)), body: const Center( child: MyStatefulWidget(), ), ), ); } } /// This is the stateful widget that the main application instantiates. class MyStatefulWidget extends StatefulWidget { const MyStatefulWidget({Key? key}) : super(key: key); @override State<MyStatefulWidget> createState() => _MyStatefulWidgetState(); } /// This is the private State class that goes with MyStatefulWidget. class _MyStatefulWidgetState extends State<MyStatefulWidget> with SingleTickerProviderStateMixin { late final AnimationController _controller = AnimationController( duration: const Duration(seconds: 2), vsync: this, )..repeat(reverse: true); late final Animation<Offset> _offsetAnimation = Tween<Offset>( begin: Offset.zero, end: const Offset(1.5, 0.0), ).animate(CurvedAnimation( parent: _controller, curve: Curves.elasticIn, )); @override void dispose() { super.dispose(); _controller.dispose(); } @override Widget build(BuildContext context) { return SlideTransition( position: _offsetAnimation, child: const Padding( padding: EdgeInsets.all(8.0), child: FlutterLogo(size: 150.0), ), ); } }
生成的效果如下图所示:
flip card
- 纸牌翻转效果,实现它,需要安装一个名为flip card的支撑库,安装和使用地址可以到pub.dev查看。
- 试举例如下:
import 'package:flutter/material.dart'; import 'package:flip_card/flip_card.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Welcome to Flutter', home: Scaffold( appBar: AppBar( title: const Text('Welcome to Flutter'), ), body: Center( child: FlipCard( direction: FlipDirection.HORIZONTAL, // default front: FractionallySizedBox( widthFactor: 0.8, heightFactor: 0.3, alignment: Alignment.center, child: Container( color: Colors.purple, child: const Align( alignment: Alignment.center, child: Text( "front", textAlign: TextAlign.center, ))), ), back: FractionallySizedBox( widthFactor: 0.8, heightFactor: 0.3, alignment: Alignment.center, child: Container( color: Colors.red, child: const Align( alignment: Alignment.center, child: Text( "back", textAlign: TextAlign.center, ))), ), ), ), )); } }
生成效果如下图所示:
- 我的微信
- 微信扫一扫加好友
- 我的微信公众号
- 扫描关注公众号