flutter-animations

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Flutter Animations

Flutter 动画

Comprehensive guide to creating smooth, performant animations in Flutter using implicit animations, explicit animations, hero transitions, and custom animation patterns.
本指南全面介绍了如何在Flutter中使用隐式动画、显式动画、Hero过渡和自定义动画模式创建流畅、高性能的动画。

When to Use This Skill

何时使用该技能

  • Creating smooth UI transitions
  • Implementing custom animations
  • Building animated widgets
  • Performance optimization for animations
  • Hero animations between screens
  • Physics-based animations
  • Complex animation sequences
  • Gesture-driven animations
  • 创建流畅的UI过渡效果
  • 实现自定义动画
  • 构建动画组件
  • 动画性能优化
  • 页面间Hero动画
  • 基于物理的动画
  • 复杂动画序列
  • 手势驱动的动画

Core Animation Types

核心动画类型

1. Implicit Animations (Recommended for Simple Cases)

1. 隐式动画(推荐用于简单场景)

dart
// AnimatedContainer
class AnimatedContainerExample extends StatefulWidget {
  
  _AnimatedContainerExampleState createState() => _AnimatedContainerExampleState();
}

class _AnimatedContainerExampleState extends State<AnimatedContainerExample> {
  bool _expanded = false;

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => setState(() => _expanded = !_expanded),
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
        width: _expanded ? 200 : 100,
        height: _expanded ? 200 : 100,
        decoration: BoxDecoration(
          color: _expanded ? Colors.blue : Colors.red,
          borderRadius: BorderRadius.circular(_expanded ? 50 : 10),
        ),
        child: const Center(child: Text('Tap me')),
      ),
    );
  }
}

// AnimatedOpacity
AnimatedOpacity(
  opacity: _visible ? 1.0 : 0.0,
  duration: const Duration(milliseconds: 500),
  child: Container(/* ... */),
)

// AnimatedAlign
AnimatedAlign(
  alignment: _aligned ? Alignment.topLeft : Alignment.bottomRight,
  duration: const Duration(milliseconds: 300),
  child: FlutterLogo(size: 50),
)
dart
// AnimatedContainer
class AnimatedContainerExample extends StatefulWidget {
  
  _AnimatedContainerExampleState createState() => _AnimatedContainerExampleState();
}

class _AnimatedContainerExampleState extends State<AnimatedContainerExample> {
  bool _expanded = false;

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => setState(() => _expanded = !_expanded),
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
        width: _expanded ? 200 : 100,
        height: _expanded ? 200 : 100,
        decoration: BoxDecoration(
          color: _expanded ? Colors.blue : Colors.red,
          borderRadius: BorderRadius.circular(_expanded ? 50 : 10),
        ),
        child: const Center(child: Text('Tap me')),
      ),
    );
  }
}

// AnimatedOpacity
AnimatedOpacity(
  opacity: _visible ? 1.0 : 0.0,
  duration: const Duration(milliseconds: 500),
  child: Container(/* ... */),
)

// AnimatedAlign
AnimatedAlign(
  alignment: _aligned ? Alignment.topLeft : Alignment.bottomRight,
  duration: const Duration(milliseconds: 300),
  child: FlutterLogo(size: 50),
)

2. Explicit Animations (Full Control)

2. 显式动画(完全可控)

dart
class ExplicitAnimationExample extends StatefulWidget {
  
  _ExplicitAnimationExampleState createState() => _ExplicitAnimationExampleState();
}

class _ExplicitAnimationExampleState extends State<ExplicitAnimationExample>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    );

    _animation = Tween<double>(begin: 0, end: 300).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Curves.easeInOut,
      ),
    );

    _controller.forward();
  }

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

  
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return Container(
          width: _animation.value,
          height: _animation.value,
          color: Colors.blue,
        );
      },
    );
  }
}
dart
class ExplicitAnimationExample extends StatefulWidget {
  
  _ExplicitAnimationExampleState createState() => _ExplicitAnimationExampleState();
}

class _ExplicitAnimationExampleState extends State<ExplicitAnimationExample>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    );

    _animation = Tween<double>(begin: 0, end: 300).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Curves.easeInOut,
      ),
    );

    _controller.forward();
  }

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

  
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return Container(
          width: _animation.value,
          height: _animation.value,
          color: Colors.blue,
        );
      },
    );
  }
}

3. Hero Animations

3. Hero动画

dart
// Source screen
class SourceScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => Navigator.push(
        context,
        MaterialPageRoute(builder: (_) => DestinationScreen()),
      ),
      child: Hero(
        tag: 'hero-image',
        child: Image.network('https://example.com/image.jpg'),
      ),
    );
  }
}

// Destination screen
class DestinationScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Detail')),
      body: Center(
        child: Hero(
          tag: 'hero-image',
          child: Image.network('https://example.com/image.jpg'),
        ),
      ),
    );
  }
}
dart
// Source screen
class SourceScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => Navigator.push(
        context,
        MaterialPageRoute(builder: (_) => DestinationScreen()),
      ),
      child: Hero(
        tag: 'hero-image',
        child: Image.network('https://example.com/image.jpg'),
      ),
    );
  }
}

// Destination screen
class DestinationScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Detail')),
      body: Center(
        child: Hero(
          tag: 'hero-image',
          child: Image.network('https://example.com/image.jpg'),
        ),
      ),
    );
  }
}

4. Custom Animated Widget

4. 自定义动画组件

dart
class FadeInWidget extends StatefulWidget {
  final Widget child;
  final Duration duration;

  const FadeInWidget({
    required this.child,
    this.duration = const Duration(milliseconds: 500),
  });

  
  _FadeInWidgetState createState() => _FadeInWidgetState();
}

class _FadeInWidgetState extends State<FadeInWidget>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  
  void initState() {
    super.initState();
    _controller = AnimationController(duration: widget.duration, vsync: this);
    _animation = Tween<double>(begin: 0, end: 1).animate(_controller);
    _controller.forward();
  }

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

  
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _animation,
      child: widget.child,
    );
  }
}
dart
class FadeInWidget extends StatefulWidget {
  final Widget child;
  final Duration duration;

  const FadeInWidget({
    required this.child,
    this.duration = const Duration(milliseconds: 500),
  });

  
  _FadeInWidgetState createState() => _FadeInWidgetState();
}

class _FadeInWidgetState extends State<FadeInWidget>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  
  void initState() {
    super.initState();
    _controller = AnimationController(duration: widget.duration, vsync: this);
    _animation = Tween<double>(begin: 0, end: 1).animate(_controller);
    _controller.forward();
  }

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

  
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _animation,
      child: widget.child,
    );
  }
}

5. Staggered Animations

5. 交错动画

dart
class StaggeredAnimation extends StatelessWidget {
  final Animation<double> controller;
  late final Animation<double> opacity;
  late final Animation<double> width;
  late final Animation<EdgeInsets> padding;

  StaggeredAnimation({required this.controller}) {
    opacity = Tween<double>(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(0.0, 0.3, curve: Curves.ease),
      ),
    );

    width = Tween<double>(begin: 50.0, end: 150.0).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(0.3, 0.6, curve: Curves.ease),
      ),
    );

    padding = EdgeInsetsTween(
      begin: const EdgeInsets.only(bottom: 16),
      end: const EdgeInsets.only(bottom: 75),
    ).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(0.6, 1.0, curve: Curves.ease),
      ),
    );
  }

  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        return Container(
          padding: padding.value,
          child: Opacity(
            opacity: opacity.value,
            child: Container(
              width: width.value,
              height: width.value,
              color: Colors.blue,
            ),
          ),
        );
      },
    );
  }
}
dart
class StaggeredAnimation extends StatelessWidget {
  final Animation<double> controller;
  late final Animation<double> opacity;
  late final Animation<double> width;
  late final Animation<EdgeInsets> padding;

  StaggeredAnimation({required this.controller}) {
    opacity = Tween<double>(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(0.0, 0.3, curve: Curves.ease),
      ),
    );

    width = Tween<double>(begin: 50.0, end: 150.0).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(0.3, 0.6, curve: Curves.ease),
      ),
    );

    padding = EdgeInsetsTween(
      begin: const EdgeInsets.only(bottom: 16),
      end: const EdgeInsets.only(bottom: 75),
    ).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(0.6, 1.0, curve: Curves.ease),
      ),
    );
  }

  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        return Container(
          padding: padding.value,
          child: Opacity(
            opacity: opacity.value,
            child: Container(
              width: width.value,
              height: width.value,
              color: Colors.blue,
            ),
          ),
        );
      },
    );
  }
}

6. Physics-Based Animations

6. 基于物理的动画

dart
class PhysicsAnimationExample extends StatefulWidget {
  
  _PhysicsAnimationExampleState createState() => _PhysicsAnimationExampleState();
}

class _PhysicsAnimationExampleState extends State<PhysicsAnimationExample>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      lowerBound: 0,
      upperBound: 500,
    );
  }

  void _runAnimation() {
    _controller.animateWith(
      SpringSimulation(
        const SpringDescription(
          mass: 1,
          stiffness: 100,
          damping: 10,
        ),
        0,
        500,
        0,
      ),
    );
  }

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _runAnimation,
      child: AnimatedBuilder(
        animation: _controller,
        builder: (context, child) {
          return Transform.translate(
            offset: Offset(0, _controller.value),
            child: Container(
              width: 100,
              height: 100,
              color: Colors.blue,
            ),
          );
        },
      ),
    );
  }
}
dart
class PhysicsAnimationExample extends StatefulWidget {
  
  _PhysicsAnimationExampleState createState() => _PhysicsAnimationExampleState();
}

class _PhysicsAnimationExampleState extends State<PhysicsAnimationExample>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      lowerBound: 0,
      upperBound: 500,
    );
  }

  void _runAnimation() {
    _controller.animateWith(
      SpringSimulation(
        const SpringDescription(
          mass: 1,
          stiffness: 100,
          damping: 10,
        ),
        0,
        500,
        0,
      ),
    );
  }

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _runAnimation,
      child: AnimatedBuilder(
        animation: _controller,
        builder: (context, child) {
          return Transform.translate(
            offset: Offset(0, _controller.value),
            child: Container(
              width: 100,
              height: 100,
              color: Colors.blue,
            ),
          );
        },
      ),
    );
  }
}

Best Practices

最佳实践

  1. Use const constructors for child widgets in animations
  2. Dispose controllers properly to avoid memory leaks
  3. Use AnimatedBuilder instead of setState for better performance
  4. Prefer implicit animations for simple cases
  5. Use TweenAnimationBuilder for custom animations without controllers
  6. Test animations on real devices for performance
  7. Limit simultaneous animations to avoid jank
  8. Use RepaintBoundary to isolate expensive repaints
  1. 动画中的子组件使用const构造函数
  2. 正确销毁控制器以避免内存泄漏
  3. 使用AnimatedBuilder替代setState以提升性能
  4. 简单场景优先使用隐式动画
  5. 无需控制器的自定义动画使用TweenAnimationBuilder
  6. 在真实设备上测试动画性能
  7. 限制同时运行的动画数量以避免卡顿
  8. 使用RepaintBoundary隔离昂贵的重绘操作

Resources

参考资源