为大家倾力打造的课程《Flutter从入门到进阶-实战携程网App》上线了,解锁Flutter开发新姿势,一网打尽Flutter核心技术 点我Get!!!
在这篇文章中,将向大家分享Flutter动画中的重要一员Hero动画
,以及一些Hero动画的开发技巧和经验
。
精心设计的动画会让用户界面感觉更直观、流畅,能改善用户体验。 Flutter的动画支持可以轻松实现各种动画类型。许多widget,特别是Material Design widgets, 都带有在其设计规范中定义的标准动画效果,但也可以自定义这些效果,在开始学习之前呢,我们先来快速过一下本篇文章的目录:
目录
什么是Hero动画?
在 Flutter中可以用 Hero
widget创建这个动画。当 Hero 通过动画从源页面飞到目标页面时,目标页面逐渐淡入视野。通常, Hero
是用户界面的一小部分,如图片,它通常在两个页面都有。从用户的角度来看, Hero
在页面之间“飞翔”。接下来我们一起来学习如何创建Hero动画:
实现标准Hero动画
...
class PhotoHero extends StatelessWidget {
const PhotoHero({ Key key, this.photo, this.onTap, this.width }) : super(key: key);
final String photo;
final VoidCallback onTap;
final double width;
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: Hero(
tag: photo,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: Image.network(
photo,
fit: BoxFit.contain,
),
),
),
),
);
}
}
class HeroAnimation extends StatelessWidget {
Widget build(BuildContext context) {
timeDilation = 10.0; // 1.0 means normal animation speed.
return Scaffold(
appBar: AppBar(
title: const Text('Basic Hero Animation'),
),
body: Center(
child: PhotoHero(
photo: 'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/Hero_animation/images/flippers-alpha.png',
width: 300.0,
onTap: () {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flippers Page'),
),
body: Container(
// Set background to blue to emphasize that it's a new route.
color: Colors.lightBlueAccent,
padding: const EdgeInsets.all(16.0),
alignment: Alignment.topLeft,
child: PhotoHero(
photo: 'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/Hero_animation/images/flippers-alpha.png',
width: 100.0,
onTap: () {
Navigator.of(context).pop();
},
),
),
);
}
));
},
),
),
);
}
}
...
Hero的函数原型
const Hero({
Key key,
@required this.tag,
this.createRectTween,
this.flightShuttleBuilder,
this.placeholderBuilder,
this.transitionOnUserGestures = false,
@required this.child,
}) : assert(tag != null),
assert(transitionOnUserGestures != null),
assert(child != null),
super(key: key);
- tag:[必须]用于关联两个Hero动画的标识;
- createRectTween:[可选]定义目标Hero的边界,在从起始位置到目的位置的“飞行”过程中该如何变化;
- child:[必须]定义动画所呈现的widget;
实现径向Hero动画
...
class RadialExpansionDemo extends StatelessWidget {
static const double kMinRadius = 32.0;
static const double kMaxRadius = 128.0;
static const opacityCurve = const Interval(0.0, 0.75, curve: Curves.fastOutSlowIn);
static RectTween _createRectTween(Rect begin, Rect end) {
return MaterialRectCenterArcTween(begin: begin, end: end);
}
static Widget _buildPage(BuildContext context, String imageName, String description) {
return Container(
color: Theme.of(context).canvasColor,
child: Center(
child: Card(
elevation: 8.0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: kMaxRadius * 2.0,
height: kMaxRadius * 2.0,
child: Hero(
createRectTween: _createRectTween,
tag: imageName,
child: RadialExpansion(
maxRadius: kMaxRadius,
child: Photo(
photo: imageName,
onTap: () {
Navigator.of(context).pop();
},
),
),
),
),
Text(
description,
style: TextStyle(fontWeight: FontWeight.bold),
textScaleFactor: 3.0,
),
const SizedBox(height: 16.0),
],
),
),
),
);
}
Widget _buildHero(BuildContext context, String imageName, String description) {
return Container(
width: kMinRadius * 2.0,
height: kMinRadius * 2.0,
child: Hero(
createRectTween: _createRectTween,
tag: imageName,
child: RadialExpansion(
maxRadius: kMaxRadius,
child: Photo(
photo: imageName,
onTap: () {
Navigator.of(context).push(
PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return Opacity(
opacity: opacityCurve.transform(animation.value),
child: _buildPage(context, imageName, description),
);
}
);
},
),
);
},
),
),
),
);
}
@override
Widget build(BuildContext context) {
timeDilation = 5.0; // 1.0 is normal animation speed.
return Scaffold(
appBar: AppBar(
title: const Text('Radial Transition Demo'),
),
body: Container(
padding: const EdgeInsets.all(32.0),
alignment: FractionalOffset.bottomLeft,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildHero(context, 'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/radial_Hero_animation/images/chair-alpha.png', 'Chair'),
_buildHero(context, 'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/radial_Hero_animation/images/binoculars-alpha.png', 'Binoculars'),
_buildHero(context, 'https://raw.githubusercontent.com/flutter/website/master/examples/_animation/radial_Hero_animation/images/beachball-alpha.png', 'Beach ball'),
],
),
),
);
}
}
...