内容简介:还记得上一节 里面是怎么更新这一次我们使用看代码:
还记得上一节 里面是怎么更新 widget 的状态的吗?我们上次的步骤是:首先创建动画,然后给动画添加监听 addListener(...)
, 在 addListener(...)
方法中我们干了件 很重要 的事儿: setState((){})
,因为只有调用这个,才会让 widget 重绘。
这一次我们使用 AnimatedWidget
来实现动画,使用它就不需要给动画 addListener(...)
和 setState((){})
了, AnimatedWidget
自己会使用当前 Animation
的 value
来绘制自己。当然,这里 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> { ... } 复制代码
可以看到 Animation
是 Listenable
的子类,所以在我们自定义的 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
对象。
效果如下:
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, ); }, ), ), 复制代码
效果如下:
builder
这个匿名类是每次动画值改变的时候就会被调用
AnimatedBuilder使用简化后的结构如下:
AnimatedBuilder( animateion: ... , child: ... , builder: (context, child) { return Container( width: ... , height: ... , child: child ) } ) 复制代码
上述代码看着可能会有迷糊的地方,里面的 child
好像被指定了两次,外面一个,里面一个。其实,外面的 child
是传给 AnimatedBuilder
的,而 AnimatedBuilder
又将这个 child
作为参数传递给了里面的匿名类( builder
)。
我们可以验证上述说明,就是给外面的 child
指定一个 key
,然后在 builder
里面打印出参数 child
的 key
。
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
里面打印出参数 child
的 key
值
输出如下:
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.0
的 RandomContainer
然后给 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(() {}); } 复制代码
效果如下:
接下来我们使用三种方式实现同一种效果来看看有什么不同:
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, ), ), ), ), ); } 复制代码
效果如下:
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, ), ), ), ), ); } 复制代码
效果如下
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
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》 这本书的介绍吧!