FL

flutter-animating-apps

Adds motion and interactive animations to Flutter mobile apps.

Install

mkdir -p .claude/skills/flutter-animating-apps && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14342" && unzip -o skill.zip -d .claude/skills/flutter-animating-apps && rm skill.zip

Installs to .claude/skills/flutter-animating-apps

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Implements animated effects, transitions, and motion in a Flutter app. Use when adding visual feedback, shared element transitions, or physics-based animations.
160 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Implement implicit animations for simple property changes
  • Implement explicit animations with playback control
  • Implement Hero transitions between routes
  • Implement physics-based animations for natural motion
  • Implement staggered animations for sequences of motions

How it works

The skill guides the user in selecting the appropriate animation strategy based on requirements, then provides workflows for implementing implicit, explicit, Hero, physics-based, and staggered animations using Flutter's animation framework components like `AnimationController` and `Tween`.

Inputs & outputs

You give it
a request to add animated effects to a Flutter app
You get back
Flutter code implementing the requested animation type

When to use flutter-animating-apps

  • Creating UI transitions
  • Implementing physics-based motion
  • Adding user interaction feedback

About this skill

Flutter Animations

Contents

Animation Strategies

Select the correct animation approach based on your requirements:

  • If animating simple property changes (size, color, opacity) without playback control: Use Implicit Animations (AnimatedContainer, AnimatedOpacity, TweenAnimationBuilder).
  • If requiring playback control (play, pause, reverse, loop) or coordinating multiple properties: Use Explicit Animations (AnimationController with AnimatedBuilder or AnimatedWidget).
  • If animating elements between two distinct routes: Use Hero Animations (Shared Element Transitions).
  • If modeling real-world motion (e.g., snapping back after a drag): Use Physics-Based Animations (SpringSimulation).
  • If animating a sequence of overlapping or delayed motions: Use Staggered Animations (multiple Tweens driven by a single AnimationController using Interval curves).

Core Concepts

  • Animation<T>: Abstract representation of a value changing over time. Holds state (completed, dismissed) and notifies listeners.
  • AnimationController: Drives the animation, generating values (0.0–1.0) tied to screen refresh rate. Always provide vsync (via SingleTickerProviderStateMixin). Always dispose() controllers.
  • Tween<T>: Stateless mapping from input range (0.0–1.0) to an output type (Color, Offset, double). Chain with curves using .animate().
  • Curve: Non-linear timing (Curves.easeIn, Curves.bounceOut). Apply via CurvedAnimation or CurveTween.

Workflow: Implementing Implicit Animations

Use for "fire-and-forget" state-driven animations.

  • Identify the target properties to animate (e.g., width, color).
  • Replace the static widget (e.g., Container) with its animated counterpart (e.g., AnimatedContainer).
  • Define the duration property.
  • (Optional) Define the curve property for non-linear motion.
  • Trigger the animation by updating the properties inside a setState() or BLoC state emission.
  • Run validator → review UI for jank → adjust duration/curve if necessary.

Workflow: Implementing Explicit Animations

Use when you need granular control over the animation lifecycle.

  • Add SingleTickerProviderStateMixin (or TickerProviderStateMixin for multiple controllers) to the State class.
  • Initialize an AnimationController in initState(), providing vsync: this and a duration.
  • Define a Tween and chain it to the controller using .animate().
  • Wrap the target UI in an AnimatedBuilder (preferred for complex trees) or subclass AnimatedWidget.
  • Pass the Animation object to the AnimatedBuilder's animation property.
  • Control playback using controller.forward(), controller.reverse(), or controller.repeat().
  • Call controller.dispose() in the dispose() method.
  • Run validator → check for memory leaks → ensure dispose() is called.

Workflow: Implementing Hero Transitions

Use to fly a widget between two routes.

  • Wrap the source widget in a Hero widget.
  • Assign a unique, data-driven tag to the source Hero.
  • Wrap the destination widget in a Hero widget.
  • Assign the exact same tag to the destination Hero.
  • Ensure the widget trees inside both Hero widgets are visually similar to prevent jarring jumps.
  • Trigger the transition by pushing the destination route via Navigator or context.go().

Workflow: Implementing Physics-Based Animations

Use for gesture-driven, natural motion.

  • Set up an AnimationController (do not set a fixed duration).
  • Capture gesture velocity using a GestureDetector (e.g., onPanEnd providing DragEndDetails).
  • Convert the pixel velocity to the coordinate space of the animating property.
  • Instantiate a SpringSimulation with mass, stiffness, damping, and the calculated velocity.
  • Drive the controller using controller.animateWith(simulation).

Examples

Explicit Animation (Staggered with AnimatedBuilder)

class StaggeredAnimationDemo extends StatefulWidget {
  @override
  State<StaggeredAnimationDemo> createState() => _StaggeredAnimationDemoState();
}

class _StaggeredAnimationDemoState extends State<StaggeredAnimationDemo>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _widthAnimation;
  late Animation<Color?> _colorAnimation;

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

    // Staggered width animation (0.0 to 0.5 interval)
    _widthAnimation = Tween<double>(begin: 50.0, end: 200.0).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(0.0, 0.5, curve: Curves.easeIn),
      ),
    );

    // Staggered color animation (0.5 to 1.0 interval)
    _colorAnimation = ColorTween(begin: Colors.blue, end: Colors.red).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(0.5, 1.0, curve: Curves.easeOut),
      ),
    );

    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose(); // CRITICAL: Prevent memory leaks
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Container(
          width: _widthAnimation.value,
          height: 50.0,
          color: _colorAnimation.value,
        );
      },
    );
  }
}

Custom Page Route Transition

Route createCustomRoute(Widget destination) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => destination,
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      const begin = Offset(0.0, 1.0); // Start from bottom
      const end = Offset.zero;
      const curve = Curves.easeOut;

      final tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
      final offsetAnimation = animation.drive(tween);

      return SlideTransition(
        position: offsetAnimation,
        child: child,
      );
    },
  );
}

// Usage: Navigator.of(context).push(createCustomRoute(const NextPage()));

Implicit Animation (AnimatedContainer)

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

  @override
  State<AnimatedBox> createState() => _AnimatedBoxState();
}

class _AnimatedBoxState extends State<AnimatedBox> {
  bool _expanded = false;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => setState(() => _expanded = !_expanded),
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOut,
        width: _expanded ? 200.0 : 100.0,
        height: _expanded ? 200.0 : 100.0,
        decoration: BoxDecoration(
          color: _expanded ? Colors.blue : Colors.red,
          borderRadius: BorderRadius.circular(_expanded ? 16.0 : 8.0),
        ),
      ),
    );
  }
}

Anti-patterns

  • ❌ Forgetting to call dispose() on AnimationController (memory leak)
  • ❌ Using explicit animations when implicit animations suffice (over-engineering)
  • ❌ Creating AnimationController in the build method (recreated every rebuild)
  • ❌ Using setState for complex multi-property animations (use AnimatedBuilder)
  • ❌ Mismatched Hero tags between source and destination routes

Resources

When not to use it

  • When forgetting to call `dispose()` on `AnimationController`
  • When using explicit animations when implicit animations suffice
  • When creating `AnimationController` in the `build` method

Limitations

  • Requires `vsync` for `AnimationController`
  • Requires `dispose()` calls for `AnimationController`
  • Hero animations require unique `tag`s and visually similar widget trees

How it compares

This skill provides structured workflows and specific Flutter components for various animation types, offering a guided approach to animation implementation compared to general Flutter development.

Compared to similar skills

flutter-animating-apps side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
flutter-animating-apps (this skill)04moNo flagsIntermediate
iOS Animation Graphics Skill05moNo flagsIntermediate
mobile-design1494moReviewIntermediate
flutter-mobile-design02moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry