Widget 的淡入淡出效果

在實現 UI 設計時,我們經常需要在螢幕上顯示或隱藏各種元素。如若這個過程只是讓某個元素快速地出現或者消失,使用者們肯定不買帳。我們一般會使用不透明動畫讓元素淡入淡出,以建立出更加流暢的使用者體驗。

在 Flutter 中,你可以使用 AnimatedOpacity widget 來完成這個效果,請參見下面的步驟:

  1. 建立一個用來淡入淡出的方框。

  2. 定義一個 StatefulWidget

  3. 顯示一個用於切換可見狀態的按鈕

  4. 淡入淡出方框。

1. 建立一個用來淡入淡出的方框

首先是建立一個來淡入淡出的東西。在這個範例中,你將在螢幕上繪製一個綠色的方框。

Container(
  width: 200,
  height: 200,
  color: Colors.green,
)

2. 定義一個 StatefulWidget

我們要對這個綠色的方框進行動畫。那麼為了表示這個方框的狀態是否可見,你需要使用 StatefulWidget

StatefulWidget 是一個類,它將會建立一個 State 物件。而這個 State 物件將包含與這個應用相關的一些資料,並且能夠更新它們。當你更新資料時,可以讓Flutter使用這些更改去重建使用者介面。

在這個範例中,我們將使用一個布林值來表示其是否可見。

要構造一個 StatefulWidget,你需要建立兩個類:一個 StatefulWidget 類以及與其對應的 State 類。小提示:Android Studio 和 VSCode 的 Flutter 外掛都包含了 stful 片段,能夠快速生成該程式碼。

// The StatefulWidget's job is to take data and create a State class.
// In this case, the widget takes a title, and creates a _MyHomePageState.
class MyHomePage extends StatefulWidget {
  final String title;

  const MyHomePage({
    super.key,
    required this.title,
  });

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

// The State class is responsible for two things: holding some data you can
// update and building the UI using that data.
class _MyHomePageState extends State<MyHomePage> {
  // Whether the green box should be visible.
  bool _visible = true;

  @override
  Widget build(BuildContext context) {
    // The green box goes here with some other Widgets.
  }
}

3. 顯示一個用於切換可見狀態的按鈕

現在你已經有了一些資料能夠決定這個綠色方框是否可見,但是還需要一個方法來改變這些資料。在這個例子中,我們想讓方框在顯示與隱藏之間切換。

為此你將使用一個按鈕——當用戶按下按鈕時,資料將會在 true 和 false 之間進行切換。為了使改變生效,你需要使用 State 類別中的 setState() 方法,這會使 Flutter 重建這個 widget。

注意:如果你想要瞭解更多與使用者輸入相關的資料,請參閱 Cookbook 的 Gestures 部分。

FloatingActionButton(
  onPressed: () {
    // Call setState. This tells Flutter to rebuild the
    // UI with the changes.
    setState(() {
      _visible = !_visible;
    });
  },
  tooltip: 'Toggle Opacity',
  child: const Icon(Icons.flip),
)

4. 淡入淡出方框

現在你的螢幕上已經有一個綠色的方框,以及一個可以透過改變 truefalse 來切換方框可見性的按鈕。那麼該如何讓方框淡入淡出呢?答案是使用 AnimatedOpacity widget。

AnimatedOpacity 小部件需要傳入三個引數:

  • opacity:它的取值範圍從 0.0(不可見)到 1.0(完全可見)。

  • duration:代表這個動畫需要持續多長時間。

  • child:需要進行動畫的小部件。在這個例子中就是那個綠色的方框。

AnimatedOpacity(
  // If the widget is visible, animate to 0.0 (invisible).
  // If the widget is hidden, animate to 1.0 (fully visible).
  opacity: _visible ? 1.0 : 0.0,
  duration: const Duration(milliseconds: 500),
  // The green box must be a child of the AnimatedOpacity widget.
  child: Container(
    width: 200,
    height: 200,
    color: Colors.green,
  ),
)

互動式範例

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    const appTitle = 'Opacity Demo';
    return const MaterialApp(
      title: appTitle,
      home: MyHomePage(title: appTitle),
    );
  }
}

// The StatefulWidget's job is to take data and create a State class.
// In this case, the widget takes a title, and creates a _MyHomePageState.
class MyHomePage extends StatefulWidget {
  const MyHomePage({
    super.key,
    required this.title,
  });

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

// The State class is responsible for two things: holding some data you can
// update and building the UI using that data.
class _MyHomePageState extends State<MyHomePage> {
  // Whether the green box should be visible
  bool _visible = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: AnimatedOpacity(
          // If the widget is visible, animate to 0.0 (invisible).
          // If the widget is hidden, animate to 1.0 (fully visible).
          opacity: _visible ? 1.0 : 0.0,
          duration: const Duration(milliseconds: 500),
          // The green box must be a child of the AnimatedOpacity widget.
          child: Container(
            width: 200,
            height: 200,
            color: Colors.green,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Call setState. This tells Flutter to rebuild the
          // UI with the changes.
          setState(() {
            _visible = !_visible;
          });
        },
        tooltip: 'Toggle Opacity',
        child: const Icon(Icons.flip),
      ),
    );
  }
}