第 10 章 动画(完整可运行示例)

三个层次:隐式动画(改参数自动过渡)→ 显式动画(控制器完全掌控)→ Hero(页面间共享元素)。一个文件跑完三种。

创建步骤
  1. flutter create anim_demo && cd anim_demo;
  2. 替换 lib/main.dart,无需额外依赖;
  3. 点方块切换大小、点「播放淡入」看控制器动画、点头像跳详情看 Hero 飞行。
import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: AnimDemoPage()));

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

  @override
  State<AnimDemoPage> createState() => _AnimDemoPageState();
}

// SingleTickerProviderStateMixin 提供 vsync(把动画帧对齐屏幕刷新)
class _AnimDemoPageState extends State<AnimDemoPage>
    with SingleTickerProviderStateMixin {
  bool _big = false;

  late final AnimationController _controller = AnimationController(
    vsync: this,
    duration: const Duration(milliseconds: 900),
  );
  late final Animation<double> _opacity =
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut);

  @override
  void dispose() {
    _controller.dispose(); // 必须释放,否则页面退出后仍在跑
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('动画示例')),
      body: ListView(
        padding: const EdgeInsets.all(20),
        children: [
          const Text('① 隐式动画:AnimatedContainer', style: TextStyle(fontWeight: FontWeight.w600)),
          const SizedBox(height: 10),
          Center(
            child: GestureDetector(
              onTap: () => setState(() => _big = !_big),
              child: AnimatedContainer(
                duration: const Duration(milliseconds: 350),
                curve: Curves.easeOutBack, // 带一点回弹
                width: _big ? 180 : 90,
                height: _big ? 180 : 90,
                decoration: BoxDecoration(
                  color: _big ? Colors.orange : Colors.teal,
                  borderRadius: BorderRadius.circular(_big ? 40 : 12),
                ),
                alignment: Alignment.center,
                child: const Text('点我', style: TextStyle(color: Colors.white)),
              ),
            ),
          ),
          const SizedBox(height: 34),

          const Text('② 显式动画:AnimationController + FadeTransition',
              style: TextStyle(fontWeight: FontWeight.w600)),
          const SizedBox(height: 10),
          Center(
            child: FadeTransition(
              opacity: _opacity,
              child: const FlutterLogo(size: 90),
            ),
          ),
          const SizedBox(height: 10),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              OutlinedButton(
                onPressed: () => _controller.forward(from: 0),
                child: const Text('播放淡入'),
              ),
              const SizedBox(width: 10),
              OutlinedButton(
                onPressed: () => _controller.reverse(),
                child: const Text('淡出'),
              ),
            ],
          ),
          const SizedBox(height: 34),

          const Text('③ Hero:页面间共享元素动画',
              style: TextStyle(fontWeight: FontWeight.w600)),
          const SizedBox(height: 10),
          Center(
            child: GestureDetector(
              onTap: () => Navigator.push(
                context,
                MaterialPageRoute(builder: (_) => const DetailPage()),
              ),
              child: const Hero(
                tag: 'logo', // 两页 tag 必须一致
                child: FlutterLogo(size: 64),
              ),
            ),
          ),
          const SizedBox(height: 8),
          const Center(
            child: Text('点 Logo 进入详情,观察它「飞」过去',
                style: TextStyle(fontSize: 12, color: Colors.grey)),
          ),
        ],
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('详情')),
      body: const Center(
        child: Hero(
          tag: 'logo',
          child: FlutterLogo(size: 180), // 尺寸不同 → 自动补间放大
        ),
      ),
    );
  }
}
关键点
动态效果 · 隐式动画补间
Animated
← 上一章第 9 章 数据持久化(两个完整示例)下一章 →第 11 章 主题与深色模式(完整可运行示例)
✉ 联系作者:linghurongjun@sina.com