隱式動畫
歡迎來到隱式動畫程式碼實驗室,您將在此學習如何使用 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 首頁畫面開始:
- 一個使用
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 的最新穩定版本。頁面最後更新於 2024-08-16。 檢視原始碼 或回報問題。