Flutter 从入门到精通(水)

43 阅读2分钟

第10章:Flutter 动画与交互效果

优秀的 App UI 离不开动画和交互设计。Flutter 拥有强大的动画系统,支持声明式动画、隐式动画、显式动画、过渡动画等,几乎可以满足你对动效的所有想象。


一、Flutter 动画系统简介

类型说明示例组件
隐式动画自动处理状态变化的过渡动画AnimatedContainer, AnimatedOpacity
显式动画完全自定义动画控制器、曲线、时长等AnimationController, Tween
过渡动画页面间跳转动画Hero, PageRouteBuilder

二、隐式动画(最简单易用)

1. AnimatedContainer

class MyAnimatedBox extends StatefulWidget {
  @override
  _MyAnimatedBoxState createState() => _MyAnimatedBoxState();
}

class _MyAnimatedBoxState extends State<MyAnimatedBox> {
  double _width = 100;

  void _toggle() {
    setState(() {
      _width = _width == 100 ? 200 : 100;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        AnimatedContainer(
          width: _width,
          height: 100,
          duration: Duration(milliseconds: 300),
          color: Colors.blue,
        ),
        ElevatedButton(onPressed: _toggle, child: Text("Animate"))
      ],
    );
  }
}

2. AnimatedOpacity(透明动画)

AnimatedOpacity(
  opacity: _visible ? 1.0 : 0.0,
  duration: Duration(seconds: 1),
  child: Text("Hello Flutter"),
)

三、显式动画(高级控制)

1. AnimationController + Tween + AnimatedBuilder

class MyFadeIn extends StatefulWidget {
  @override
  _MyFadeInState createState() => _MyFadeInState();
}

class _MyFadeInState extends State<MyFadeIn> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _fade;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(duration: Duration(seconds: 2), vsync: this);
    _fade = Tween(begin: 0.0, end: 1.0).animate(_controller);
    _controller.forward(); // 启动动画
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _fade,
      builder: (_, child) => Opacity(opacity: _fade.value, child: child),
      child: Text("Fade In Text", style: TextStyle(fontSize: 24)),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

2. TweenAnimationBuilder(简化 Tween 动画)

TweenAnimationBuilder<double>(
  tween: Tween(begin: 0, end: 1),
  duration: Duration(seconds: 1),
  builder: (context, value, child) {
    return Opacity(opacity: value, child: child);
  },
  child: Text("Tween Fade"),
)

四、页面过渡动画(Hero 动画)

用于两个页面之间共享一个元素的“跳转动效”。

示例:

页面 A:
Hero(
  tag: 'hero-image',
  child: Image.asset('assets/image.png', width: 100),
)
页面 B:
Hero(
  tag: 'hero-image',
  child: Image.asset('assets/image.png', width: 300),
)

注意:

  • tag 要一致
  • 图片或 Widget 类型一致性要高

五、交互动画推荐组件

组件作用
AnimatedCrossFade两个组件之间渐变切换
AnimatedSwitcher子组件变化时动画切换
AnimatedIconFlutter 自带动画图标
InteractiveViewer支持拖拽、缩放、旋转
Draggable / DragTarget实现拖拽交互(如拖动文件)

六、常见问题解析

❗ 动画不生效或不流畅?

  • 忘记使用 StatefulWidget
  • 控制器未调用 forward() 或未释放资源
  • 使用动画嵌套组件时嵌套层级过深,建议合理拆分

❗ Hero 动画跳转失败?

  • 检查两个页面的 Hero 是否都设置了相同 tag
  • child 类型不一致,例如一个用的是 Image.asset,另一个用了 ClipRRect + Image.asset

❗ Tween 数值类型不匹配?

  • Tween<double> 对应的是 double
  • Tween<Offset>Tween<Color> 要用对应类型