概述
Flutter提供了丰富的动画形式,其中Curve负责动画切换路径,Tween用于构造动画的插值方式,AnimationController控制组件动画。AnimationController接收一个TickerProvider(抽象类)的对象,控制其动画。普通的Widget并不是TickerProvider的实现实例,因此需要通过mixin的方式,如下所示:
class FontAnimation extends StatefulWidget {
FontAnimation({Key key}) : super(key: key);
@override
_FontAnimationState createState() => _FontAnimationState();
}
//mixin SingleTickerProviderStateMixin实现TickerProvider接口
class _FontAnimationState extends State with SingleTickerProviderStateMixin{
Animation tween;
AnimationController animationController;
@override
void initState() {
super.initState();
//vsync参数需要是一个TickerProvider的实现对象
animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 2000));
}
//...代码省略
AnimatedBuilder类
AnimatedBuilder类继承于AnimatedWidget,AnimatedWidget继承自StatefulWidget。AnimatedBuilder构造函数接收一个实现了抽象类Listenable的对象animation,以及一个builder方法,其中builder方法会在AnimatedBuilder的build方法中调用。每次animation的值改变时就会调用build方法(实际调用了传递过来的builder方法),从而实现动画。AniamtedBuilder的代码很简单,如下所示:
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);
}
}
注意构造函数的child是可选的,但是注释中建议如果builder方法返回值不依赖于动画,则建议通过构造函数的child参数设置,以免每次动画时都通过builder方法重绘与动画无关的组件。
**
AnimatedWidget类
AnimatedWidget是一个抽象类,主要是通过listenable增加listenable值变化的监听处理,这里只是简单地通过setState通知重绘组建。
abstract class AnimatedWidget extends StatefulWidget {
/// Creates a widget that rebuilds when the given listenable changes.
///
/// The [listenable] argument is required.
const AnimatedWidget({
Key key,
@required this.listenable,
}) : assert(listenable != null),
super(key: key);
/// The [Listenable] to which this widget is listening.
///
/// Commonly an [Animation] or a [ChangeNotifier].
final Listenable listenable;
/// Override this method to build widgets that depend on the state of the
/// listenable (e.g., the current value of the animation).
@protected
Widget build(BuildContext context);
/// Subclasses typically do not override this method.
@override
_AnimatedState createState() => _AnimatedState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty('animation', listenable));
}
}
class _AnimatedState extends State {
@override
void initState() {
super.initState();
widget.listenable.addListener(_handleChange);
}
@override
void didUpdateWidget(AnimatedWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.listenable != oldWidget.listenable) {
oldWidget.listenable.removeListener(_handleChange);
widget.listenable.addListener(_handleChange);
}
}
@override
void dispose() {
widget.listenable.removeListener(_handleChange);
super.dispose();
}
void _handleChange() {
setState(() {
// The listenable's state is our build state, and it changed already.
});
}
@override
Widget build(BuildContext context) => widget.build(context);
}
AnimationController类
AnimationController继承自Animation
AnimationController({
double value,
this.duration,
this.reverseDuration,
this.debugLabel,
this.lowerBound = 0.0,
this.upperBound = 1.0,
this.animationBehavior = AnimationBehavior.normal,
@required TickerProvider vsync,
}) : assert(lowerBound != null),
assert(upperBound != null),
assert(upperBound >= lowerBound),
assert(vsync != null),
_direction = _AnimationDirection.forward {
//调用TickerProvider绑定定时执行方法_tick
_ticker = vsync.createTicker(_tick);
_internalSetValue(value ?? lowerBound);
}
//...
void _tick(Duration elapsed) {
_lastElapsedDuration = elapsed;
final double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.microsecondsPerSecond;
assert(elapsedInSeconds >= 0.0);
_value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound);
if (_simulation.isDone(elapsedInSeconds)) {
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.completed :
AnimationStatus.dismissed;
stop(canceled: false);
}
notifyListeners();
_checkStatusChanged();
}
Animation的Tween或CurvedAnimation等动画插值类通过指定一个parent绑定到AnimationController,实际时执行了AnimationController的addListener和addStatusListener方法绑定Animation监听回调方法,当AnimationController中的定时器导致AnimationController的value变更时,可以通过AnimationController的监听回调同时变更Animation的动画插值。只是不同的动画插值新插入的值的算法不同。
在_tick方法中有notifyListeners的调用,事实上在设置value时也有调用,notifyListener定义在
AnimationLocalListenersMixin,是一个mixin类型,方便需要的其他类直接使用。从notifyListener方法可以看到,实际上就是遍历全部的listener监听器,调用对应的callback回调方法。
void notifyListeners() {
final List localListeners = List.from(_listeners);
for (VoidCallback listener in localListeners) {
try {
if (_listeners.contains(listener))
listener();
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'animation library',
context: ErrorDescription('while notifying listeners for $runtimeType'),
informationCollector: () sync* {
yield DiagnosticsProperty(
'The $runtimeType notifying listeners was',
this,
style: DiagnosticsTreeStyle.errorProperty,
);
},
));
}
}
}
}
AnimationController的dispose方法用于释放资源,其实是调用定时器ticker(TickerProvider)的dispose方法取消可能还在执行的TickerFuture任务。
//AnimationController
void dispose() {
assert(() {
if (_ticker == null) {
throw FlutterError.fromParts([
ErrorSummary('AnimationController.dispose() called more than once.'),
ErrorDescription('A given $runtimeType cannot be disposed more than once.\n'),
DiagnosticsProperty(
'The following $runtimeType object was disposed multiple times',
this,
style: DiagnosticsTreeStyle.errorProperty,
),
]);
}
return true;
}());
_ticker.dispose();
_ticker = null;
super.dispose();
}
//TickerProvider的dispose方法
void dispose() {
if (_future != null) {
final TickerFuture localFuture = _future;
_future = null;
assert(!isActive);
unscheduleTick();
localFuture._cancel(this);
}
assert(() {
// We intentionally don't null out _startTime. This means that if start()
// was ever called, the object is now in a bogus state. This weakly helps
// catch cases of use-after-dispose.
_startTime = Duration.zero;
return true;
}());
}