在Flutter Animation动画开发之——AnimatedWidget这篇文章中我们介绍了AnimatedWidget的使用方法。今天要介绍的AnimatedBuilder其实是继承AnimatedWidget,所以功能与其类似,也是无需手动调用addListener监听动画然后调用setState来更新UI。其源码很简单,先做个简单介绍:
class AnimatedBuilder extends AnimatedWidget {
/// Creates an animated builder.
///
/// The [animation] and [builder] arguments must not be null.
const AnimatedBuilder({
Key key,
@required Listenable animation,
@required this.builder,
this.child,
}) : assert(animation != null),
assert(builder != null),
super(key: key, listenable: animation);
/// Called every time the animation changes value.
final TransitionBuilder builder;
/// The child widget to pass to the [builder].
///
/// If a [builder] callback's return value contains a subtree that does not
/// depend on the animation, it's more efficient to build that subtree once
/// instead of rebuilding it on every animation tick.
///
/// If the pre-built subtree is passed as the [child] parameter, the
/// [AnimatedBuilder] will pass it back to the [builder] function so that it
/// can be incorporated into the build.
///
/// Using this pre-built child is entirely optional, but can improve
/// performance significantly in some cases and is therefore a good practice.
final Widget child;
@override
Widget build(BuildContext context) {
return builder(context, child);
}
}
下面的代码演示了一个绿色方块,中间显示“野猿新一”四个字,方块的宽高从100变成200
class AnimationRoute extends StatefulWidget {
@override
AnimationRouteState createState() => AnimationRouteState();
}
class AnimationRouteState extends State with SingleTickerProviderStateMixin {
Animation animation;
AnimationController controller;
initState() {
super.initState();
// Controller设置动画时长
// vsync设置一个TickerProvider,当前State 混合了SingleTickerProviderStateMixin就是一个TickerProvider
controller = AnimationController(
duration: Duration(seconds: 5),
vsync: this //
);
// Tween设置动画的区间值,animate()方法传入一个Animation,AnimationController继承Animation
animation = new Tween(begin: 100.0, end: 200.0).animate(controller);
//启动动画(正向执行)
controller.forward();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (BuildContext ctx, Widget child) {
return Center(
child: Container(
color: Colors.green,
alignment: Alignment.center,
width: animation.value,
height: animation.value,
child: Text('野猿新一',
style: TextStyle(
color: Colors.black,
fontSize: 18.0,
),
),
),
);
}
);
}
@override
void dispose() {
// 释放资源
controller.dispose();
super.dispose();
}
}
方块中的Text('野猿新一')其实是不被动画影响的,因为动画只影响方块的宽高,但是上面代码的写法中当动画每次更新还是会重绘该Text。上面源码解析中我们提到AnimatedBuilder中还有一个child参数,可以传入不被动画影响的子控件树,我们可以把该Text传给child,这样改控件只会绘制一次,可以提高效率。
build方法修改如下,将Text('野猿新一')传给AnimatedBuilder的child参数,然后Container的child参数直接引用AnimatedBuilder的child
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
child: Text('野猿新一',
style: TextStyle(
color: Colors.black,
fontSize: 18.0,
),
),
builder: (BuildContext ctx, Widget child) {
return Center(
child: Container(
color: Colors.green,
alignment: Alignment.center,
width: animation.value,
height: animation.value,
child: child,
),
);
}
);
}