Flutter Motion Kit

可预览的 Flutter 动画 + 避坑指南 · 支持 Claude Code 一键复用

← 返回

AnimatedSwitcher 内容切换

分类 implicit · 难度 2/5 · 验证于 Flutter 3.32 / 2026-06

在新旧内容之间做淡入缩放过渡(如计数器、加载/成功状态切换)。关键是给子组件唯一 Key。

⏳ 在线预览未就绪:运行 node scripts/sync-gists.mjs 生成 gist 后即可内嵌。

代码

// ✅ 推荐:给 child 唯一 ValueKey,AnimatedSwitcher 才能识别「内容变了」并过渡。
// 可直接粘进 DartPad (https://dartpad.dev) 运行。
import 'package:flutter/material.dart';

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

class _App extends StatelessWidget {
  const _App();
  @override
  Widget build(BuildContext context) => const MaterialApp(
        debugShowCheckedModeBanner: false,
        home: _Demo(),
      );
}

class _Demo extends StatefulWidget {
  const _Demo();
  @override
  State<_Demo> createState() => _DemoState();
}

class _DemoState extends State<_Demo> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => _count++),
        child: const Icon(Icons.add),
      ),
      body: Center(
        child: AnimatedSwitcher(
          duration: const Duration(milliseconds: 350),
          transitionBuilder: (child, animation) => ScaleTransition(
            scale: animation,
            child: FadeTransition(opacity: animation, child: child),
          ),
          child: Text(
            '$_count',
            // 关键:用 ValueKey 标识不同内容,否则不会有过渡
            key: ValueKey<int>(_count),
            style: const TextStyle(fontSize: 96, fontWeight: FontWeight.bold),
          ),
        ),
      ),
    );
  }
}

⚠️ 坑(3)

子组件不带唯一 Key 时,AnimatedSwitcher 判定为「同一个 widget」,根本不触发过渡。
✅ 给 child 设 ValueKey(用能区分新旧内容的值),内容变化才会被识别。
official-docs · 出处
它只对「child 整体替换」生效,对 child 内部状态变化无效。
✅ 状态变化要体现成不同的 child(或不同 Key)。
official-docs · 出处
transitionBuilder 同时作用于进入和离开,离开用的是上一个 child。
✅ 组合 FadeTransition/ScaleTransition 时确保对两端都成立,避免离开态错位。
official-docs · 出处

官方文档