Flutter 中的 Animations(二)

栏目: ASP.NET · 发布时间: 6年前

内容简介:还记得上一节 里面是怎么更新这一次我们使用看代码:

还记得上一节 里面是怎么更新 widget 的状态的吗?我们上次的步骤是:首先创建动画,然后给动画添加监听 addListener(...) , 在 addListener(...) 方法中我们干了件 很重要 的事儿: setState((){}) ,因为只有调用这个,才会让 widget 重绘。

这一次我们使用 AnimatedWidget 来实现动画,使用它就不需要给动画 addListener(...)setState((){}) 了, AnimatedWidget 自己会使用当前 Animationvalue 来绘制自己。当然,这里 Animation 我们是以构造参数的方式传递进去的。

看代码:

class AnimatedContainer extends AnimatedWidget {

  AnimatedContainer({Key key, Animation<double> animation})
      : super (key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    return Center(
      child: Container(
        decoration: BoxDecoration(
            color: Colors.redAccent
        ),
        margin: EdgeInsets.symmetric(vertical: 10.0),
        height: animation.value,
        width: animation.value,
      ),
    );
  }
}
复制代码

上述代码中,我们定义了一个 AnimatedContainer 继承了 AnimatedWidget ,然后定义了一个构造方法,注意,构造方法中我们定义了一个 Animation 然后把这个 Animation 传到父类(super)中去了,我们可以看看 listenable: animation 这个参数,是一个 Listenable 类型,如下:

/// The [Listenable] to which this widget is listening.
///
/// Commonly an [Animation] or a [ChangeNotifier].
final Listenable listenable;
复制代码

然后再看看 Animation 类:

abstract class Animation<T> extends Listenable implements ValueListenable<T> {
...
}
复制代码

可以看到 AnimationListenable 的子类,所以在我们自定义的 AnimatedContainer 类中可以传一个 Animation 类型的的参数作为父类中 listenable 的值。

使用我们上面定义的 AnimatedContainer 也很简单,直接作为 widget 使用就好,部分代码如下:

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedWidgetDemo',
      theme: ThemeData(
          primaryColor: Colors.redAccent
      ),
      home: Scaffold(
          appBar: AppBar(
            title: Text('AnimatedWidgetDemo'),
          ),
          body: AnimatedContainer(animation: animation,)
      ),
    );``
  }
复制代码

可以看出我们在实例化 AnimatedContainer 的时候传入了一个 Animation 对象。

AnimatedWidgetDemo.dart

效果如下:

Flutter 中的 Animations(二)

AnimatedBuilder

我们先看看如何使用 AnimatedBuilder 做一个上面一样的效果

部分代码如下:

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedBuilderExample',
      theme: ThemeData(primaryColor: Colors.redAccent),
      home: Scaffold(
        appBar: AppBar(
          title: Text('animatedBuilderExample'),
        ),
        body: Center(
          child: AnimatedBuilder(
            animation: _animation,
            child: Container(
              decoration: BoxDecoration(color: Colors.redAccent),
            ),
            builder: (context, child) {
              return Container(
                width: _animation.value,
                height: _animation.value,
                child: child,
              );
            },
          ),
        ),
      ),
    );
  }
复制代码

因为 AnimatedBuilder 是继承于 AnimatedWidget 的,

class AnimatedBuilder extends AnimatedWidget { ... }
复制代码

所以可以直接把 AnimatedBuilder 当作 widget 使用

上述代码关键部分如下:

body: Center(
  child: AnimatedBuilder(
    animation: _animation,
    child: Container(
      decoration: BoxDecoration(color: Colors.redAccent),
    ),
    builder: (context, child) {
      return Container(
        width: _animation.value,
        height: _animation.value,
        child: child,
      );
    },
  ),
),
复制代码

效果如下:

Flutter 中的 Animations(二)

AnimatedBuilderExample_2.dart

builder 这个匿名类是每次动画值改变的时候就会被调用

AnimatedBuilder使用简化后的结构如下:

AnimatedBuilder(
    animateion: ... ,
    child: ... ,
    builder: (context, child) {
        return Container(
            width: ... ,
            height: ... ,
            child: child
        )
    }
)
复制代码

上述代码看着可能会有迷糊的地方,里面的 child 好像被指定了两次,外面一个,里面一个。其实,外面的 child 是传给 AnimatedBuilder 的,而 AnimatedBuilder 又将这个 child 作为参数传递给了里面的匿名类( builder )。

我们可以验证上述说明,就是给外面的 child 指定一个 key ,然后在 builder 里面打印出参数 childkey

body: Center(
  child: AnimatedBuilder(
    animation: _animation,
    child: Container(
      decoration: BoxDecoration(color: Colors.redAccent),
    key: Key("android"),
    ),
    builder: (context, child) {
      print("child.key: ${child.key}");
      return Container(
        width: _animation.value,
        height: _animation.value,
        child: child,
      );
    },
  ),
),
复制代码

我们在外面的 child 里面的添加了一个 key 值,然后在 builder 里面打印出参数 childkey

输出如下:

flutter: child.key: [<'android'>]
复制代码

区别

我们来看看 AnimatedBuilder AnimatedWidget 和添加 addListener{} 监听并在里面触发 setState(...) 这三种方式做动画有什么区别。

为了更直观的看出它们的区别,这里使用一个第三方控件: RandomContainer ,这个控件会在屏幕每次重绘的时候改变自身的颜色。

首先在 pubspec.yaml 中添加依赖 random_pk: any ,如下:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.2
  # RandomContainer
  random_pk: any
复制代码

先写一个小例子来看看 RandomContainer 这个控件每次在屏幕重绘的时候自身颜色改变的情况。

在屏幕上绘制一个宽高都为 200.0RandomContainer 然后给 RandomContainer 添加点击事件,点击事件里面做的就是调用 setState(...)widget 重绘,部分代码如下:

body: Center(
  child: GestureDetector(
    onTap: _changeColor,
    child: RandomContainer(
      width: 200.0,
      height: 200.0,
    ),
  ),
),
复制代码

使用 RandomContainer 的时候需要引入 import 'package:random_pk/random_pk.dart';

点击事件代码如下:

void _changeColor() {
    setState(() {});
  }
复制代码

AnimatedBuilderExample_1.dart

效果如下:

Flutter 中的 Animations(二)

接下来我们使用三种方式实现同一种效果来看看有什么不同:

AnimatedWidget

_controller =
        AnimationController(vsync: this, duration: Duration(seconds: 5))
          ..repeat();
复制代码
@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedBuilder',
      theme: ThemeData(primaryColor: Colors.redAccent),
      home: Scaffold(
        appBar: AppBar(
          title: Text('AnimatedBuilder'),
        ),
        body: Center(
          child: RandomContainer(
            width: 200.0,
            height: 200.0,
            child: AnimatedWidgetDemo( // new
              animation: _controller,
            ),
          ),
        ),
      ),
    );
  }
复制代码

效果如下:

Flutter 中的 Animations(二)

AnimatedBuilder

_controller =
        AnimationController(vsync: this, duration: Duration(seconds: 5))
          ..repeat();
复制代码
@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedBuilder',
      theme: ThemeData(primaryColor: Colors.redAccent),
      home: Scaffold(
        appBar: AppBar(
          title: Text('AnimatedBuilder'),
        ),
        body: Center(
          child: RandomContainer(
            width: 200.0,
            height: 200.0,
            child: AnimatedBuilderDemo( // new
              child: getContainer(),
              animation: _controller,
            ),
          ),
        ),
      ),
    );
  }
复制代码

AnimatedBuilder的效果和 AnimatedWidget 的效果是一样的。

接下来我们看看在 addListener{} 里面调用 setState(...) 的效果,也就是我们在上一节中实现动画的方式

_controller =
        AnimationController(vsync: this, duration: Duration(seconds: 5))
          ..repeat()
          ..addListener(() {
            setState(() {});
          });
复制代码
@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedBuilder',
      theme: ThemeData(primaryColor: Colors.redAccent),
      home: Scaffold(
        appBar: AppBar(
          title: Text('AnimatedBuilder'),
        ),
        body: Center(
          child: RandomContainer(
            width: 200.0,
            height: 200.0,
            child: Transform.rotate( // new
              child: getContainer(),
              angle: _controller.value * 2.0 * pi,
            ),
          ),
        ),
      ),
    );
  }
复制代码

效果如下

Flutter 中的 Animations(二)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Designing Data-Intensive Applications

Designing Data-Intensive Applications

Martin Kleppmann / O'Reilly Media / 2017-4-2 / USD 44.99

Data is at the center of many challenges in system design today. Difficult issues need to be figured out, such as scalability, consistency, reliability, efficiency, and maintainability. In addition, w......一起来看看 《Designing Data-Intensive Applications》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码