State-driven Flutter animation primitives.
All widgets respect MediaQuery.disableAnimations (system reduce-motion) and
require no controllers or initState.
| Widget |
What it does |
CountUpText |
Animates a number from its previous value to a new one |
TypewriterText |
Reveals text character by character |
dependencies:
animate_kit: ^0.3.0
FadeEntrance(
delay: const Duration(milliseconds: 100),
direction: FadeSlideDirection.up, // default
child: MyCard(),
)
| Parameter |
Type |
Default |
Description |
child |
Widget |
required |
Widget to animate |
delay |
Duration |
Duration.zero |
Wait before starting |
duration |
Duration |
500ms |
Fade + slide duration |
direction |
FadeSlideDirection |
up |
Slide direction; none for fade-only |
FadeSlideDirection values: up, down, left, right, none.
ScaleEntrance(
delay: const Duration(milliseconds: 150),
initialScale: 0.85, // default
child: MyDialog(),
)
| Parameter |
Type |
Default |
Description |
child |
Widget |
required |
Widget to animate |
delay |
Duration |
Duration.zero |
Wait before starting |
duration |
Duration |
400ms |
Fade + scale duration |
initialScale |
double |
0.85 |
Scale at animation start (0.0–1.0) |
StaggeredList(
itemDelay: const Duration(milliseconds: 60),
children: items.map((e) => ItemTile(e)).toList(),
)
| Parameter |
Type |
Default |
Description |
children |
List<Widget> |
required |
Items to stagger |
itemDelay |
Duration |
80ms |
Extra delay added per item index |
duration |
Duration |
400ms |
Each item's FadeEntrance duration |
direction |
FadeSlideDirection |
up |
Slide direction for each item |
crossAxisAlignment |
CrossAxisAlignment |
start |
Column alignment |
AnimatedVisibility(
visible: isHovered,
minOpacity: 0.35,
ignorePointerWhenHidden: true,
child: ActionButton(),
)
| Parameter |
Type |
Default |
Description |
child |
Widget |
required |
Widget to fade |
visible |
bool |
required |
true → full opacity, false → minOpacity |
minOpacity |
double |
0.35 |
Opacity when visible is false |
duration |
Duration |
120ms |
Transition duration |
ignorePointerWhenHidden |
bool |
false |
Block hit-testing when visible is false |
AnimatedSurface(
decoration: BoxDecoration(
color: isSelected ? Colors.blue : Colors.grey,
borderRadius: BorderRadius.circular(8),
),
child: MyTile(),
)
| Parameter |
Type |
Default |
Description |
decoration |
Decoration |
required |
Target decoration |
child |
Widget |
required |
Child widget |
duration |
Duration |
120ms |
Transition duration |
ScaleToggle(
scaled: isSelected,
minScale: 0.9,
child: MyCard(),
)
| Parameter |
Type |
Default |
Description |
child |
Widget |
required |
Widget to scale |
scaled |
bool |
required |
true → full size, false → minScale |
minScale |
double |
0.9 |
Scale when scaled is false ((0.0, 1.0]) |
duration |
Duration |
150ms |
Transition duration |
SlideToggle(
visible: isExpanded,
hiddenOffset: const Offset(0, -1), // slides in from above
child: MyPanel(),
)
| Parameter |
Type |
Default |
Description |
child |
Widget |
required |
Widget to slide |
visible |
bool |
required |
true → natural position, false → hiddenOffset |
hiddenOffset |
Offset |
Offset(0, 1) |
Fractional offset when hidden (one height below) |
duration |
Duration |
300ms |
Transition duration |
PulseAnimation(
minScale: 0.9,
minOpacity: 0.6,
child: NotificationBadge(),
)
| Parameter |
Type |
Default |
Description |
child |
Widget |
required |
Widget to pulse |
minScale |
double |
0.9 |
Scale at trough of pulse |
minOpacity |
double |
0.6 |
Opacity at trough of pulse |
duration |
Duration |
900ms |
One full pulse cycle |
ShakeAnimation(
trigger: _errorCount, // increment to fire a shake
child: MyTextField(),
)
| Parameter |
Type |
Default |
Description |
child |
Widget |
required |
Widget to shake |
trigger |
Object |
required |
Change this value to replay the shake |
duration |
Duration |
500ms |
Duration of one shake |
offset |
double |
6.0 |
Max horizontal displacement (logical pixels) |
SkeletonBox(
child: Container(
width: 200, height: 16,
decoration: BoxDecoration(
color: Colors.grey.shade800,
borderRadius: BorderRadius.circular(4),
),
),
)
| Parameter |
Type |
Default |
Description |
child |
Widget |
required |
Widget to shimmer |
color |
Color? |
surface@65% |
Shimmer highlight color |
duration |
Duration |
1500ms |
Duration of one shimmer cycle |
CountUpText(
value: totalScore.toDouble(),
formatter: (v) => v.toInt().toString(),
)
| Parameter |
Type |
Default |
Description |
value |
double |
required |
Target number to count to |
duration |
Duration |
800ms |
Count-up animation duration |
style |
TextStyle? |
null |
Text style |
formatter |
String Function(double)? |
rounded int |
Custom number formatter |
TypewriterText(
text: 'Hello, world!',
duration: const Duration(milliseconds: 1200),
delay: const Duration(milliseconds: 300),
)
| Parameter |
Type |
Default |
Description |
text |
String |
required |
Text to reveal |
duration |
Duration |
1200ms |
Total reveal duration |
delay |
Duration |
Duration.zero |
Wait before starting |
style |
TextStyle? |
null |
Text style |
All widgets check MediaQuery.of(context).disableAnimations:
| Widget |
Reduce-motion behaviour |
FadeEntrance |
Returns child unchanged |
ScaleEntrance |
Returns child unchanged |
StaggeredList |
Each FadeEntrance snaps (no animation) |
AnimatedVisibility |
Snaps to target opacity via Opacity |
AnimatedSurface |
Uses Duration.zero — snaps immediately |
ScaleToggle |
Uses Duration.zero — snaps immediately |
SlideToggle |
Uses Duration.zero — snaps immediately |
PulseAnimation |
Returns child unchanged |
ShakeAnimation |
Returns child unchanged |
SkeletonBox |
Returns child unchanged |
CountUpText |
Shows target value immediately |
TypewriterText |
Shows full text immediately |
MIT — see LICENSE.