一键导入
flutter-animation
Add animated effects to your Flutter app
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Add animated effects to your Flutter app
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Configure your Flutter app to support assistive technologies like Screen Readers
Measure and reduce the size of the Flutter app bundle, APK, or IPA
Use the Flutter team's recommended app architecture
Execute long-running tasks in a background thread in Flutter
Set up a macOS environment for Flutter development
Adding a Home Screen widget to your Flutter App
| name | flutter-animation |
| description | Add animated effects to your Flutter app |
| metadata | {"model":"models/gemini-3.1-pro-preview","last_modified":"Mon, 02 Mar 2026 21:40:10 GMT"} |
Implements and manages Flutter animations, selecting the appropriate animation strategy (implicit, explicit, tween, physics, hero, or staggered) based on UI requirements. Assumes a working Flutter environment, stateful/stateless widget competence, and a standard widget tree structure.
Evaluate the UI requirement using the following decision tree to select the correct animation approach:
AnimatedContainer).SpringSimulation, animateWith).Hero widget).AnimationController with multiple Tweens and Interval curves).AnimationController, Tween, AnimatedBuilder / AnimatedWidget).STOP AND ASK THE USER: If the requirement is ambiguous, pause and ask the user to clarify the desired visual effect before writing implementation code.
For simple transitions between values, use implicit animation widgets. Do not manually manage state or controllers.
AnimatedContainer(
duration: const Duration(milliseconds: 500),
curve: Curves.bounceIn,
width: _isExpanded ? 200.0 : 100.0,
height: _isExpanded ? 200.0 : 100.0,
decoration: BoxDecoration(
color: _isExpanded ? Colors.green : Colors.blue,
borderRadius: BorderRadius.circular(_isExpanded ? 50.0 : 8.0),
),
child: const FlutterLogo(),
)
When you need to control the animation (play, pause, reverse), use an AnimationController with a Tween. Separate the transition rendering from the state using AnimatedBuilder.
class _MyAnimatedWidgetState extends State<MyAnimatedWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
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.easeOut),
)..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_controller.reverse();
} else if (status == AnimationStatus.dismissed) {
_controller.forward();
}
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose(); // STRICT REQUIREMENT
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return SizedBox(
height: _animation.value,
width: _animation.value,
child: child,
);
},
child: const FlutterLogo(), // Passed as child for performance
);
}
}
To animate transitions between routes, use PageRouteBuilder and chain a CurveTween with a Tween<Offset>.
Route<void> _createRoute() {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const DestinationPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(0.0, 1.0);
const end = Offset.zero;
const curve = Curves.ease;
final tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
);
}
For realistic motion (e.g., snapping back after a drag), calculate velocity and apply a SpringSimulation.
void _runSpringAnimation(Offset pixelsPerSecond, Size size, Alignment dragAlignment) {
_animation = _controller.drive(
AlignmentTween(begin: dragAlignment, end: Alignment.center),
);
final unitsPerSecondX = pixelsPerSecond.dx / size.width;
final unitsPerSecondY = pixelsPerSecond.dy / size.height;
final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
final unitVelocity = unitsPerSecond.distance;
const spring = SpringDescription(mass: 1, stiffness: 1, damping: 1);
final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
_controller.animateWith(simulation);
}
To fly a widget between routes, wrap the identical widget tree in both routes with a Hero widget using the exact same tag.
// Source Route
Hero(
tag: 'unique-photo-tag',
child: Image.asset('photo.png', width: 100),
)
// Destination Route
Hero(
tag: 'unique-photo-tag',
child: Image.asset('photo.png', width: 300),
)
For sequential or overlapping animations, use a single AnimationController and define multiple Tweens with Interval curves.
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;
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Opacity(
opacity: opacity.value,
child: Container(width: width.value, height: 50, color: Colors.blue),
);
},
);
}
}
After generating animation code, verify the following:
State class use SingleTickerProviderStateMixin (or TickerProviderStateMixin for multiple controllers)?_controller.dispose() explicitly called in the dispose() method?AnimatedBuilder, is the static widget passed to the child parameter rather than rebuilt inside the builder function?
If any of these are missing, fix the code immediately before presenting it to the user.dispose() methods for all AnimationController instances to prevent memory leaks.Tween and Curve classes as stateless and immutable. Do not attempt to mutate them after instantiation.AnimatedBuilder or AnimatedWidget instead of calling setState() inside a controller's addListener when building complex widget trees.'image' if multiple heroes exist.