Flutter中的基本动画

来自菜鸟教程
跳转至:导航、​搜索

虽然 Flutter 有大量用于为您的应用程序创建动画效果的令人惊叹的包,但也有用于手动创建更精细动画的内置方法。

先决条件

对于我们的跨屏动画,我假设您已经知道如何创建基本路线,只是为了保持简短。 如果您对此不满意,可以在此处查看 文档

线性动画

我们动画的主要三个部分是 ticker 来控制我们的时间,controller 来注册我们的参数,比如我们的持续时间,然后我们想要更改的值。 在我们的小部件被渲染之前,在我们的 initState 中,我们可以将我们的控制器设置为它的参数,设置它的方向,并添加一个侦听器来在每次更改时重置我们的小部件状态。

默认情况下,控制器会在我们设置的持续时间内将变化从 0 移动到 1,我们可以在监听器中打印 controller.value 来观察这种情况。 我们可以通过设置 upperBoundlowerBound 属性来更改我们的默认开始和结束值。

主要.dart

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;

  void initState() {
    super.initState();
    controller = AnimationController(
      duration: Duration(seconds: 1),
      vsync: this); // Links this controller to this widget so it won't run if the parent widget isn't rendered, to save on resources.

    controller.forward();
    controller.addListener(() => setState(() {}));
  }
}

要使用我们的动画,我们只需将我们想要的任何内容(例如不透明度)设置为 controller.value

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Opacity(
          opacity: controller.value,
          child: Container(width: 50, height: 50, color: Colors.red),
        ),
      ),
    );
  }

曲线动画

我们可以使用不同的曲线变化来精确控制控制器的变化方式,而不是无聊的线性动画。 为此,我们可以使用 CurvedAnimation 在我们原来的控制器上创建一种包装器。 这个包装器将采用它的父级、我们的控制器和我们想要应用的曲线。 当使用弯曲动画时,除了 0 和 1 之外,我们不能有下限和上限,所以当我们应用它时,我们可以乘以它的值。 在 文档 中有一个非常广泛的选项列表。

controller 上的另一个有用方法是 addStatusListener,这将允许我们利用它的生命周期。 只要它的值达到其上限或下限,我们的控制器就会触发完成或解除事件。 我们可以使用它的 forwardreverse 方法来无限循环我们的动画。

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;

  void initState() {
    super.initState();
    controller = AnimationController(
        duration: Duration(seconds: 1), 
        vsync: this);
    animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);

    controller.forward();
    animation.addListener(() => setState(() {}));

    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) controller.reverse(from: 400);
      else if (status == AnimationStatus.dismissed) controller.forward();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          margin: EdgeInsets.only(bottom: animation.value * 400),
          width: 50,
          height: 50,
          color: Colors.red),
      ),
    );
  }
}

补间

除了使用数字值之外,我们还可以使用补间(介于之间的缩写)来处理其他事物的范围,例如颜色。 就像我们的曲线动画一样,它将充当控制器或动画的包装器,我们可以将颜色设置为其值。

主要.dart

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;
  Animation changeColor;

  void initState() {
    super.initState();
    controller =
        AnimationController(duration: Duration(seconds: 1), vsync: this);
    animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);
    changeColor = ColorTween(begin: Colors.red, end: Colors.blue).animate(animation);
    controller.forward();
    animation.addListener(() => setState(() {}));

    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) controller.reverse(from: 400);
      else if (status == AnimationStatus.dismissed) controller.forward();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          margin: EdgeInsets.only(bottom: animation.value * 400),
          width: 50,
          height: 50,
          color: changeColor.value),
      ),
    );
  }
}

十字屏

当我们想要在不同屏幕上的小部件之间进行转换时,另一个很棒的技术是使用 Hero 小部件。 我们只需要用匹配的标签将每个都包装在自己的 Hero 小部件中。 标签必须是唯一的,并且任何时候屏幕上都不能有多个。

第一屏

Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
      SizedBox(height: 1),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          Hero(tag: 'icon', child: Icon(Icons.add)),
        ]),
      Navbar()
  ]));
}

第二屏

Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        SizedBox(height: 1),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            SizedBox(width: 1),
            Hero(
              tag: 'icon',
              child: Icon(Icons.add, color: Colors.red, size: 75)),
          ]),
        Navbar()
      ]),
  );
}

结论

在过去的大多数前端 Web 技术中,动画都是事后才想到的,但 Flutter 团队在开发这个令人惊叹的框架时在牢记动画方面做得非常出色。 我们实际上只是触及了 Flutter 在动画方面的能力的皮毛。