概述
Flutter提供了豐富的動(dòng)畫形式,其中Curve負(fù)責(zé)動(dòng)畫切換路徑,Tween用于構(gòu)造動(dòng)畫的插值方式,AnimationController控制組件動(dòng)畫。AnimationController接收一個(gè)TickerProvider(抽象類)的對(duì)象,控制其動(dòng)畫。普通的Widget并不是TickerProvider的實(shí)現(xiàn)實(shí)例,因此需要通過mixin的方式,如下所示:
class FontAnimation extends StatefulWidget {
FontAnimation({Key key}) : super(key: key);
@override
_FontAnimationState createState() => _FontAnimationState();
}
//mixin SingleTickerProviderStateMixin實(shí)現(xiàn)TickerProvider接口
class _FontAnimationState extends State<FontAnimation> with SingleTickerProviderStateMixin{
Animation<double> tween;
AnimationController animationController;
@override
void initState() {
super.initState();
//vsync參數(shù)需要是一個(gè)TickerProvider的實(shí)現(xiàn)對(duì)象
animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 2000));
}
//...代碼省略
AnimatedBuilder類
AnimatedBuilder類繼承于AnimatedWidget,AnimatedWidget繼承自StatefulWidget。AnimatedBuilder構(gòu)造函數(shù)接收一個(gè)實(shí)現(xiàn)了抽象類Listenable的對(duì)象animation,以及一個(gè)builder方法,其中builder方法會(huì)在AnimatedBuilder的build方法中調(diào)用。每次animation的值改變時(shí)就會(huì)調(diào)用build方法(實(shí)際調(diào)用了傳遞過來的builder方法),從而實(shí)現(xiàn)動(dòng)畫。AniamtedBuilder的代碼很簡(jiǎn)單,如下所示:
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);
}
}
注意構(gòu)造函數(shù)的child是可選的,但是注釋中建議如果builder方法返回值不依賴于動(dòng)畫,則建議通過構(gòu)造函數(shù)的child參數(shù)設(shè)置,以免每次動(dòng)畫時(shí)都通過builder方法重繪與動(dòng)畫無關(guān)的組件。
**
AnimatedWidget類
AnimatedWidget是一個(gè)抽象類,主要是通過listenable增加listenable值變化的監(jiān)聽處理,這里只是簡(jiǎn)單地通過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<Listenable>('animation', listenable));
}
}
class _AnimatedState extends State<AnimatedWidget> {
@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<double>,在構(gòu)造函數(shù)中接收一個(gè)TickerProvider對(duì)象,并調(diào)用TickerProvider的createTicker綁定一個(gè)動(dòng)畫執(zhí)行方法_tick。_tick方法接收一個(gè)定時(shí)過去的時(shí)間elapsed參數(shù),通過該參數(shù)計(jì)算當(dāng)前動(dòng)畫進(jìn)行的值(如果存在邊界會(huì)被截?cái)啵?,如果?dòng)畫結(jié)束則更新狀態(tài)為完成(AnimationStatus.completed)或消失(AnimationStatus.dismissed)。同時(shí)通知?jiǎng)赢嫳O(jiān)聽器動(dòng)畫值已經(jīng)改變。同時(shí)調(diào)用_checkStatusChanged方法更新狀態(tài)(該方法會(huì)檢測(cè)狀態(tài)變化再更新)。
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 {
//調(diào)用TickerProvider綁定定時(shí)執(zhí)行方法_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等動(dòng)畫插值類通過指定一個(gè)parent綁定到AnimationController,實(shí)際時(shí)執(zhí)行了AnimationController的addListener和addStatusListener方法綁定Animation監(jiān)聽回調(diào)方法,當(dāng)AnimationController中的定時(shí)器導(dǎo)致AnimationController的value變更時(shí),可以通過AnimationController的監(jiān)聽回調(diào)同時(shí)變更Animation的動(dòng)畫插值。只是不同的動(dòng)畫插值新插入的值的算法不同。
在_tick方法中有notifyListeners的調(diào)用,事實(shí)上在設(shè)置value時(shí)也有調(diào)用,notifyListener定義在
AnimationLocalListenersMixin,是一個(gè)mixin類型,方便需要的其他類直接使用。從notifyListener方法可以看到,實(shí)際上就是遍歷全部的listener監(jiān)聽器,調(diào)用對(duì)應(yīng)的callback回調(diào)方法。
void notifyListeners() {
final List<VoidCallback> localListeners = List<VoidCallback>.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<AnimationLocalListenersMixin>(
'The $runtimeType notifying listeners was',
this,
style: DiagnosticsTreeStyle.errorProperty,
);
},
));
}
}
}
}
AnimationController的dispose方法用于釋放資源,其實(shí)是調(diào)用定時(shí)器ticker(TickerProvider)的dispose方法取消可能還在執(zhí)行的TickerFuture任務(wù)。
//AnimationController
void dispose() {
assert(() {
if (_ticker == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('AnimationController.dispose() called more than once.'),
ErrorDescription('A given $runtimeType cannot be disposed more than once.\n'),
DiagnosticsProperty<AnimationController>(
'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;
}());
}