Container 裡的動畫漸變效果

Container 類提供了一系列實用方法,能夠便捷地建立出一個具有指定寬度、高度、背景顏色、外邊距和邊框等屬性的 widget。

簡單的動畫通常會在一段時間內改變這些屬性。例如你可能想將灰色背景逐漸變為綠色背景來告訴使用者已經選擇了某個專案。

為了製作這樣的簡單動畫效果,Flutter 提供了 AnimatedContainer widget。與 Container 一樣, AnimatedContainer 也可以設定它的寬度、高度以及背景顏色等等。但是 AnimatedContainer 在使用新屬性進行重建時,將會自動在舊值和新值之間生成動畫。這種動畫在 Flutter 中被稱為『隱式動畫』。

下面這篇教程將介紹如何使用 AnimatedContainer 實現當用戶點選按鈕時改變它的大小、背景顏色,以及邊框半徑的動畫。

步驟

  1. 建立一個擁有預設屬性的 StatefulWidget

  2. 建立一個使用這些屬性的 AnimatedContainer

  3. 透過設定新的屬性觸發重建並啟動動畫

1. 建立一個擁有預設屬性的 StatefulWidget

首先你需要建立一個 StatefulWidget 類別和 State 類。然後在 State 類別中定義需要隨時間更改的屬性。在這個範例中,我們將會改變其寬度、高度、顏色和邊框半徑。此外,你還可以定義其他預設屬性。

但是這些屬性必須定義在 State 類別中,這樣我們才能在使用者點選按鈕時更新它們。

class AnimatedContainerApp extends StatefulWidget {
  const AnimatedContainerApp({super.key});

  @override
  State<AnimatedContainerApp> createState() => _AnimatedContainerAppState();
}

class _AnimatedContainerAppState extends State<AnimatedContainerApp> {
  // Define the various properties with default values. Update these properties
  // when the user taps a FloatingActionButton.
  double _width = 50;
  double _height = 50;
  Color _color = Colors.green;
  BorderRadiusGeometry _borderRadius = BorderRadius.circular(8);

  @override
  Widget build(BuildContext context) {
    // Fill this out in the next steps.
  }
}

2. 建立一個使用這些屬性的 AnimatedContainer

接下來,你就可以使用上一步中定義的屬性來建立 AnimatedContainer。此外,你還必須提供一個 duration 它將定義這個動畫應該執行多長時間。

AnimatedContainer(
  // Use the properties stored in the State class.
  width: _width,
  height: _height,
  decoration: BoxDecoration(
    color: _color,
    borderRadius: _borderRadius,
  ),
  // Define how long the animation should take.
  duration: const Duration(seconds: 1),
  // Provide an optional curve to make the animation feel smoother.
  curve: Curves.fastOutSlowIn,
)

3. 透過設定新的屬性觸發重建並啟動動畫

最後將設定新的屬性觸發 AnimatedContainer 重建並啟動動畫。那麼如何觸發重建呢?當我們提到 StatefulWidgets 時,setState() 就行了。

在這個例子中,我們給應用新增了一個按鈕。當用戶點選按鈕時,將會呼叫 setState 去重新整理它的寬度、高度、背景顏色和邊框半徑等屬性。

實際專案通常只會在某些固定值之間進行轉換(例如從灰色背景轉場到綠色背景)。在這個應用中,每次使用者點選按鈕都會生成新的值。

FloatingActionButton(
  // When the user taps the button
  onPressed: () {
    // Use setState to rebuild the widget with new values.
    setState(() {
      // Create a random number generator.
      final random = Random();

      // Generate a random width and height.
      _width = random.nextInt(300).toDouble();
      _height = random.nextInt(300).toDouble();

      // Generate a random color.
      _color = Color.fromRGBO(
        random.nextInt(256),
        random.nextInt(256),
        random.nextInt(256),
        1,
      );

      // Generate a random border radius.
      _borderRadius =
          BorderRadius.circular(random.nextInt(100).toDouble());
    });
  },
  child: const Icon(Icons.play_arrow),
)

互動式範例

import 'dart:math';

import 'package:flutter/material.dart';

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

class AnimatedContainerApp extends StatefulWidget {
  const AnimatedContainerApp({super.key});

  @override
  State<AnimatedContainerApp> createState() => _AnimatedContainerAppState();
}

class _AnimatedContainerAppState extends State<AnimatedContainerApp> {
  // Define the various properties with default values. Update these properties
  // when the user taps a FloatingActionButton.
  double _width = 50;
  double _height = 50;
  Color _color = Colors.green;
  BorderRadiusGeometry _borderRadius = BorderRadius.circular(8);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('AnimatedContainer Demo'),
        ),
        body: Center(
          child: AnimatedContainer(
            // Use the properties stored in the State class.
            width: _width,
            height: _height,
            decoration: BoxDecoration(
              color: _color,
              borderRadius: _borderRadius,
            ),
            // Define how long the animation should take.
            duration: const Duration(seconds: 1),
            // Provide an optional curve to make the animation feel smoother.
            curve: Curves.fastOutSlowIn,
          ),
        ),
        floatingActionButton: FloatingActionButton(
          // When the user taps the button
          onPressed: () {
            // Use setState to rebuild the widget with new values.
            setState(() {
              // Create a random number generator.
              final random = Random();

              // Generate a random width and height.
              _width = random.nextInt(300).toDouble();
              _height = random.nextInt(300).toDouble();

              // Generate a random color.
              _color = Color.fromRGBO(
                random.nextInt(256),
                random.nextInt(256),
                random.nextInt(256),
                1,
              );

              // Generate a random border radius.
              _borderRadius =
                  BorderRadius.circular(random.nextInt(100).toDouble());
            });
          },
          child: const Icon(Icons.play_arrow),
        ),
      ),
    );
  }
}