概述
Flutter提供了豐富的動畫形式胁出,其中Curve負責(zé)動畫切換路徑移怯,Tween用于構(gòu)造動畫的插值方式烁设,AnimationController控制組件動畫。AnimationController接收一個TickerProvider(抽象類)的對象汪茧,控制其動畫。普通的Widget并不是TickerProvider的實現(xiàn)實例限番,因此需要通過mixin的方式陆爽,如下所示:
class FontAnimation extends StatefulWidget {
FontAnimation({Key key}) : super(key: key);
@override
_FontAnimationState createState() => _FontAnimationState();
}
//mixin SingleTickerProviderStateMixin實現(xiàn)TickerProvider接口
class _FontAnimationState extends State<FontAnimation> with SingleTickerProviderStateMixin{
Animation<double> tween;
AnimationController animationController;
@override
void initState() {
super.initState();
//vsync參數(shù)需要是一個TickerProvider的實現(xiàn)對象
animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 2000));
}
//...代碼省略
AnimatedBuilder類
AnimatedBuilder類繼承于AnimatedWidget,AnimatedWidget繼承自StatefulWidget扳缕。AnimatedBuilder構(gòu)造函數(shù)接收一個實現(xiàn)了抽象類Listenable的對象animation慌闭,以及一個builder方法别威,其中builder方法會在AnimatedBuilder的build方法中調(diào)用。每次animation的值改變時就會調(diào)用build方法(實際調(diào)用了傳遞過來的builder方法)驴剔,從而實現(xiàn)動畫省古。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);
}
}
注意構(gòu)造函數(shù)的child是可選的丧失,但是注釋中建議如果builder方法返回值不依賴于動畫豺妓,則建議通過構(gòu)造函數(shù)的child參數(shù)設(shè)置,以免每次動畫時都通過builder方法重繪與動畫無關(guān)的組件布讹。
**
AnimatedWidget類
AnimatedWidget是一個抽象類琳拭,主要是通過listenable增加listenable值變化的監(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ù)中接收一個TickerProvider對象,并調(diào)用TickerProvider的createTicker綁定一個動畫執(zhí)行方法_tick膘流。_tick方法接收一個定時過去的時間elapsed參數(shù)絮缅,通過該參數(shù)計算當前動畫進行的值(如果存在邊界會被截斷),如果動畫結(jié)束則更新狀態(tài)為完成(AnimationStatus.completed)或消失(AnimationStatus.dismissed)呼股。同時通知動畫監(jiān)聽器動畫值已經(jīng)改變耕魄。同時調(diào)用_checkStatusChanged方法更新狀態(tài)(該方法會檢測狀態(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綁定定時執(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等動畫插值類通過指定一個parent綁定到AnimationController彭谁,實際時執(zhí)行了AnimationController的addListener和addStatusListener方法綁定Animation監(jiān)聽回調(diào)方法吸奴,當AnimationController中的定時器導(dǎo)致AnimationController的value變更時,可以通過AnimationController的監(jiān)聽回調(diào)同時變更Animation的動畫插值缠局。只是不同的動畫插值新插入的值的算法不同奄抽。
在_tick方法中有notifyListeners的調(diào)用,事實上在設(shè)置value時也有調(diào)用甩鳄,notifyListener定義在
AnimationLocalListenersMixin逞度,是一個mixin類型,方便需要的其他類直接使用妙啃。從notifyListener方法可以看到档泽,實際上就是遍歷全部的listener監(jiān)聽器,調(diào)用對應(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方法用于釋放資源馆匿,其實是調(diào)用定時器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;
}());
}