官方文檔: flutter.dev/docs/codela…html
咱們平時使用的 AnimatonController 來控制的動畫,須要咱們指定動畫的運行時間,動畫的運動曲線,並手動控制動畫的開始和結束,這實際上是顯式動畫,與之對應的就是隱式動畫。顧名思義,隱式動畫其實不須要咱們對動畫作太多的干預,直接使用 Flutter 內部定義好的動畫組件,就能夠實現簡單的動畫效果。git
常見的隱式動畫組件有 AnimatedOpacity , AnimatedContainer, AnimatedPadding, AnimatedPositioned, AnimatedSwitcher 以及 AnimatedAlign 等,經過這些組件的名字也能看出來這些組件的做用,接下來簡單介紹一下如何使用隱式動畫組件。github
AnimatedOpacity 這個 Widget 能夠實現透明度變化的動畫效果。編程
先看一下 AnimatedOpacity 的構造函數。bash
const AnimatedOpacity({
Key key,
this.child,
@required this.opacity,
Curve curve = Curves.linear,
@required Duration duration,
VoidCallback onEnd,
this.alwaysIncludeSemantics = false,
}) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
super(key: key, curve: curve, duration: duration, onEnd: onEnd);
複製代碼
這些參數基本上看一眼就知道是幹嗎的了。微信
能夠看得出,和咱們平時正常使用 Widget 其實沒有任何區別,可是卻大大簡化的動畫的使用。dom
class FadeInDemo extends StatefulWidget {
_FadeInDemoState createState() => _FadeInDemoState();
}
class _FadeInDemoState extends State<FadeInDemo> {
double opacity = 0.0;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.network(
'https://picsum.photos/250?image=9',
),
MaterialButton(
child: Text(
'Show Details',
style: TextStyle(color: Colors.blueAccent),
),
onPressed: () => setState(() {
opacity = 1;
}),
),
AnimatedOpacity(
duration: Duration(seconds: 3),
opacity: opacity,
child: Column(
children: <Widget>[
Text('Type: Owl'),
Text('Age: 39'),
Text('Employment: None'),
],
),
)
]);
}
}
複製代碼
使用 AnimatedOpcacity 須要指定初始的透明度值, 0 爲不可見,當點擊按鈕,設置爲 1,同時 setState 將會觸發動畫效果。duration 指定了動畫時間爲 3s 。ide
效果:函數
AnimatedContainer 這個 Widget 和 Container 同樣,是同一個能夠控制多個屬性(如 margin、borderRaidu、color)的 widget ,同時還能加上動畫。佈局
構造函數以下:
AnimatedContainer({
Key key,
this.alignment,
this.padding,
Color color,
Decoration decoration,
this.foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
this.margin,
this.transform,
this.child,
Curve curve = Curves.linear,
@required Duration duration,
VoidCallback onEnd,
}) : assert(margin == null || margin.isNonNegative),
assert(padding == null || padding.isNonNegative),
assert(decoration == null || decoration.debugAssertIsValid()),
assert(constraints == null || constraints.debugAssertIsValid()),
assert(color == null || decoration == null,
'Cannot provide both a color and a decoration\n'
'The color argument is just a shorthand for "decoration: BoxDecoration(color: color)".'
),
decoration = decoration ?? (color != null ? BoxDecoration(color: color) : null),
constraints =
(width != null || height != null)
? constraints?.tighten(width: width, height: height)
?? BoxConstraints.tightFor(width: width, height: height)
: constraints,
super(key: key, curve: curve, duration: duration, onEnd: onEnd);
複製代碼
和正常的 Container 使用沒有什麼區別,就是加上了動畫相關的幾個屬性。
const _duration = Duration(milliseconds: 400);
double randomBorderRadius() {
return Random().nextDouble() * 64;
}
double randomMargin() {
return Random().nextDouble() * 64;
}
Color randomColor() {
return Color(0xFFFFFFFF & Random().nextInt(0xFFFFFFFF));
}
class AnimatedContainerDemo extends StatefulWidget {
_AnimatedContainerDemoState createState() => _AnimatedContainerDemoState();
}
class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
Color color;
double borderRadius;
double margin;
@override
void initState() {
super.initState();
color = Colors.deepPurple;
borderRadius = randomBorderRadius();
margin = randomMargin();
}
void change() {
setState(() {
color = randomColor();
borderRadius = randomBorderRadius();
margin = randomMargin();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
width: 128,
height: 128,
child: AnimatedContainer(
margin: EdgeInsets.all(margin),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(borderRadius),
),
duration: _duration,
curve: Curves.easeInOutBack,
),
),
MaterialButton(
color: Theme.of(context).primaryColor,
child: Text(
'change',
style: TextStyle(color: Colors.white),
),
onPressed: () => change(),
),
],
),
),
);
}
}
複製代碼
上面示例的代碼能夠動態的改變 Container 的 margin,borderRaidus 以及 color 屬性。 效果:
AnimatedSwitcher 的詳細的介紹和原理能夠參考 Flutter 中文網—通用「動畫切換」組件
AnimatedSwitcher 能夠同時對其新、舊子元素添加顯示、隱藏動畫。也就是說在AnimatedSwitcher的子元素髮生變化時,會對其舊元素和新元素。 其構造函數以下:
const AnimatedSwitcher({
Key key,
this.child,
@required this.duration, // 新child顯示動畫時長
this.reverseDuration,// 舊child隱藏的動畫時長
this.switchInCurve = Curves.linear, // 新child顯示的動畫曲線
this.switchOutCurve = Curves.linear,// 舊child隱藏的動畫曲線
this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder, // 動畫構建器
this.layoutBuilder = AnimatedSwitcher.defaultLayoutBuilder, //佈局構建器
})
複製代碼
當AnimatedSwitcher的child發生變化時(類型或Key不一樣),舊child會執行隱藏動畫,新child會執行執行顯示動畫。究竟執行何種動畫效果則由transitionBuilder參數決定,該參數接受一個AnimatedSwitcherTransitionBuilder類型的builder,定義以下:
typedef AnimatedSwitcherTransitionBuilder =
Widget Function(Widget child, Animation<double> animation);
複製代碼
該builder在AnimatedSwitcher的child切換時會分別對新、舊child綁定動畫:
對舊child,綁定的動畫會反向執行(reverse) 對新child,綁定的動畫會正向指向(forward) 這樣一下,便實現了對新、舊child的動畫綁定。AnimatedSwitcher的默認值是AnimatedSwitcher.defaultTransitionBuilder :
Widget defaultTransitionBuilder(Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: child,
);
}
複製代碼
能夠看到,返回了FadeTransition對象,也就是說默認狀況,AnimatedSwitcher會對新舊child執行「漸隱」和「漸顯」動畫。
class AnimatedSwitcherCounterRoute extends StatefulWidget {
const AnimatedSwitcherCounterRoute({Key key}) : super(key: key);
@override
_AnimatedSwitcherCounterRouteState createState() => _AnimatedSwitcherCounterRouteState();
}
class _AnimatedSwitcherCounterRouteState extends State<AnimatedSwitcherCounterRoute> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation) {
///縮放動畫
return ScaleTransition(child: child, scale: animation);
///上下左右平移動畫
// return SlideTransitionX(
// child: child,
// direction: AxisDirection.up, //上入下出
// position: animation,
// );
},
child: Text(
'$_count',
//顯示指定key,不一樣的key會被認爲是不一樣的Text,這樣才能執行動畫
key: ValueKey<int>(_count),
style: Theme.of(context).textTheme.display1,
),
),
RaisedButton(
child: const Text('+1',),
onPressed: () {
setState(() {
_count += 1;
});
},
),
],
),
);
}
}
複製代碼
上面的 transitionBuilder 經過指定不一樣的返回值類型,能夠控制不一樣的動畫效果。
縮放:
平移:
推薦閱讀