官方文檔android
還記得 上一節 裏面是怎麼更新 widget 的狀態的嗎?咱們上次的步驟是:首先建立動畫,而後給動畫添加監聽 addListener(...)
, 在 addListener(...)
方法中咱們幹了件 很重要 的事兒:setState((){})
,由於只有調用這個,纔會讓 widget 重繪。git
這一次咱們使用 AnimatedWidget
來實現動畫,使用它就不須要給動畫 addListener(...)
和 setState((){})
了,AnimatedWidget
本身會使用當前 Animation
的 value
來繪製本身。固然,這裏 Animation
咱們是以構造參數的方式傳遞進去的。github
看代碼:markdown
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
類型,以下:app
/// The [Listenable] to which this widget is listening.
///
/// Commonly an [Animation] or a [ChangeNotifier].
final Listenable listenable;
複製代碼
而後再看看 Animation 類:dom
abstract class Animation<T> extends Listenable implements ValueListenable<T> {
...
}
複製代碼
能夠看到 Animation
是 Listenable
的子類,因此在咱們自定義的 AnimatedContainer
類中能夠傳一個 Animation
類型的的參數做爲父類中 listenable
的值。ide
使用咱們上面定義的 AnimatedContainer
也很簡單,直接做爲 widget
使用就好,部分代碼以下:post
@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
作一個上面同樣的效果
部分代碼以下:
@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(() {});
}
複製代碼
效果以下:
_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,
),
),
),
),
);
}
複製代碼
效果以下:
_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,
),
),
),
),
);
}
複製代碼
效果以下
若有錯誤,還請指出,謝謝!