隐式动画
欢迎来到隐式动画代码实验室,在这里您将学习如何使用 Flutter 小部件轻松创建特定属性的动画。
为了充分利用此代码实验室,您应该具备以下方面的基本知识:
- 如何 创建 Flutter 应用。
- 如何使用 有状态小部件。
此代码实验室涵盖以下内容:
- 使用
AnimatedOpacity
创建淡入效果。 - 使用
AnimatedContainer
为大小、颜色和边距的转换创建动画。 - 隐式动画概述以及使用隐式动画的技术。
完成此代码实验室的预计时间:15-30 分钟。
什么是隐式动画?
#使用 Flutter 的 动画库,您可以为 UI 中的小部件添加运动并创建视觉效果。库中的一组小部件可以为您管理动画。这些小部件统称为隐式动画或隐式动画小部件,其名称源自它们实现的 ImplicitlyAnimatedWidget 类。使用隐式动画,您可以通过设置目标值来为小部件属性创建动画;每当该目标值发生变化时,小部件都会将属性从旧值动画到新值。这样,隐式动画以便利性换取了控制权——它们管理动画效果,因此您无需手动操作。
示例:淡入文本效果
#以下示例显示了如何使用称为 AnimatedOpacity 的隐式动画小部件,为现有 UI 添加淡入效果。示例从没有动画代码开始——它包含一个 Material App 主屏幕,其中包含:
- 猫头鹰的照片。
- 一个显示详情按钮,点击时没有任何反应。
- 照片中猫头鹰的描述文本。
淡入(起始代码)
#要查看示例,请点击运行。
// Copyright 2019 the Dart project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.
import 'package:flutter/material.dart';
const owlUrl =
'https://raw.githubusercontent.com/flutter/website/main/src/content/assets/images/docs/owl.jpg';
class FadeInDemo extends StatefulWidget {
const FadeInDemo({super.key});
@override
State<FadeInDemo> createState() => _FadeInDemoState();
}
class _FadeInDemoState extends State<FadeInDemo> {
@override
Widget build(BuildContext context) {
return ListView(children: <Widget>[
Image.network(owlUrl),
TextButton(
child: const Text(
'Show Details',
style: TextStyle(color: Colors.blueAccent),
),
onPressed: () => {},
),
const Column(
children: [
Text('Type: Owl'),
Text('Age: 39'),
Text('Employment: None'),
],
)
]);
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: FadeInDemo(),
),
),
);
}
}
void main() {
runApp(
const MyApp(),
);
}
使用 AnimatedOpacity 组件为不透明度设置动画
#本节包含一个步骤列表,您可以使用这些步骤将隐式动画添加到 淡入起始代码 中。在完成这些步骤后,您还可以运行 淡入完整代码,其中已进行了更改。这些步骤概述了如何使用 AnimatedOpacity
小部件添加以下动画功能:
- 在用户点击显示详情之前,猫头鹰的描述文本保持隐藏状态。
- 当用户点击显示详情时,猫头鹰的描述文本淡入。
1. 选择要为其创建动画的小部件属性
#要创建淡入效果,您可以使用 AnimatedOpacity
小部件为 opacity
属性创建动画。将 Column
小部件包装在 AnimatedOpacity
小部件中。
@override
Widget build(BuildContext context) {
return ListView(children: <Widget>[
Image.network(owlUrl),
TextButton(
child: const Text(
'Show Details',
style: TextStyle(color: Colors.blueAccent),
),
onPressed: () => {},
),
const Column(
children: [
Text('Type: Owl'),
Text('Age: 39'),
Text('Employment: None'),
],
),
AnimatedOpacity(
child: const Column(
children: [
Text('Type: Owl'),
Text('Age: 39'),
Text('Employment: None'),
],
),
),
]);
}
2. 为动画属性初始化一个状态变量
#要在用户点击显示详情之前隐藏文本,请将 opacity
的起始值设置为零。
class _FadeInDemoState extends State<FadeInDemo> {
double opacity = 0;
@override
Widget build(BuildContext context) {
return ListView(children: <Widget>[
// ...
AnimatedOpacity(
opacity: opacity,
child: const Column(
3. 设置动画持续时间
#除了 opacity
参数外,AnimatedOpacity
还需要一个 duration 用于其动画。对于此示例,您可以从 2 秒开始。
AnimatedOpacity(
duration: const Duration(seconds: 2),
opacity: opacity,
child: const Column(
4. 设置动画触发器并选择结束值
#配置动画,使其在用户点击显示详情时触发。为此,请使用 TextButton
的 onPressed()
处理程序更改 opacity
状态。为了使 FadeInDemo
小部件在用户点击显示详情时完全可见,请使用 onPressed()
处理程序将 opacity
设置为 1。
TextButton(
child: const Text(
'Show Details',
style: TextStyle(color: Colors.blueAccent),
),
onPressed: () => {},
onPressed: () => setState(() {
opacity = 1;
}),
),
淡入(完整)
#这是您已完成更改的示例。运行此示例,然后点击显示详情以触发动画。
// Copyright 2019 the Dart project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.
import 'package:flutter/material.dart';
const owlUrl =
'https://raw.githubusercontent.com/flutter/website/main/src/content/assets/images/docs/owl.jpg';
class FadeInDemo extends StatefulWidget {
const FadeInDemo({super.key});
@override
State<FadeInDemo> createState() => _FadeInDemoState();
}
class _FadeInDemoState extends State<FadeInDemo> {
double opacity = 0;
@override
Widget build(BuildContext context) {
return ListView(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: const Column(
children: [
Text('Type: Owl'),
Text('Age: 39'),
Text('Employment: None'),
],
),
)
]);
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: FadeInDemo(),
),
),
);
}
}
void main() {
runApp(
const MyApp(),
);
}
整合在一起
#淡入文本效果 示例演示了 AnimatedOpacity
小部件的以下功能。
- 它侦听其
opacity
属性的状态更改。 - 当
opacity
属性发生更改时,它会将转换动画化到opacity
的新值。 - 它需要一个
duration
参数来定义值之间转换需要花费多长时间。
示例:形状转换效果
#以下示例显示了如何使用 AnimatedContainer
小部件为多种属性(margin
、borderRadius
和 color
)创建不同类型(double
和 Color
)的动画。示例从没有动画代码开始。它从一个 Material App 主屏幕开始,其中包含:
- 一个
Container
小部件,配置了borderRadius
、margin
和color
。每次运行示例时,都会重新生成这些属性。 - 一个更改按钮,点击时没有任何反应。
形状转换(起始代码)
#要开始示例,请点击运行。
// Copyright 2019 the Dart project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.
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({super.key});
@override
State<AnimatedContainerDemo> createState() => _AnimatedContainerDemoState();
}
class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
late Color color;
late double borderRadius;
late double margin;
@override
void 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({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: AnimatedContainerDemo(),
);
}
}
void main() {
runApp(
const MyApp(),
);
}
使用 AnimatedContainer 为颜色、borderRadius 和 margin 设置动画
#本节包含一个步骤列表,您可以使用这些步骤将隐式动画添加到 形状转换起始代码 中。完成每个步骤后,您还可以运行 形状转换完整示例,其中已进行了更改。
形状转换起始代码 为 Container
小部件中的每个属性分配了一个随机值。相关函数会生成相关值:
randomColor()
函数为color
属性生成一个Color
。randomBorderRadius()
函数为borderRadius
属性生成一个double
。randomMargin()
函数为margin
属性生成一个double
。
以下步骤使用 AnimatedContainer
小部件:
- 在用户点击更改时,转换为
color
、borderRadius
和margin
的新值。 - 为
color
、borderRadius
和margin
设置新值时,为转换创建动画。
1. 添加隐式动画
#将 Container
小部件更改为 AnimatedContainer
小部件。
SizedBox(
width: 128,
height: 128,
child: Container(
child: AnimatedContainer(
margin: EdgeInsets.all(margin),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(borderRadius),
),
),
),
2. 设置动画属性的起始值
#当 AnimatedContainer
小部件的属性发生更改时,它会在旧值和新值之间进行转换。为了包含用户点击更改时触发的行为,请创建一个 change()
方法。change()
方法可以使用 setState()
方法为 color
、borderRadius
和 margin
状态变量设置新值。
void change() {
setState(() {
color = randomColor();
borderRadius = randomBorderRadius();
margin = randomMargin();
});
}
@override
Widget build(BuildContext context) {
// ...
3. 设置动画触发器
#要将动画设置为在用户按下更改时触发,请在 onPressed()
处理程序中调用 change()
方法。
ElevatedButton(
child: const Text('Change'),
onPressed: () => {},
onPressed: () => change(),
),
4. 设置持续时间
#设置驱动旧值和新值之间转换的动画的 duration
。
SizedBox(
width: 128,
height: 128,
child: AnimatedContainer(
margin: EdgeInsets.all(margin),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(borderRadius),
),
duration: const Duration(milliseconds: 400),
),
),
形状转换(完整)
#这是您已完成更改的示例。运行代码并点击更改以触发动画。每次点击更改时,形状都会将其 margin
、borderRadius
和 color
的值动画化为新值。
// Copyright 2019 the Dart project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.
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({super.key});
@override
State<AnimatedContainerDemo> createState() => _AnimatedContainerDemoState();
}
class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
late Color color;
late double borderRadius;
late double margin;
@override
void 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({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: AnimatedContainerDemo(),
);
}
}
void main() {
runApp(
const MyApp(),
);
}
使用动画曲线
#前面的示例显示了如何:
- 隐式动画允许您为特定小部件属性的值之间的转换创建动画。
duration
参数允许您设置动画完成所需的时间。
隐式动画还允许您控制在设置的 duration
期间发生的动画的速率更改。要定义此速率更改,请将 curve
参数的值设置为 Curve
,例如在 Curves
类中声明的一个。
前面的示例未为 curve
参数指定值。如果没有指定曲线值,则隐式动画会应用 线性动画曲线。
在 形状转换完整示例 中为 curve
参数指定一个值。当您为 curve
传递 easeInOutBack
常量时,动画会发生变化,
SizedBox(
width: 128,
height: 128,
child: AnimatedContainer(
margin: EdgeInsets.all(margin),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(borderRadius),
),
duration: _duration,
curve: Curves.easeInOutBack,
),
),
当您将 Curves.easeInOutBack
常量传递给 AnimatedContainer
小部件的 curve
属性时,请观察 margin
、borderRadius
和 color
的变化速率如何遵循该常量定义的曲线。
整合在一起
#形状转换完整示例 为 margin
、borderRadius
和 color
属性的值之间的转换创建动画。AnimatedContainer
小部件会为其任何属性的更改创建动画。这些包括您未使用的属性,例如 padding
、transform
,甚至 child
和 alignment
!通过展示隐式动画的其他功能,形状转换完整示例 在 淡入完整示例 的基础上进行了构建。
总结隐式动画:
- 一些隐式动画,例如
AnimatedOpacity
小部件,只为一个属性创建动画。其他一些,例如AnimatedContainer
小部件,可以为多个属性创建动画。 - 隐式动画在属性值更改时,使用提供的
curve
和duration
来动画化旧值和新值之间的过渡。 - 如果未指定
curve
,则隐式动画默认为线性曲线。
下一步是什么?
#恭喜你完成了这个Codelab!要了解更多信息,请查看以下建议
除非另有说明,否则本网站上的文档反映了 Flutter 的最新稳定版本。页面最后更新于 2024-08-16。 查看源代码 或 报告问题.