openclaw-flutter-animations

Flutter 流畅动画实现指南 - 包含隐式/显式动画、Hero动画、交错动画、物理动画

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "openclaw-flutter-animations" with this command: npx skills add ThomasChangX/openclaw-flutter-animations

Flutter Animations

Overview

Create smooth, performant animations in Flutter using the right approach for each use case. This skill covers complete animation workflow: from choosing between implicit/explicit approaches to implementing complex effects like hero transitions and staggered animations.

Animation Type Decision Tree

Choose the right animation type based on your requirements:

Implicit Animations - Use when:

  • Animating a single property (color, size, position)
  • Animation is triggered by state change
  • No need for fine-grained control

Explicit Animations - Use when:

  • Need full control over animation lifecycle
  • Animating multiple properties simultaneously
  • Need to react to animation state changes
  • Creating custom animations or transitions

Hero Animations - Use when:

  • Sharing an element between two screens
  • Creating shared element transitions
  • User expects element to "fly" between routes

Staggered Animations - Use when:

  • Multiple animations should run sequentially or overlap
  • Creating ripple effects or sequential reveals
  • Animating list items in sequence

Physics-Based Animations - Use when:

  • Animations should feel natural/physical
  • Spring-like behavior, scrolling gestures
  • Draggable interactions

Implicit Animations

Implicit animations automatically handle the animation when properties change. No controller needed.

Common Implicit Widgets

AnimatedContainer - Animates multiple properties (size, color, decoration, padding):

AnimatedContainer(
  duration: const Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  width: _expanded ? 200 : 100,
  height: _expanded ? 200 : 100,
  color: _expanded ? Colors.blue : Colors.red,
  child: const FlutterLogo(),
)

AnimatedOpacity - Simple fade animation:

AnimatedOpacity(
  opacity: _visible ? 1.0 : 0.0,
  duration: const Duration(milliseconds: 300),
  child: const Text('Hello'),
)

TweenAnimationBuilder - Custom tween animation without boilerplate:

TweenAnimationBuilder<double>(
  tween: Tween<double>(begin: 0, end: 1),
  duration: const Duration(seconds: 1),
  builder: (context, value, child) {
    return Opacity(
      opacity: value,
      child: Transform.scale(
        scale: value,
        child: child,
      ),
    );
  },
  child: const FlutterLogo(),
)

Other implicit widgets:

  • AnimatedPadding - Padding animation
  • AnimatedPositioned - Position animation (in Stack)
  • AnimatedAlign - Alignment animation
  • AnimatedContainer - Multiple properties
  • AnimatedSwitcher - Cross-fade between widgets
  • AnimatedDefaultTextStyle - Text style animation

Best Practices

  • Prefer implicit animations for simple cases
  • Use appropriate curves for natural motion (see Curves class)
  • Set curve and duration for predictable behavior
  • Use onEnd callback when needed
  • Avoid nested implicit animations for performance

Explicit Animations

Explicit animations provide full control with AnimationController.

Core Components

AnimationController - Drives the animation:

late AnimationController _controller;

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

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

Tween - Interpolates between begin and end values:

animation = Tween<double>(begin: 0, end: 300).animate(_controller);

CurvedAnimation - Applies a curve to the animation:

animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.easeInOut,
);

AnimatedWidget Pattern

Best for reusable animated widgets:

class AnimatedLogo extends AnimatedWidget {
  const AnimatedLogo({super.key, required Animation<double> animation})
    : super(listenable: animation);

  @override
  Widget build(BuildContext context) {
    final animation = listenable as Animation<double>;
    return Center(
      child: Container(
        height: animation.value,
        width: animation.value,
        child: const FlutterLogo(),
      ),
    );
  }
}

AnimatedBuilder Pattern

Best for complex widgets with animations:

class GrowTransition extends StatelessWidget {
  const GrowTransition({
    required this.child,
    required this.animation,
    super.key,
  });

  final Widget child;
  final Animation<double> animation;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AnimatedBuilder(
        animation: animation,
        builder: (context, child) {
          return SizedBox(
            height: animation.value,
            width: animation.value,
            child: child,
          );
        },
        child: child,
      ),
    );
  }
}

Monitoring Animation State

animation.addStatusListener((status) {
  switch (status) {
    case AnimationStatus.completed:
      _controller.reverse();
      break;
    case AnimationStatus.dismissed:
      _controller.forward();
      break;
    default:
      break;
  }
});

Multiple Simultaneous Animations

class AnimatedLogo extends AnimatedWidget {
  const AnimatedLogo({super.key, required Animation<double> animation})
    : super(listenable: animation);

  static final _opacityTween = Tween<double>(begin: 0.1, end: 1);
  static final _sizeTween = Tween<double>(begin: 0, end: 300);

  @override
  Widget build(BuildContext context) {
    final animation = listenable as Animation<double>;
    return Center(
      child: Opacity(
        opacity: _opacityTween.evaluate(animation),
        child: Container(
          height: _sizeTween.evaluate(animation),
          width: _sizeTween.evaluate(animation),
          child: const FlutterLogo(),
        ),
      ),
    );
  }
}

Built-in Explicit Transitions

Flutter provides ready-to-use transitions:

  • FadeTransition - Fade animation
  • ScaleTransition - Scale animation
  • SlideTransition - Slide animation
  • SizeTransition - Size animation
  • RotationTransition - Rotation animation
  • PositionedTransition - Position animation (in Stack)

Example:

FadeTransition(
  opacity: _animation,
  child: const FlutterLogo(),
)

Performance Tips

  • Dispose controllers when widget is removed
  • Use AnimatedBuilder for optimal rebuilds
  • Avoid setState() in animation listeners (use AnimatedWidget/AnimatedBuilder)
  • Use timeDilation to slow animations during debugging

Hero Animations

Hero animations create shared element transitions between screens.

Basic Hero Animation

Source screen:

Hero(
  tag: 'hero-image',
  child: Image.asset('images/logo.png'),
)

Destination screen:

Hero(
  tag: 'hero-image',  // Same tag!
  child: Image.asset('images/logo.png'),
)

Complete Example

class PhotoHero extends StatelessWidget {
  const PhotoHero({
    super.key,
    required this.photo,
    this.onTap,
    required this.width,
  });

  final String photo;
  final VoidCallback? onTap;
  final double width;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: width,
      child: Hero(
        tag: photo,
        child: Material(
          color: Colors.transparent,
          child: InkWell(
            onTap: onTap,
            child: Image.asset(photo, fit: BoxFit.contain),
          ),
        ),
      ),
    );
  }
}

Navigating between screens:

Navigator.of(context).push(
  MaterialPageRoute<void>(
    builder: (context) {
      return Scaffold(
        appBar: AppBar(title: const Text('Detail')),
        body: Center(
          child: PhotoHero(
            photo: 'images/logo.png',
            width: 300.0,
            onTap: () => Navigator.of(context).pop(),
          ),
        ),
      );
    },
  ),
);

Radial Hero Animation

Transform from circle to rectangle during transition:

class RadialExpansion extends StatelessWidget {
  const RadialExpansion({
    super.key,
    required this.maxRadius,
    this.child,
  }) : clipRectSize = 2.0 * (maxRadius / math.sqrt2);

  final double maxRadius;
  final double clipRectSize;
  final Widget? child;

  @override
  Widget build(BuildContext context) {
    return ClipOval(
      child: Center(
        child: SizedBox(
          width: clipRectSize,
          height: clipRectSize,
          child: ClipRect(child: child),
        ),
      ),
    );
  }
}

Use with MaterialRectCenterArcTween for center-based interpolation:

static RectTween _createRectTween(Rect? begin, Rect? end) {
  return MaterialRectCenterArcTween(begin: begin, end: end);
}

Hero Best Practices

  • Use unique, consistent tags (often the data object itself)
  • Keep hero widget trees similar between routes
  • Wrap images in Material with transparent color for "pop" effect
  • Use timeDilation to debug transitions
  • Consider HeroMode to disable hero animations when needed

Staggered Animations

Run multiple animations with different timing.

Basic Staggered Animation

All animations share one controller:

class StaggerAnimation extends StatelessWidget {
  StaggerAnimation({super.key, required this.controller})
    : opacity = Tween<double>(begin: 0.0, end: 1.0).animate(
        CurvedAnimation(
          parent: controller,
          curve: const Interval(0.0, 0.100, curve: Curves.ease),
        ),
      ),
      width = Tween<double>(begin: 50.0, end: 150.0).animate(
        CurvedAnimation(
          parent: controller,
          curve: const Interval(0.125, 0.250, curve: Curves.ease),
        ),
      );

  final AnimationController controller;
  final Animation<double> opacity;
  final Animation<double> width;

  Widget _buildAnimation(BuildContext context, Widget? child) {
    return Container(
      alignment: Alignment.bottomCenter,
      child: Opacity(
        opacity: opacity.value,
        child: Container(
          width: width.value,
          height: 150,
          color: Colors.blue,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      builder: _buildAnimation,
    );
  }
}

Interval-Based Timing

Each animation has an Interval between 0.0 and 1.0:

animation = Tween<double>(begin: 0, end: 300).animate(
  CurvedAnimation(
    parent: controller,
    curve: const Interval(
      0.25,  // Start at 25% of controller duration
      0.50,  // End at 50% of controller duration
      curve: Curves.ease,
    ),
  ),
);

Common Tweens

borderRadius = BorderRadiusTween(
  begin: BorderRadius.circular(4),
  end: BorderRadius.circular(75),
).animate(
  CurvedAnimation(
    parent: controller,
    curve: const Interval(0.375, 0.500, curve: Curves.ease),
  ),
);

Staggered Menu Animation

class _MenuState extends State<Menu> with SingleTickerProviderStateMixin {
  static const _initialDelayTime = Duration(milliseconds: 50);
  static const _itemSlideTime = Duration(milliseconds: 250);
  static const _staggerTime = Duration(milliseconds: 50);
  static const _buttonDelayTime = Duration(milliseconds: 150);
  static const _buttonTime = Duration(milliseconds: 500);

  final _animationDuration =
      _initialDelayTime +
      (_staggerTime * _menuTitles.length) +
      _buttonDelayTime +
      _buttonTime;

  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: _animationDuration,
      vsync: this,
    );
    _controller.forward();
  }

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

Stagger Best Practices

  • Use Interval to offset animations in time
  • Ensure controller duration covers all intervals
  • Use curves for natural motion within intervals
  • Consider timeDilation to debug timing
  • Stagger menu items with increasing delay for ripple effect

Physics-Based Animations

Create natural-feeling animations using physics simulations.

Fling Animation

_controller.fling(
  velocity: 2.0,  // Units per second
);

Custom Physics Simulation

_controller.animateWith(
  SpringSimulation(
    spring: const SpringDescription(
      mass: 1,
      stiffness: 100,
      damping: 10,
    ),
    start: 0.0,
    end: 1.0,
    velocity: 0.0,
  ),
);

Common Physics Simulations

  • SpringSimulation - Spring physics
  • BouncingScrollSimulation - Scroll with bounce
  • ClampingScrollSimulation - Scroll without bounce
  • GravitySimulation - Gravity-based

Best Practices

DO

  • Dispose AnimationController in widget disposal
  • Use AnimatedBuilder/AnimatedWidget instead of setState() in listeners
  • Choose appropriate curves for natural motion
  • Use timeDilation for debugging animations
  • Consider performance (avoid heavy widgets in animation builds)
  • Test animations on various devices
  • Support reverse animations for intuitive feel

DON'T

  • Forget to dispose AnimationController (memory leak)
  • Use setState() in animation listeners when AnimatedBuilder suffices
  • Assume animation completes instantly (handle AnimationStatus)
  • Over-animate (animations can distract users)
  • Create animations that feel "jerky" (use smooth curves)
  • Ignore accessibility (respect disableAnimations preference)

Resources

references/

  • implicit.md - Complete reference for implicit animation widgets with examples and best practices.
  • explicit.md - Deep dive into explicit animations, AnimationController, and patterns.
  • hero.md - Hero animations guide with standard and radial transitions.
  • staggered.md - Staggered animation patterns and timing strategies.
  • physics.md - Physics-based animations and simulations.
  • curves.md - Reference for Curves class and choosing appropriate curves.

assets/templates/

Template code for common animation patterns:

  • implicit_animation.dart - Implicit animation examples
  • explicit_animation.dart - Explicit animation setup
  • hero_transition.dart - Hero animation boilerplate
  • staggered_animation.dart - Staggered animation template

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

Ai Competitor Analyzer

提供AI驱动的竞争对手分析,支持批量自动处理,提升企业和专业团队分析效率与专业度。

Registry SourceRecently Updated
General

Ai Data Visualization

提供自动化AI分析与多格式批量处理,显著提升数据可视化效率,节省成本,适用企业和个人用户。

Registry SourceRecently Updated
General

Ai Cost Optimizer

提供基于预算和任务需求的AI模型成本优化方案,计算节省并指导OpenClaw配置与模型切换策略。

Registry SourceRecently Updated