隐式动画
欢迎来到隐式动画代码实验室,您将学习如何使用 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. 选择要动画化的 Widget 属性
#要创建淡入效果,您可以使用 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
小部件使用不同类型(double
和 Color
)动画化多个属性(margin
、borderRadius
和 color
)。 示例一开始没有动画代码 。它从包含以下内容的Material App 主屏幕开始:
- 使用
borderRadius
、margin
和color
配置的Container
小部件。每次运行示例时都会重新生成这些属性。 - 单击时不执行任何操作的 更改 按钮。
形状变化(起始代码)
#要启动示例,请单击 运行 。
// 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 和边距
#本节包含一个步骤列表,您可以使用这些步骤将隐式动画添加到形状变化起始代码。完成每个步骤后,您还可以运行已完成更改的完整的形状变化示例。
形状变化起始代码 为 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
,则隐式动画默认为线性曲线。
接下来的步骤?
#恭喜您完成了代码实验室!要了解更多信息,请查看以下建议:
除非另有说明,否则本网站上的文档反映的是 Flutter 的最新稳定版本。页面最后更新于 2025-01-30。 查看源代码 或 报告问题。