Help us improve
Share bugs, ideas, or general feedback.
From flutter-skills
Implements animated effects, transitions, and motion in a Flutter app. Use when adding visual feedback, shared element transitions, or physics-based animations.
npx claudepluginhub gsmlg-dev/code-agent --plugin flutter-skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/flutter-skills:flutter-animating-appsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- [Core Concepts](#core-concepts)
Provides best practices for Flutter animations using built-in framework and Material 3 motion tokens. Covers implicit/explicit animations, page transitions, decision tree for approach selection.
Provides Flutter patterns for widget architecture, state management, Impeller renderer, and platform-adaptive design, avoiding common mistakes.
Guides SwiftUI animation patterns including implicit/explicit animations, transitions, phase/keyframe animations, Animatable protocol, and @Animatable macro. Use when implementing motion or transitions in views.
Share bugs, ideas, or general feedback.
Manage Flutter animations using the core typed Animation system. Do not manually calculate frames; rely on the framework's ticker and interpolation classes.
Animation<T>: Treat this as an abstract representation of a value that changes over time. It holds state (completed, dismissed) and notifies listeners, but knows nothing about the UI.AnimationController: Instantiate this to drive the animation. It generates values (typically 0.0 to 1.0) tied to the screen refresh rate. Always provide a vsync (usually via SingleTickerProviderStateMixin) to prevent offscreen resource consumption. Always dispose() controllers to prevent memory leaks.Tween<T>: Define a stateless mapping from an input range (usually 0.0-1.0) to an output type (e.g., Color, Offset, double). Chain tweens with curves using .animate().Curve: Apply non-linear timing (e.g., Curves.easeIn, Curves.bounceOut) to an animation using a CurvedAnimation or CurveTween.Apply conditional logic to select the correct animation approach:
AnimatedContainer, AnimatedOpacity, TweenAnimationBuilder).AnimationController with AnimatedBuilder or AnimatedWidget).SpringSimulation).Tweens driven by a single AnimationController using Interval curves).Use this workflow for "fire-and-forget" state-driven animations.
Container) with its animated counterpart (e.g., AnimatedContainer).duration property.curve property for non-linear motion.setState() call.Use this workflow when you need granular control over the animation lifecycle.
SingleTickerProviderStateMixin (or TickerProviderStateMixin for multiple controllers) to the State class.AnimationController in initState(), providing vsync: this and a duration.Tween and chain it to the controller using .animate().AnimatedBuilder (preferred for complex trees) or subclass AnimatedWidget.Animation object to the AnimatedBuilder's animation property.controller.forward(), controller.reverse(), or controller.repeat().controller.dispose() in the dispose() method.dispose() is called.Use this workflow to fly a widget between two routes.
Hero widget.tag to the source Hero.Hero widget.tag to the destination Hero.Hero widgets are visually similar to prevent jarring jumps.Navigator.Use this workflow for gesture-driven, natural motion.
AnimationController (do not set a fixed duration).GestureDetector (e.g., onPanEnd providing DragEndDetails).SpringSimulation with mass, stiffness, damping, and the calculated velocity.controller.animateWith(simulation).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,
);
},
);
}
}
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()));