隐式动画
什么是隐式动画?
What are implicit animations?
示例:淡入文字效果
Example: Fade-in text effect
示例:形状变化效果
Example: Shape-shifting effect
下一个是什么?
What’s next?
欢迎来到隐式动画的 codelab,在这里您将学到:如何使用 Flutter widgets 轻松地对一组特定属性创建动画。
Welcome to the implicit animations codelab, where you learn how to use Flutter widgets that make it easy to create animations for a specific set of properties.
为了充分理解该 codelab,您应该具备以下基本知识:
To get the most out of this codelab, you should have basic knowledge about:
-
如何 创建一个 Flutter 应用。
How to make a Flutter app.
-
如何使用 stateful widgets。
How to use stateful widgets.
该 codelab 包括以下内容:
This codelab covers the following material:
-
使用
AnimatedOpacity
来创建一个淡入效果。Using
AnimatedOpacity
to create a fade-in effect. -
使用
AnimatedContainer
让尺寸、颜色和边距产生动画变换。Using
AnimatedContainer
to animate transitions in size, color, and margin. -
隐式动画及其使用方法的概述。
Overview of implicit animations and techniques for using them.
完成该 codelab 的时间约为:15-30 分钟。
Estimated time to complete this codelab: 15-30 minutes.
什么是隐式动画?
What are implicit animations?
通过使用 Flutter 的 动画库,您可以为 UI 中的组件添加运动和创建视觉效果。
With Flutter’s animation library, you can add motion and create visual effects for the widgets in your UI.
您可以使用库中的一套组件来管理动画,这些组件统称为隐式动画或隐式动画组件,其名称源于它们都实现了 ImplicitlyAnimatedWidget 类。
One widget set in the library manages animations for you. These widgets are collectively referred to as implicit animations, or implicitly animated widgets, deriving their name from the ImplicitlyAnimatedWidget class that they implement.
使用隐式动画,您可以通过设置一个目标值,驱动 widget 的属性进行动画变换;每当目标值发生变化时,属性会从旧值逐渐更新到新值。通过这种方式,隐式动画内部实现了动画控制,从而能够方便地使用— 隐式动画组件会管理动画效果,用户不需要再进行额外的处理。
With implicit animations, you can animate a widget property by setting a target value; whenever that target value changes, the widget animates the property from the old value to the new one. In this way, implicit animations trade control for convenience—they manage animation effects so that you don’t have to.
示例:淡入文字效果
Example: Fade-in text effect
下面的示例展示了如何使用名为 AnimatedOpacity 的隐式动画 widget,为已存在的 UI 添加淡入效果。
The following example shows how to add a fade-in effect to existing UI using an implicitly animated widget called AnimatedOpacity.
这个示例开始没有动画效果— 它包含一个由 Material App 组成的主页面,有以下内容:
The example begins with no animation code—it consists of a Material App home screen containing:
-
一张猫头鹰的照片。
A photograph of an owl.
-
一个点击时什么也不做的 Show details 按钮。
One Show details button that does nothing when clicked.
-
照片中猫头鹰的描述文字。
Description text of the owl in the photograph.
淡入 (初始代码)
Fade-in (starter code)
点击 Run 按钮来运行这个示例:
Click the Run button to run the example:
{$ begin main.dart $}
import 'package:flutter/material.dart';
const owlUrl =
'https://raw.githubusercontent.com/flutter/website/master/src/images/owl.jpg';
class FadeInDemo extends StatefulWidget {
const FadeInDemo({Key? key}) : super(key: key);
@override
_FadeInDemoState createState() => _FadeInDemoState();
}
class _FadeInDemoState extends State<FadeInDemo> {
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
Image.network(owlUrl),
TextButton(
child: const Text(
'Show Details',
style: TextStyle(color: Colors.blueAccent),
),
onPressed: () => {}),
Column(
children: const [
Text('Type: Owl'),
Text('Age: 39'),
Text('Employment: None'),
],
)
]);
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: FadeInDemo(),
),
),
);
}
}
void main() {
runApp(
const MyApp(),
);
}
{$ end main.dart $}
使用 AnimatedOpacity widget 进行透明度动画
Animate opacity with AnimatedOpacity widget
这部分包含在 淡入初始代码 中添加一个隐式动画一系列步骤。完成这些步骤后,您还可以运行 淡入完成代码,该代码已经实现了淡入效果。这些步骤概述了如何使用 AnimatedOpacity
widget 来添加以下的动画特性:
This section contains a list of steps you can use to add an
implicit animation to the
fade-in starter code. After the steps, you can also run the
fade-in complete code with the changes already made.
The steps outline how to use the AnimatedOpacity
widget to add the following animation feature:
-
用户点击 Show details 按钮后,显示猫头鹰的描述文字。
The owl’s description text remains hidden until the user clicks the Show details button.
-
当用户点击 Show details 按钮时,猫头鹰的描述文字淡入。
When the user clicks the Show details button, the owl’s description text fades in.
1. 选择要进行动画的 widget 属性
1. Pick a widget property to animate
想要创建淡入效果,您可以使用 AnimatedOpacity
widget 对 opacity
属性进行动画。将 Container
widget 换成 AnimatedOpacity
widget:
To create a fade-in effect, you can animate the opacity
property using the
AnimatedOpacity
widget. Change the Container
widget to an
AnimatedOpacity
widget:
@@ -2,6 +2,8 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license
|
3
3
|
// that can be found in the LICENSE file.
|
4
|
+
// ignore_for_file: missing_required_argument
|
5
|
+
|
4
6
|
import 'package:flutter/material.dart';
|
5
7
|
const owlUrl =
|
@@ -25,12 +27,14 @@
|
|
25
27
|
style: TextStyle(color: Colors.blueAccent),
|
26
28
|
),
|
27
29
|
onPressed: () => {}),
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
AnimatedOpacity(
|
31
|
+
child: Column(
|
32
|
+
children: const [
|
33
|
+
Text('Type: Owl'),
|
34
|
+
Text('Age: 39'),
|
35
|
+
Text('Employment: None'),
|
36
|
+
],
|
37
|
+
),
|
34
38
|
)
|
35
39
|
]);
|
36
40
|
}
|
2. 为动画属性初始化一个状态变量
2. Initialize a state variable for the animated property
将 opacity
的初始值设置为 0 ,以便在用户点击 Show details 前隐藏文字:
To hide the text before the user clicks Show details, set
the starting value for opacity
to zero:
@@ -2,8 +2,6 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license
|
3
3
|
// that can be found in the LICENSE file.
|
4
|
-
// ignore_for_file: missing_required_argument
|
5
|
-
|
6
4
|
import 'package:flutter/material.dart';
|
7
5
|
const owlUrl =
|
@@ -17,6 +15,8 @@
|
|
17
15
|
}
|
18
16
|
class _FadeInDemoState extends State<FadeInDemo> {
|
17
|
+
double opacity = 0.0;
|
18
|
+
|
19
19
|
@override
|
20
20
|
Widget build(BuildContext context) {
|
21
21
|
return Column(children: <Widget>[
|
@@ -28,6 +28,8 @@
|
|
28
28
|
),
|
29
29
|
onPressed: () => {}),
|
30
30
|
AnimatedOpacity(
|
31
|
+
duration: const Duration(seconds: 3),
|
32
|
+
opacity: opacity,
|
31
33
|
child: Column(
|
32
34
|
children: const [
|
33
35
|
Text('Type: Owl'),
|
3. 为动画设置一个触发器,并选择一个结束值
3. Set up a trigger for the animation, and choose an end value
当用户点击 Show details 按钮时,将会触发动画。为了做到这点,我们使用 TextButton
的 onPressed()
方法,在调用时改变 opacity
的状态值为 1。
Configure the animation to trigger when the user clicks the Show details
button. To do this, change opacity
state using the onPressed()
handler for
TextlButton
. To make the FadeInDemo
widget become fully visible when
the user clicks the Show details button, use the onPressed()
handler
to set opacity
to 1:
@@ -22,11 +22,14 @@
|
|
22
22
|
return Column(children: <Widget>[
|
23
23
|
Image.network(owlUrl),
|
24
24
|
TextButton(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
child: const Text(
|
26
|
+
'Show Details',
|
27
|
+
style: TextStyle(color: Colors.blueAccent),
|
28
|
+
),
|
29
|
+
onPressed: () => setState(() {
|
30
|
+
opacity = 1;
|
31
|
+
}),
|
32
|
+
),
|
30
33
|
AnimatedOpacity(
|
31
34
|
duration: const Duration(seconds: 2),
|
32
35
|
opacity: opacity,
|
4. 为动画设置时长
4. Set the duration of the animation
除了 opacity
参数,AnimatedOpacity
还需要 duration 参数确定动画时长。在下面的示例中,您可以设置淡入时长为 2 秒:
In addition to an opacity
parameter, AnimatedOpacity
requires a
duration to use for its animation. For this example,
you can start with 2 seconds:
@@ -28,7 +28,7 @@
|
|
28
28
|
),
|
29
29
|
onPressed: () => {}),
|
30
30
|
AnimatedOpacity(
|
31
|
-
duration: const Duration(seconds:
|
31
|
+
duration: const Duration(seconds: 2),
|
32
32
|
opacity: opacity,
|
33
33
|
child: Column(
|
34
34
|
children: const [
|
淡入 (完成代码)
Fade-in (complete)
下面的示例是修改后的完成版代码— 运行这个示例,然后点击 Show details 按钮就可以触发动画。
Here’s the example with the completed changes you’ve made—run this example and click the Show details button to trigger the animation.
{$ begin main.dart $}
import 'package:flutter/material.dart';
const owlUrl =
'https://raw.githubusercontent.com/flutter/website/master/src/images/owl.jpg';
class FadeInDemo extends StatefulWidget {
const FadeInDemo({Key? key}) : super(key: key);
@override
_FadeInDemoState createState() => _FadeInDemoState();
}
class _FadeInDemoState extends State<FadeInDemo> {
double opacity = 0.0;
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
Image.network(owlUrl),
TextButton(
child: const Text(
'Show Details',
style: TextStyle(color: Colors.blueAccent),
),
onPressed: () => setState(() {
opacity = 1;
}),
),
AnimatedOpacity(
duration: const Duration(seconds: 2),
opacity: opacity,
child: Column(
children: const [
Text('Type: Owl'),
Text('Age: 39'),
Text('Employment: None'),
],
),
)
]);
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: FadeInDemo(),
),
),
);
}
}
void main() {
runApp(
const MyApp(),
);
}
{$ end main.dart $}
小结一下
Putting it all together
淡入文字效果 的例子展现了 AnimatedOpacity
的特性:
The Fade-in text effect example demonstrates the following features
of AnimatedOpacity
:
-
AnimatedOpacity
会监听其opacity
属性的状态变化。AnimatedOpacity
listens for state changes in itsopacity
property. -
当
opacity
属性改变时,AnimatedOpacity
会自动将opacity
变化到新值,同时使 widget 进行动画跟随变换。Whenever
opacity
changes,AnimatedOpacity
automatically animates the widget’s transition to the new value foropacity
. -
AnimatedOpacity
需要一个duration
参数来确定新旧opacity
进行动画变换的时长。AnimatedOpacity
requires aduration
parameter to define the time it takes to animate the transition between an oldopacity
value and a new one.
示例:形状变化效果
Example: Shape-shifting effect
下面的示例将展示如何使用 AnimatedContainer widget
让多个不同类型(double
和 Color
)的属性(margin
、borderRadius
和 color
)同时进行动画变换。
The following example shows how to use the AnimatedContainer widget to
animate multiple properties (margin
, borderRadius
, and color
) with
different types (double
and Color
).
这个示例开始没有动画效果— 它以一个由 Material App 组成的主页面开始,有以下内容:
The example begins with no animation code—it starts with a Material App home screen that contains:
-
一个有
margin
、borderRadius
、和color
属性的Container
,这些属性每次运行时的值都不同。A
Container
withborderRadius
,margin
, andcolor
properties that are different each time you run the example. -
一个点击时什么都不做的 Change 按钮。
A Change button that does nothing when clicked.
形状变化 (初始代码)
Shape-shifting (starter code)
点击 Run 按钮来运行这个示例:
Click the Run button to run the example:
{$ begin main.dart $}
import 'dart:math';
import 'package:flutter/material.dart';
double randomBorderRadius() {
return Random().nextDouble() * 64;
}
double randomMargin() {
return Random().nextDouble() * 64;
}
Color randomColor() {
return Color(0xFFFFFFFF & Random().nextInt(0xFFFFFFFF));
}
class AnimatedContainerDemo extends StatefulWidget {
const AnimatedContainerDemo({Key? key}) : super(key: key);
@override
_AnimatedContainerDemoState createState() => _AnimatedContainerDemoState();
}
class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
late Color color;
late double borderRadius;
late double margin;
@override
initState() {
super.initState();
color = randomColor();
borderRadius = randomBorderRadius();
margin = randomMargin();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
SizedBox(
width: 128,
height: 128,
child: Container(
margin: EdgeInsets.all(margin),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(borderRadius),
),
),
),
ElevatedButton(
child: const Text('change'),
onPressed: () => {},
),
],
),
),
);
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: AnimatedContainerDemo(),
);
}
}
void main() {
runApp(
const MyApp(),
);
}
{$ end main.dart $}
使用 AnimatedContainer 将 color、borderRadius、和 margin 进行动画变换
Animate color, borderRadius, and margin with AnimatedContainer
这部分包含在 形状变化初始代码 中添加一个隐式动画的一系列步骤。完成这些步骤后,您还可以运行 形状变化完成代码,该代码已经实现了淡入效果。
This section contains a list of steps you can use to add an implicit animation to the shape-shifting starter code. After the steps, you can also run the shape-shifting complete example with the changes already made.
在 形状变化初始代码 中每个 Container
widget 的属性(color
、borderRadius
和 margin
)都由一个相关的函数赋值(分别是 randomColor()
、randomBorderRadius()
和 randomMargin()
)。您可以使用 AnimatedContainer
widget 重构这段代码,来完成以下的效果:
In the shape-shifting starter code,
each property in the Container
widget (color
,
borderRadius
, and margin
)
is assigned a value by an associated function (randomColor()
,
randomBorderRadius()
, and randomMargin()
respectively).
By using an AnimatedContainer
widget,
you can refactor this code to do the following:
-
每当用户点击 Change 按钮时,
color
、borderRadius
和margin
都会生成一个新值。Generate new values for
color
,borderRadius
, andmargin
whenever the user clicks the Change button. -
每当
color
、borderRadius
和margin
被设置时,都会进行动画变换到新的值。Animate the transition to the new values for
color
,borderRadius
, andmargin
whenever they are set.
1. 添加一个隐式动画
1. Add an implicit animation
将 Container
widget 换成 AnimatedContainer
widget:
Change the Container
widget to an AnimatedContainer
widget:
@@ -2,6 +2,8 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license
|
3
3
|
// that can be found in the LICENSE file.
|
4
|
+
// ignore_for_file: missing_required_argument
|
5
|
+
|
4
6
|
import 'dart:math';
|
5
7
|
import 'package:flutter/material.dart';
|
@@ -47,7 +49,7 @@
|
|
47
49
|
SizedBox(
|
48
50
|
width: 128,
|
49
51
|
height: 128,
|
50
|
-
child:
|
52
|
+
child: AnimatedContainer(
|
51
53
|
margin: EdgeInsets.all(margin),
|
52
54
|
decoration: BoxDecoration(
|
53
55
|
color: color,
|
2. 为动画属性设置初始值
2. Set starting values for animated properties
当属性的新旧值发生变化时,AnimatedContainer
会自动在新旧值之间产生动画效果。通过创建一个 change()
方法,我们将定义当用户点击 Change 按钮时触发变更的行为。
change()
方法可以使用 setState()
为 color
、borderRadius
和 margin
状态变量设置新值:
AnimatedContainer
automatically animates between old and new values of
its properties when they change. Create a change()
method that defines the
behavior triggered when the user clicks the Change button.
The change()
method can use setState()
to set new values
for the color
, borderRadius
, and margin
state variables:
@@ -40,6 +40,14 @@
|
|
40
40
|
margin = randomMargin();
|
41
41
|
}
|
42
|
+
void change() {
|
43
|
+
setState(() {
|
44
|
+
color = randomColor();
|
45
|
+
borderRadius = randomBorderRadius();
|
46
|
+
margin = randomMargin();
|
47
|
+
});
|
48
|
+
}
|
49
|
+
|
42
50
|
@override
|
43
51
|
Widget build(BuildContext context) {
|
44
52
|
return Scaffold(
|
3. 为动画设置触发器
3. Set up a trigger for the animation
每当用户点击 Change 按钮时触发动画,调用 onPressed()
处理器的 change()
方法:
To set the animation to trigger whenever the user presses the Change button,
invoke the change()
method in the onPressed()
handler:
@@ -67,7 +67,7 @@
|
|
67
67
|
),
|
68
68
|
ElevatedButton(
|
69
69
|
child: const Text('change'),
|
70
|
-
onPressed: () =>
|
70
|
+
onPressed: () => change(),
|
71
71
|
),
|
72
72
|
],
|
73
73
|
),
|
4. 设置时长
4. Set duration
在最后,设置新旧值之间变换的时长参数 duration
:
Finally, set the duration
of the animation that powers the transition
between the old and new values:
@@ -2,12 +2,12 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license
|
3
3
|
// that can be found in the LICENSE file.
|
4
|
-
// ignore_for_file: missing_required_argument
|
5
|
-
|
6
4
|
import 'dart:math';
|
7
5
|
import 'package:flutter/material.dart';
|
6
|
+
const _duration = Duration(milliseconds: 400);
|
7
|
+
|
8
8
|
double randomBorderRadius() {
|
9
9
|
return Random().nextDouble() * 64;
|
10
10
|
}
|
@@ -63,6 +63,7 @@
|
|
63
63
|
color: color,
|
64
64
|
borderRadius: BorderRadius.circular(borderRadius),
|
65
65
|
),
|
66
|
+
duration: _duration,
|
66
67
|
),
|
67
68
|
),
|
68
69
|
ElevatedButton(
|
形状变化 (完成代码)
Shape-shifting (complete)
下面的示例是修改后的完成版代码—
运行这个示例,然后点击 Change 按钮就可以触发动画。注意:每次您点击 Change 按钮,形状的 margin
、borderRadius
和 color
都会进行动画变化到新的值。
Here’s the example with the completed changes you’ve made—run the code
and click the Change button to trigger the animation. Notice that each time
you click the Change button, the shape animates to its new values
for margin
, borderRadius
, and color
.
{$ begin main.dart $}
import 'dart:math';
import 'package:flutter/material.dart';
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 {
const AnimatedContainerDemo({Key? key}) : super(key: key);
@override
_AnimatedContainerDemoState createState() => _AnimatedContainerDemoState();
}
class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
late Color color;
late double borderRadius;
late double margin;
@override
initState() {
super.initState();
color = randomColor();
borderRadius = randomBorderRadius();
margin = randomMargin();
}
void change() {
setState(() {
color = randomColor();
borderRadius = randomBorderRadius();
margin = randomMargin();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
SizedBox(
width: 128,
height: 128,
child: AnimatedContainer(
margin: EdgeInsets.all(margin),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(borderRadius),
),
duration: _duration,
),
),
ElevatedButton(
child: const Text('change'),
onPressed: () => change(),
),
],
),
),
);
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: AnimatedContainerDemo(),
);
}
}
void main() {
runApp(
const MyApp(),
);
}
{$ end main.dart $}
使用动画曲线
Using animation curves
前面的示例展示出,如何让您通过隐式动画对特定的 widget 属性值进行动画变化,以及如何通过 duration
参数设置动画完成所需的时间。隐式动画还允许您在 duration
时长内控制动画的 速率 变化。用来定义这种速率变化的参数是 curve。
The preceding examples show how implicit animations allow you to animate
changes in values for specific widget properties, and how the
duration
parameter allows you to set the amount of time an
animation takes to complete. Implicit animations also allow you to
control changes to the rate of an animation within the duration
.
The parameter you use to define this change in rate is curve.
前面的例子中没有指定 curve
,所以隐式动画默认使用 线性动画曲线。在 形状变化完成代码 中添加一个 curve
参数,然后当您将常量 easeInOutBack 传递给 curve
时,观察动画的变化:
The preceding examples do not specify a curve
,
so the implicit animations apply a linear animation curve by default.
Add a curve
parameter to the shape-shifting complete
and watch how the animation changes when you pass the
easeInOutBack constant for curve
:
@@ -64,6 +64,7 @@
|
|
64
64
|
borderRadius: BorderRadius.circular(borderRadius),
|
65
65
|
),
|
66
66
|
duration: _duration,
|
67
|
+
curve: Curves.easeInOutBack,
|
67
68
|
),
|
68
69
|
),
|
69
70
|
ElevatedButton(
|
现在您已经将 easeInOutBack
作为 curve
的值传递给了 AnimatedContainer
,注意:margin
、borderRadius
和 color
的变化速率遵循 easeInOutBack
所定义的曲线:
Now that you have passed easeInOutBack
as the value for curve
to
AnimatedContainer
, notice that the rates of change for margin
,
borderRadius
, and color
follow the curve defined by the
easeInOutBack
curve:
小结一下
Putting it all together
形状变化完成代码 示例对 margin
、borderRadius
和 color
属性值进行了动画变换。注意:AnimatedContainer
可以对它的任意属性进行动画改变,包括那些您没有使用的属性,比如 padding
、transform
,甚至是 child
和 alignment
!
这个 形状变化完成代码 的示例建立在 渐变完成代码 的基础上,展现出隐式动画的额外功能:
The shape-shifting complete example animates transitions between values for
margin
, borderRadius
, and color
properties.
Note that AnimatedContainer
animates changes to any of its properties,
including those you didn’t use such as padding
, transform
,
and even child
and alignment
!
The shape-shifting complete example builds upon fade-in complete by showing
additional capabilities of implicit animations:
-
一些隐式动画(比如
AnimatedOpacity
)只能对一个属性值进行动画变换,然而有些(比如AnimatedContainer
)可以同时变换多个属性。Some implicit animations (for example,
AnimatedOpacity
) only animate a single property, while others (likeAnimatedContainer
) can animate many properties. -
隐式动画会在新旧属性值变换时,自动使用提供的
curve
和duration
进行动画变换。Implicit animations automatically animate between the old and new values of properties when they change using the provided
curve
andduration
. -
如果您没有指定
curve
,隐式动画的曲线会默认使用 线性曲线。If you do not specify a
curve
, implicit animations default to a linear curve.
下一个是什么?
What’s next?
恭喜,您已经完成了这个 codelab!如果您想要了解更多,这里有一些其他文章的推荐:
Congratulations, you’ve finished the codelab! If you’d like to learn more, here are some suggestions for where to go next:
-
尝试一下 动画教程。
Try the animations tutorial.
-
学习 hero 动画 和 staggered 动画。
Learn about hero animations and staggered animations.
-
查看更多 动画库 的信息。
Checkout the animation library.
-
尝试一下其他的 codelab。
Try another codelab.