Marketplace
custom-plugin-flutter-skill-animations
Production-grade Flutter animations mastery - Implicit and explicit animations, AnimationController, Hero transitions, physics-based motion, Lottie/Rive integration, 60fps optimization with comprehensive code examples
$ 安裝
git clone https://github.com/pluginagentmarketplace/custom-plugin-flutter /tmp/custom-plugin-flutter && cp -r /tmp/custom-plugin-flutter/skills/animations ~/.claude/skills/custom-plugin-flutter// tip: Run this command in your terminal to install the skill
SKILL.md
name: custom-plugin-flutter-skill-animations description: Production-grade Flutter animations mastery - Implicit and explicit animations, AnimationController, Hero transitions, physics-based motion, Lottie/Rive integration, 60fps optimization with comprehensive code examples sasmp_version: "1.3.0" bonded_agent: 01-flutter-ui-development bond_type: PRIMARY_BOND
custom-plugin-flutter: Animations Skill
Quick Start - Production Animation Pattern
class AnimatedProductCard extends StatefulWidget {
final Product product;
final bool isSelected;
const AnimatedProductCard({
required this.product,
required this.isSelected,
});
@override
State<AnimatedProductCard> createState() => _AnimatedProductCardState();
}
class _AnimatedProductCardState extends State<AnimatedProductCard>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _scaleAnimation;
late final Animation<double> _opacityAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
);
_scaleAnimation = Tween<double>(begin: 1.0, end: 0.95).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
_opacityAnimation = Tween<double>(begin: 1.0, end: 0.8).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _onTapDown(TapDownDetails details) => _controller.forward();
void _onTapUp(TapUpDetails details) => _controller.reverse();
void _onTapCancel() => _controller.reverse();
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: _onTapDown,
onTapUp: _onTapUp,
onTapCancel: _onTapCancel,
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: Opacity(
opacity: _opacityAnimation.value,
child: child,
),
);
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
decoration: BoxDecoration(
border: Border.all(
color: widget.isSelected ? Colors.blue : Colors.grey,
width: widget.isSelected ? 2 : 1,
),
borderRadius: BorderRadius.circular(12),
),
child: ProductContent(product: widget.product),
),
),
);
}
}
1. Implicit Animations
Built-in Animated Widgets
// AnimatedContainer - Animate multiple properties
AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: isExpanded ? 300 : 100,
height: isExpanded ? 200 : 100,
decoration: BoxDecoration(
color: isActive ? Colors.blue : Colors.grey,
borderRadius: BorderRadius.circular(isExpanded ? 16 : 8),
boxShadow: isActive ? [BoxShadow(blurRadius: 10)] : [],
),
child: content,
)
// AnimatedOpacity
AnimatedOpacity(
opacity: isVisible ? 1.0 : 0.0,
duration: Duration(milliseconds: 200),
child: content,
)
// AnimatedPositioned (inside Stack)
AnimatedPositioned(
duration: Duration(milliseconds: 300),
left: isLeft ? 0 : 100,
top: isTop ? 0 : 100,
child: widget,
)
// AnimatedSwitcher - Cross-fade between widgets
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
transitionBuilder: (child, animation) {
return FadeTransition(opacity: animation, child: child);
},
child: Text(
currentText,
key: ValueKey(currentText), // Important!
),
)
// TweenAnimationBuilder - Custom implicit animation
TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: progress),
duration: Duration(milliseconds: 500),
builder: (context, value, child) {
return CircularProgressIndicator(value: value);
},
)
2. Explicit Animations
AnimationController Pattern
class ExplicitAnimationWidget extends StatefulWidget {
@override
State<ExplicitAnimationWidget> createState() => _ExplicitAnimationWidgetState();
}
class _ExplicitAnimationWidgetState extends State<ExplicitAnimationWidget>
with TickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<Offset> _slideAnimation;
late final Animation<double> _fadeAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 500),
vsync: this,
);
_slideAnimation = Tween<Offset>(
begin: Offset(0, 1),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _controller,
curve: Interval(0.0, 0.6, curve: Curves.easeOut),
));
_fadeAnimation = Tween<double>(
begin: 0,
end: 1,
).animate(CurvedAnimation(
parent: _controller,
curve: Interval(0.3, 1.0, curve: Curves.easeIn),
));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SlideTransition(
position: _slideAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
child: Content(),
),
);
}
}
Animation Transitions
// Built-in transitions
FadeTransition(opacity: animation, child: widget)
SlideTransition(position: offsetAnimation, child: widget)
ScaleTransition(scale: animation, child: widget)
RotationTransition(turns: animation, child: widget)
SizeTransition(sizeFactor: animation, child: widget)
// AnimatedBuilder - Custom rendering
AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(_controller.value * pi),
alignment: Alignment.center,
child: child,
);
},
child: Card(),
)
3. Hero Animations
// Source screen
Hero(
tag: 'product-${product.id}',
child: Image.network(product.imageUrl),
)
// Destination screen
Hero(
tag: 'product-${product.id}',
child: Image.network(product.imageUrl),
)
// Custom Hero flight
Hero(
tag: 'avatar',
flightShuttleBuilder: (
flightContext,
animation,
flightDirection,
fromHeroContext,
toHeroContext,
) {
return AnimatedBuilder(
animation: animation,
builder: (context, _) {
return CircleAvatar(
radius: lerpDouble(40, 60, animation.value),
);
},
);
},
child: CircleAvatar(),
)
4. Staggered Animations
class StaggeredList extends StatefulWidget {
@override
State<StaggeredList> createState() => _StaggeredListState();
}
class _StaggeredListState extends State<StaggeredList>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
final List<Animation<Offset>> _animations = [];
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 1000),
vsync: this,
);
// Create staggered animations
for (int i = 0; i < 5; i++) {
final start = i * 0.1;
final end = start + 0.4;
_animations.add(
Tween<Offset>(begin: Offset(1, 0), end: Offset.zero).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(start, end, curve: Curves.easeOut),
),
),
);
}
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Column(
children: List.generate(5, (index) {
return SlideTransition(
position: _animations[index],
child: ListTile(title: Text('Item $index')),
);
}),
);
}
}
5. Physics-Based Animations
// Spring simulation
class SpringAnimation extends StatefulWidget {
@override
State<SpringAnimation> createState() => _SpringAnimationState();
}
class _SpringAnimationState extends State<SpringAnimation>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late Animation<double> _animation;
final SpringDescription spring = SpringDescription(
mass: 1,
stiffness: 100,
damping: 10,
);
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
final simulation = SpringSimulation(spring, 0, 1, 0);
_controller.animateWith(simulation);
_animation = _controller.drive(Tween(begin: 0.0, end: 100.0));
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Transform.translate(
offset: Offset(0, _animation.value),
child: child,
);
},
child: Ball(),
);
}
}
6. Lottie & Rive Integration
// Lottie animation
import 'package:lottie/lottie.dart';
Lottie.asset(
'assets/loading.json',
width: 200,
height: 200,
fit: BoxFit.contain,
repeat: true,
animate: true,
onLoaded: (composition) {
_controller.duration = composition.duration;
},
)
// Rive animation
import 'package:rive/rive.dart';
RiveAnimation.asset(
'assets/animation.riv',
fit: BoxFit.cover,
stateMachines: ['StateMachine1'],
onInit: (artboard) {
final controller = StateMachineController.fromArtboard(
artboard,
'StateMachine1',
);
artboard.addController(controller!);
_trigger = controller.findInput<bool>('trigger') as SMITrigger;
},
)
7. Page Transitions
// Custom page route
class FadePageRoute<T> extends PageRouteBuilder<T> {
final Widget page;
FadePageRoute({required this.page})
: super(
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(opacity: animation, child: child);
},
transitionDuration: Duration(milliseconds: 300),
);
}
// Slide + fade combined
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final offsetAnimation = Tween<Offset>(
begin: Offset(1.0, 0.0),
end: Offset.zero,
).animate(CurvedAnimation(
parent: animation,
curve: Curves.easeInOut,
));
return SlideTransition(
position: offsetAnimation,
child: FadeTransition(
opacity: animation,
child: child,
),
);
}
8. Performance Optimization
// Use const where possible
const AnimatedContainer(...)
// RepaintBoundary for expensive animations
RepaintBoundary(
child: AnimatedWidget(),
)
// AnimatedBuilder isolates rebuilds
AnimatedBuilder(
animation: _controller,
child: ExpensiveWidget(), // Not rebuilt
builder: (context, child) {
return Transform.scale(
scale: _controller.value,
child: child, // Reused
);
},
)
// Avoid layout-triggering animations
// Good: Transform, Opacity
// Avoid: width/height in tight loops
Troubleshooting Guide
Issue: Animation jank (dropped frames)
1. Use RepaintBoundary to isolate
2. Profile with DevTools Performance
3. Avoid layout changes during animation
4. Use AnimatedBuilder, not setState
5. Check for heavy build methods
Issue: Animation doesn't start
1. Verify AnimationController.forward() called
2. Check vsync: this (TickerProviderStateMixin)
3. Verify widget is mounted before animating
4. Check duration is set
Issue: Animation leaks memory
1. Dispose AnimationController in dispose()
2. Cancel any listeners
3. Use mounted check before setState
Animation Selection Guide
| Need | Solution |
|---|---|
| Simple property change | AnimatedContainer |
| Cross-fade widgets | AnimatedSwitcher |
| Custom timing control | AnimationController |
| Shared element | Hero |
| List items appearing | Staggered animations |
| Natural motion | Physics simulation |
| Complex vector | Lottie/Rive |
Create fluid, 60fps animations in Flutter.
Repository

pluginagentmarketplace
Author
pluginagentmarketplace/custom-plugin-flutter/skills/animations
1
Stars
0
Forks
Updated4d ago
Added1w ago