一、AnimatedContainer

Flutter 中实现动画的方式有很多,AnimatedContainer 是实现渐变动画的一种方式,Flutter 中称为 隐式动画

实际上 AnimatedContainer 的行为表现,更像是一种渐变过程,它会对新旧属性的值变化之间生成动画效果

AnimatedContainer 的使用上,一般需要配合 StatefulWidget 来保存不同的 state 状态

1、创建默认属性的 StatefulWidget

double _opacity = 1.0;
Matrix4 _transform = Matrix4.translationValues(0, 0, 0);

上面设置了两个 state ,分别是透明值和位置变换,位置变换需要使用 matrix 类型进行计算

2、 AnimatedContainer Widget

AnimatedContainer 的构造如下,其中 duration 是 required 的,其他都是一些属性,比如 margin/transform/width/height/color/

AnimatedContainer 会在 state 变化的时候,去进行一些区间动画

  AnimatedContainer({
    Key key,
    this.alignment,
    this.padding,
    Color color,
    Decoration decoration,
    this.foregroundDecoration,
    double width,
    double height,
    BoxConstraints constraints,
    this.margin,
    this.transform,
    this.child,
    Curve curve = Curves.linear,
    @required Duration duration,
    Duration reverseDuration,
  })

AnimatedContainer 对 state 更新是借助 ImplicitlyAnimatedWidgetState 生命周期的控制中实现的,其中在 didUpdatedWidget 的时候,会去依次遍历目标 value,如果需要进行渐变,则会依赖 Curve.transform 做动画。

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: widget.duration,
      reverseDuration: widget.reverseDuration,
      debugLabel: kDebugMode ? '${widget.toStringShort()}' : null,
      vsync: this,
    );
    _updateCurve();
    _constructTweens();
    didUpdateTweens();
  }

  @override
  void didUpdateWidget(T oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.curve != oldWidget.curve)
      _updateCurve();
    _controller.duration = widget.duration;
    _controller.reverseDuration = widget.reverseDuration;
    if (_constructTweens()) {
      forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
        _updateTween(tween, targetValue);
        return tween;
      });
      _controller
        ..value = 0.0
        ..forward();
      didUpdateTweens();
    }
  }

3、使用 AnimatedContainer

创建一个 AnimatedContainer,将 transform 和 opacity 传入,设置 duration 时间是 500 ms

AnimatedContainer(
  duration: Duration(milliseconds: 500),
  transform: _transform,
  child: AnimatedOpacity(
    opacity: _opacity,
    duration: Duration(milliseconds: 500),
    child: Container(
      alignment: Alignment.center,
      width: 100,
      height: 50,
      color: Colors.pink,
    ),
  ),
)

4、改变默认的 样式

这里可以通过点击去改变

  void _onTapHandle() {
    setState(() {
      _opacity = _opacity == 1.0 ? 0.0 : 1.0;
      _transform = _opacity == 0.0
          ? Matrix4.translationValues(-300, 0, 0)
          : Matrix4.translationValues(0, 0, 0);
    });
  }

5、最终的效果

在右下角我放了一个按钮,点击这个按钮,会改变上面的 Container 的动画效果

GIF.gif

二、官方的 AnimatedContainer 动画

下面代码实现的是 CookBook 的渐变动画效果 https://flutter-io.cn/docs/cookbook/animation/animated-container.html

1、初始化 state

  double _width = 60;
  double _height = 60;
  Color _color = Colors.pink;
  Matrix4 _transform = null;
  BorderRadius _borderRadius = BorderRadius.circular(8);

2、设置 state

CookBook 设置 state 的时候,更多的是区间随机,从颜色到宽度高度、甚至是边框圆角以及位置都是随机的,所以更能体现 AnimatedContainer 的效果

  void _onTapHandle() {
    Random random = new Random();
    setState(() {
      _width = random.nextInt(300).toDouble();
      _height = random.nextInt(300).toDouble();
      _borderRadius = BorderRadius.circular(random.nextInt(300).toDouble());
      _color = Color.fromARGB(
          255, random.nextInt(256), random.nextInt(256), random.nextInt(256));
      _transform = Matrix4.translationValues(random.nextInt(50).toDouble(),
          random.nextInt(50).toDouble(), random.nextInt(50).toDouble());
    });
  }

3、效果:

GIF2.gif

完整代码