從源碼解析Flutter的動(dòng)畫業(yè)務(wù)邏輯

概述

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;
    }());
  }


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容