Flutter基建 - 12种隐式动画小组件全解析

本篇基于Flutter 3.16.4,Dart 3.2.3版本

Flutter 3.16.4 • channel stable • Framework • revision 2e9cb0aa71 (3 days ago) • 2023-12-11 14:35:13 -0700

Engine • revision 54a7145303

Tools • Dart 3.2.3 • DevTools 2.28.4

本篇为Flutter基建的第九篇文章,文章会围绕着ImplicitylyAnimatedWidget隐式动画组件来分析,通过代码+Gif的形式将12种常用的动画呈现出来,隐式动画使用起来并不是很难,但它在日常开发中可以帮助我们解决一些小而美的视角效果,下面一起进入文章的了解下吧~

Flutter基建 - 12种隐式动画小组件全解析_第1张图片

简述

隐式动画的基类为ImplicitlyAnmatedWidget,它是一个抽象方法,上面12种动画组件都是继承自它,它的构造方法也是比较简单,只有四个参数,下面先来看下各个参数的作用。

const ImplicitlyAnimatedWidget({
  super.key,
  this.curve = Curves.linear,
  required this.duration,
  this.onEnd,
});
  • curve参数表示的是动画执行的过程方式,默认为线性方式;
  • duration参数表示的是动画执行的时长,此参数为必传参数;
  • onEnd参数表示的是动画结束的回调。

ImplicitlyAnmatedWidget构造方法中参数含义都是比较简单,基本上都是写一次就懂的~

AnimatedAlign

AnimatedAlign提供的对齐方式的动画效果,下面我们来实现文本从左边移到右边的动画效果。

const duration = Duration(seconds: 1);

Alignment _alignment = Alignment.centerLeft;

Widget buildAnimatedAlign() {
  return AnimatedAlign(
    alignment: _alignment,
    duration: duration,
    child: const Text("AnimatedAlign"),
  );
}

先构建一个AnimatedAlign组件,它的alignment参数传入的初始值为居中左对齐方式,时长为1s,然后子组件就是一个简单的文本组件。


@override
Widget build(BuildContext context) {
  return buildScaffold(
    context,
    ListView(
      children: [
        buildAnimatedAlign(),
      ],
    ),
    actionButton: FloatingActionButton(
      onPressed: () {
        setState(() {
          _alignment = _alignment == Alignment.centerLeft
              ? Alignment.centerRight
              : Alignment.centerLeft;
        });
      },
      child: const Icon(
        Icons.favorite,
      ),
    ),
  );
}

最后在FloatingActionButton的点击事件中将_alignment进行变化,从左边变化为右边,反之从右边变化为左边,下面我们看下实现的效果:

AnimatedContainer

AnimatedContainer提供的动画效果比较多,在其内部属性变化的时候都会按照动画的效果变化,比如颜色值、大小、padding等改变时都会执行动画,下面我们以颜色来感受下。

Color _color = Colors.red;

Widget buildAnimatedContainer() {
  return AnimatedContainer(
    duration: duration,
    color: _color,
    height: 100,
  );
}

setState(() {
  _color = _color == Colors.red ? Colors.blue : Colors.red;
});

这里将AnimatedContainer的背景色设置默认为红色,然后点击按钮时以红色和蓝色切换,这时候背景色就会以线性的方式改变。

AnimatedDefaultTextStyle

AnimatedDefaultTextStyle提供的是当文本样式改变时执行的动画效果,这里我们以文本颜色值改变来感受下此动画。

TextStyle _textStyle = const TextStyle(color: Colors.red, fontSize: 20);

Widget buildAnimatedTextStyle() {
  return AnimatedDefaultTextStyle(
    style: _textStyle,
    duration: duration,
    child: const Text('AnimatedDefaultTextStyle'),
  );
}

setState(() {
  _textStyle = _textStyle.color == Colors.red
      ? const TextStyle(color: Colors.blue, fontSize: 20)
      : const TextStyle(color: Colors.red, fontSize: 20);
});

点击时让文本的颜色值以红色和蓝色切换,然后看文本呈现的变化效果。

AnimatedScale

AnimatedScale提供的是一种缩放动画,通过此动画组件缩放就不在是一瞬间的效果,而是以线性的方式逐渐变化大小。

double _scale = 1.0;

Widget buildAnimatedScale() {
  return AnimatedScale(
    scale: _scale,
    duration: duration,
    child: Container(
      height: 100,
      color: Colors.red,
    ),
  );
}

setState(() {
	_scale = _scale == 1.0 ? 0.5 : 1.0;
})

AnimatedRotation

AnimtedRotation提供的是一种旋转动画,旋转一圈分为8份,没份表示旋转45度,下面来看看Text旋转效果

double _rotation = 0;
Widget buildAnimatedRotation() {
  return AnimatedRotation(
    turns: _rotation,
    duration: duration,
    child: Container(
      alignment: Alignment.center,
      child: const Text('AnimatedRotation'),
    ),
  );
}

setState(() {
	_rotation += 1.0 / 8.0;
})

AnimatedSlide

AnimatedSlide提供的是Offset的对象上的动画,当AnimatedSlide的Offset发生变化时,它会以指定的动画形式进行偏移,下面我们来实现每次点击按钮Y轴向下偏移1个单位。

Offset _offset = Offset.zero;
Widget buildAnimatedSlide() {
  return AnimatedSlide(
    offset: _offset,
    duration: duration,
    child: const FlutterLogo(
      size: 50,
    ),
  );
}
setState(() {
	_offset = Offset(0, _offset.dy + 1);
})

AnimatedOpacity

AnimatedOpacity提供的是组件的透明度变化动画,当AnimatedOpacity透明度值变化时,子组件会按照指定的动画形式就行改变,下面我们来实现组件显示和隐藏之间的切换,看看具体的动画效果。

double _opacity = 1.0;
Widget buildAnimatedOpacity() {
  return AnimatedOpacity(
    opacity: _opacity,
    duration: duration,
    child: const FlutterLogo(
      size: 50,
    ),
  );
}

setState(() {
	 _opacity = _opacity == 1 ? 0 : 1;
})

AnimatedPadding

AnimatedPadding提供的是当padding参数发生改变时,子组件的边距会以动画的形式逐渐改变,下面我们以Container每次增加10边距的效果直观感受下。

double _padding = 0.0;

Widget buildAnimatedPadding() {
  return AnimatedPadding(
    padding: EdgeInsets.all(_padding),
    duration: duration,
    child: Container(
      height: 50,
      color: Colors.red,
    ),
  );
}

setState(() {
	_padding += 10.0;
})

AnimatedTheme

AnimatedTheme比较有趣,它可以提供当主题发生改变时,其子组件的各种样式都会随之以动画的形式变化,这里我们选择切换黑白主题时,改变Icon的颜色,这种可以很好的感受其动画的效果。

bool _isDark = false;

Widget buildAnimatedTheme() {
  return AnimatedTheme(
    duration: duration,
    data: _isDark ? ThemeData.dark() : ThemeData.light(),
    child: const Icon(
      Icons.favorite,
      size: 50,
    ),
  );
}

setState(() {
	_isDark = !_isDark;
})

这里逻辑比较简单,这是在AnimatedTheme的data参数中判断当前是是否为黑色主题,然后根据判断赋值对应的ThemeData,当_isDark发生变化时,Icon的颜色也会随之而黑白切换。

AnimatedSize

AnimatedSize算是12种隐式动画组件中较为简单的一种了,它自身并没有可改变的参数,而是根据子组件的大小变化而产生动画效果,下面我们来实现Logo大小变化的动画效果。

double _size = 50;

Widget buildAnimatedSize() {
  return AnimatedSize(
    duration: duration,
    child: FlutterLogo(
      size: _size,
    ),
  );
}

setState(() {
	_size = _size == 50 ? 100 : 50;
})

这样就完成了FlutterLogo在50和100之间的大小变化并且带有动画效果。

AnimatedSwitcher

AnimatedSwitcher是这里面比较复杂的一种,它提供的是新旧组件切换时一种动画效果,但是看它的参数中只有一个child参数,并没有第二个子组件的定义,其实新旧组件的切换是在transitionBuilder参数中进行定义,transitionBuilder用于定义新组件是如何进行出现,默认的是FadeTransition效果,其效果为淡入淡出,下面我们来看看实现的具体效果。

int _count = 0;

Widget buildAnimatedSwitcher() {
  return AnimatedSwitcher(
    duration: duration,
    child: Text(
      key: ValueKey(_count),
      "$_count",
      style: const TextStyle(fontSize: 40),
    ),
  );
}

setState(() {
	++count;
})

这里我们看下Text中给key赋值了,使用ValueKey将_count值和key进行绑定,这样的作用是用于判断新旧组件是否变化,在默认的FadeTransition实现中会取child的key,这里千万别忘记给Text赋值key哦,不然动画就不会生效。

TweenAnimationBuilder

TweenAnimationBuilder被称之为补间动画,它会根据目标值的变化而随之执行动画,补间动画的自由度比较多,可以实现大小、颜色等等动画,如果上面11种不满足需求使用,可以使用TweenAnimationBuilder来定义自己想要的动画效果,下面我们还是以简单的例子来感受下补间动画的使用和效果。

double _tweenSize = 50;

Widget buildTweenAnimated() {
  return TweenAnimationBuilder(
    tween: Tween(begin: 100, end: _tweenSize),
    duration: duration,
    builder: (context, size, child) {
      return FlutterLogo(
        size: size,
      );
    },
  );
}

setState(() {
	_tweenSize = _tweenSize == 50 ? 100 : 50;
})

这里tween参数需要提供一个Tween类型的对象,Tween构造方法中有begin和end两个参数,也就是说在进入界面的时候FlutterLogo就会从begin:100的大小动画至end:50,后续每次点击按钮FlutterLog的大小都会在100和50之间切换,下面来看下效果图是否如我们期望的一致。

写在最后

本篇文章主要介绍了Flutter中12种常用的隐式动画小组件,这12中动画都不是很复杂但是在日常使用中可能会给我们带来小而美的效果,在一些简单的效果中可以使用其帮助我们简化动画的实现,希望通过文章给阅读的小伙伴们带来一点帮助,后续会循序渐进逐步接触Flutter更多的知识。

你可能感兴趣的:(flutter)