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),
        ),
      ),
    );
  }
}