欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Flutter Animation动画开发之——AnimationStatusListener动画状态监听

程序员文章站 2022-03-09 20:15:03
...

源码分析

在之前的文章中我们介绍过Animation的addListener可以用来监听动画每一帧的变化

而如果要监听动画的状态变化,就要用到Animation的void addStatusListener(AnimationStatusListener listener)方法

addStatusListener方法是定义在AnimationLocalStatusListenersMixin中的,Animation抽象类的实现都有withAnimationLocalStatusListenersMixin,比如AnimationController

class AnimationController extends Animation<double>
  with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {

}

我们再看下withAnimationLocalStatusListenersMixin的源码

  • didRegisterListener():在添加动画状态监听前调用,该方法需融合withAnimationLocalStatusListenersMixin的类来实现

  • addStatusListener(AnimationStatusListener listener):添加动画状态监听

  • removeStatusListener(AnimationStatusListener listener):移除动画状态监听

  • didUnregisterListener():在移除动画状态监听后调用,该方法需融合withAnimationLocalStatusListenersMixin的类来实现

  • notifyStatusListeners(AnimationStatus status):动画状态变化的通知,通知所有注册的AnimationStatusListener

/// A mixin that implements the addStatusListener/removeStatusListener protocol
/// and notifies all the registered listeners when notifyStatusListeners is
/// called.
///
/// This mixin requires that the mixing class provide methods [didRegisterListener]
/// and [didUnregisterListener]. Implementations of these methods can be obtained
/// by mixing in another mixin from this library, such as [AnimationLazyListenerMixin].
mixin AnimationLocalStatusListenersMixin {
  final ObserverList<AnimationStatusListener> _statusListeners = ObserverList<AnimationStatusListener>();

  /// Called immediately before a status listener is added via [addStatusListener].
  ///
  /// At the time this method is called the registered listener is not yet
  /// notified by [notifyStatusListeners].
  void didRegisterListener();

  /// Called immediately after a status listener is removed via [removeStatusListener].
  ///
  /// At the time this method is called the removed listener is no longer
  /// notified by [notifyStatusListeners].
  void didUnregisterListener();

  /// Calls listener every time the status of the animation changes.
  ///
  /// Listeners can be removed with [removeStatusListener].
  void addStatusListener(AnimationStatusListener listener) {
    didRegisterListener();
    _statusListeners.add(listener);
  }

  /// Stops calling the listener every time the status of the animation changes.
  ///
  /// Listeners can be added with [addStatusListener].
  void removeStatusListener(AnimationStatusListener listener) {
    final bool removed = _statusListeners.remove(listener);
    if (removed) {
      didUnregisterListener();
    }
  }

  /// Calls all the status listeners.
  ///
  /// If listeners are added or removed during this function, the modifications
  /// will not change which listeners are called during this iteration.
  void notifyStatusListeners(AnimationStatus status) {
    final List<AnimationStatusListener> localListeners = List<AnimationStatusListener>.from(_statusListeners);
    for (AnimationStatusListener listener in localListeners) {
      try {
        if (_statusListeners.contains(listener))
          listener(status);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'animation library',
          context: ErrorDescription('while notifying status listeners for $runtimeType'),
          informationCollector: () sync* {
            yield DiagnosticsProperty<AnimationLocalStatusListenersMixin>(
              'The $runtimeType notifying status listeners was',
              this,
              style: DiagnosticsTreeStyle.errorProperty,
            );
          },
        ));
      }
    }
  }
}

接下来我们在看下AnimationStatus的源码,AnimationStatus是一个枚举类,共定义了如下几种动画状态

  •   dismissed:回到动画起点处
  •   forward:从起点往终点方向执行
  •   reverse:从终点往起点反方向执行
  •   completed:到达动画终点处
/// The status of an animation
enum AnimationStatus {
  /// The animation is stopped at the beginning
  dismissed,

  /// The animation is running from beginning to end
  forward,

  /// The animation is running backwards, from end to beginning
  reverse,

  /// The animation is stopped at the end
  completed,
}

示例代码

看完源码分析,接下我我们写一个示例跑一下

一下示例中演示了一个绿色的方块,宽高从100变化到200,由于监听了动画的状态,当动画执行到终点时会反向执行,当反向执行到起点时又会再正向执行,如此循环往复就可以实现无限循环动画(当然,AnimationController.repeat()也可实现动画循环播放)

绿色方块汇总还有两个Text

当动画到达起点时第一个Text显示“起点”,当动画到达终点时第一个Text显示“终点”

当动画往前正向执行时第二个Text显示“正向”,当动画往后反向执行时第二个Text显示“反向”

大家可以直接拷贝如下整段代码运行看下效果

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: AnimationRoute(),
    );
  }
}

class AnimationRoute extends StatefulWidget {
  @override
  AnimationRouteState createState() => AnimationRouteState();
}

class AnimationRouteState extends State<AnimationRoute> with SingleTickerProviderStateMixin {

  AnimationController controller;
  String position = '';
  String direction = '';

  initState() {
    super.initState();
    // Controller设置动画时长
    // vsync设置一个TickerProvider,当前State 混合了SingleTickerProviderStateMixin就是一个TickerProvider
    controller = AnimationController(
        lowerBound: 100,
        upperBound: 200,
        duration: Duration(seconds: 5),
        vsync: this //
    )..addStatusListener((status){
        if (status == AnimationStatus.dismissed) {
          controller.forward();
          setState(() {
            position = '起点';
          });
        } else if (status == AnimationStatus.forward) {
          setState(() {
            direction = '正向';
          });
        } else if (status == AnimationStatus.reverse) {
          setState(() {
            direction = '反向';
          });
        } else if (status == AnimationStatus.completed) {
          controller.reverse();
          setState(() {
            position = '终点';
          });
        }
      });
    //启动动画(正向执行)
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: controller,
        builder: (BuildContext ctx, Widget child) {
          return Center(
            child: Container(
              color: Colors.green,
              alignment: Alignment.center,
              width: controller.value,
              height: controller.value,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(position,
                    style: TextStyle(
                      color: Colors.black,
                      fontSize: 18.0,
                    ),
                  ),
                  Text(direction,
                    style: TextStyle(
                      color: Colors.black,
                      fontSize: 18.0,
                    ),
                  )
                ],
              ),
            ),
          );
        }
    );
  }

  @override
  void dispose() {
    // 释放资源
    controller.dispose();
    super.dispose();
  }
}