Flutter Micro Interactions
A collection of ready-to-use micro-interactions and animations for Flutter. Add professional motion to your app with a few lines of code.

Why this package
- Simple API. Wrap a widget, get the animation. Every widget works with sensible defaults.
- Consistent. All widgets share the same core parameters:
duration,curve, andenabled. - Accessible. The package respects the platform "reduce motion" setting, announces state changes to screen readers, and supports keyboard focus and activation.
- Safe. Widgets react to configuration changes, clean up their resources, and are guarded against use after dispose.
- No extra dependencies. Only Flutter.
Requirements
Flutter 3.38 or newer.
Installation
Add the package to your pubspec.yaml:
dependencies:
flutter_micro_interactions: ^0.2.0
Then import it:
import 'package:flutter_micro_interactions/flutter_micro_interactions.dart';
Widgets
| Widget | What it does |
|---|---|
TapFeedback |
Scale, bounce, or fade feedback on tap |
ButtonStates |
Loading, success, and error states for any button |
HoverGlow |
Glow effect on hover and keyboard focus |
InputFocus |
Lift and scale effect when an input gains focus |
FloatingLabel |
Text field with an animated floating label and validation |
RippleEffect, RippleButton, WaterRipple |
Ripple animations |
PullToRefresh |
Pull-to-refresh with Material or adaptive indicator |
SwipeActions |
Swipe a list item to reveal tappable actions |
MicroReorderableList |
Drag and drop reordering with a lift effect |
LongPressMenu |
Context menu on long press |
ParallaxScroll, ParallaxImage |
Parallax scrolling effects |
ElasticScroll, ElasticListView, ElasticGridView |
Elastic overscroll |
ShakeToAction, ShakeAnimation, ShakeButton |
Shake animations and gestures |
MorphingShapes, ShapeMorph |
Smooth morphing between shapes |
MicroPageRoute, MicroNavigator |
Page transitions (fade, slide, scale, flip, blur, and more) |
LoadingSkeleton, TextSkeleton, CardSkeleton, ListItemSkeleton, GridItemSkeleton, SkeletonLoader |
Loading skeletons with shimmer |
ToastNotification |
Animated toast notifications |
CardFlip |
3D card flip |
SuccessBurst |
Particle burst for success confirmations |
PulseHighlight |
Pulse rings that draw attention to a widget |
AnimatedCounter |
Animated number changes, with an odometer mode |
AnimatedBadge |
Count badge that bounces when the count changes |
StaggeredList, StaggeredItem |
Staggered entrance animation for lists |
ShimmerButton |
Call-to-action button with a shimmer sweep |
SlideToConfirm |
Slide a thumb across a track to confirm an action |
ExpandableFab |
Floating button that expands into a set of actions |
AnimatedCheck |
Checkmark or cross drawn with an animated stroke |
AnimatedGradientBorder |
Animated sweeping gradient border |
TypewriterText |
Text that types itself out, with cursor and looping |
LiquidProgress |
Progress indicator filled with an animated wave |
Usage
Tap feedback
TapFeedback.scale(
onTap: () => print('Tapped'),
child: const Card(child: Padding(padding: EdgeInsets.all(16), child: Text('Tap me'))),
);
// Other effects
TapFeedback.bounce(child: myWidget);
TapFeedback.fade(child: myWidget);
TapFeedback.scaleAndOpacity(child: myWidget);
Button states
Control the state with a ButtonStateController:
final controller = ButtonStateController();
ButtonStates(
controller: controller,
child: ElevatedButton(
onPressed: () async {
controller.setLoading();
await submitForm();
controller.setSuccess();
},
child: const Text('Submit'),
),
);
You can pass your own loadingBuilder, successBuilder, and errorBuilder,
and use autoResetDuration to return to the idle state automatically.
Remember to dispose the controller.
Hover glow
HoverGlow(
child: Card(child: Padding(padding: EdgeInsets.all(16), child: Text('Hover me'))),
);
The glow also appears on keyboard focus.
Input focus
InputFocus(
child: TextField(
decoration: InputDecoration(labelText: 'Username'),
),
);
Floating label
A text field with an animated label that works with Form validation:
FloatingLabel(
label: 'Email',
validator: (value) {
if (value == null || value.isEmpty) return 'Email is required';
if (!value.contains('@')) return 'Enter a valid email';
return null;
},
);
Pull to refresh
PullToRefresh(
onRefresh: () async => loadData(),
child: ListView(children: items),
);
// Cupertino-style indicator on Apple platforms
PullToRefresh.adaptive(onRefresh: loadData, child: list);
Swipe actions
Swipe to reveal a row of tappable actions. More than one action per side is supported:
SwipeActions(
leftActions: [
MicroSwipeAction.favorite(context, onPressed: markFavorite),
],
rightActions: [
MicroSwipeAction.archive(context, onPressed: archive),
MicroSwipeAction.delete(context, onPressed: delete),
],
child: ListTile(title: Text('Swipe me')),
);
Reorderable list
Every child needs a key. The onReorder callback follows the standard Flutter convention:
MicroReorderableList(
onReorder: (oldIndex, newIndex) {
setState(() {
if (oldIndex < newIndex) newIndex -= 1;
final item = items.removeAt(oldIndex);
items.insert(newIndex, item);
});
},
children: [
for (final item in items)
ListTile(key: ValueKey(item), title: Text(item)),
],
);
Long press menu
LongPressMenu(
menuItems: [
LongPressMenuItem.copy(onTap: copy),
LongPressMenuItem.share(onTap: share),
LongPressMenuItem.delete(onTap: delete), // styled as destructive
],
child: Card(child: Text('Long press me')),
);
Parallax scroll
ParallaxScroll(
background: Image.asset('assets/background.jpg', fit: BoxFit.cover),
parallaxFactor: 0.5,
children: [
// Your scrollable content
],
);
Use ParallaxImage inside any list to give a single image a parallax effect:
ListView(
children: [
ParallaxImage(imageProvider: AssetImage('assets/photo.jpg')),
],
);
Elastic scroll
ElasticListView.builder(
elasticity: 0.3,
itemCount: 20,
itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
);
Shake
ShakeToAction triggers when the user makes a fast back-and-forth drag on the widget.
It is gesture based; it does not read the accelerometer. You can also trigger the
animation from code with a ShakeController:
final shake = ShakeController();
ShakeToAction(
controller: shake,
onShake: undoLastAction,
child: Card(child: Text('Shake me')),
);
// Later, from code:
shake.shake();
ShakeButton shakes when a press is not valid, for example on a form error:
ShakeButton(
isValid: formIsValid,
onPressed: submit,
builder: (context, handlePressed) =>
ElevatedButton(onPressed: handlePressed, child: const Text('Submit')),
);
Morphing shapes
MorphingShapes(
shapes: [MicroShapeType.circle, MicroShapeType.square, MicroShapeType.star],
autoPlay: true,
);
Use a MorphingShapesController to change shapes from code.
Page transitions
Navigator.of(context).push(
MicroPageRoute(type: MicroTransitionType.slideLeft, child: DetailScreen()),
);
// Or with the helper
MicroNavigator.push(context, DetailScreen(), type: MicroTransitionType.fade);
Available types: fade, slideRight, slideLeft, slideTop, slideBottom,
scale, rotate, size, flip, blur.
Loading skeletons
SkeletonLoader(
loading: isLoading,
skeleton: CardSkeleton(hasImage: true, titleLines: 1, subtitleLines: 2),
child: MyCard(),
);
When you show many skeletons at once, wrap them in a SkeletonScope so they
share one synchronized shimmer animation:
SkeletonScope(
child: ListView(
children: [for (var i = 0; i < 10; i++) const ListItemSkeleton()],
),
);
Toast notifications
context.showToast(
message: 'Saved successfully',
type: ToastType.success,
);
// With more options
context.showToast(
message: 'Connection lost',
type: ToastType.error,
position: ToastPosition.bottom,
actionLabel: 'Retry',
onAction: reconnect,
);
Toasts stack when several are visible, animate in and out, and are announced to screen readers.
Card flip
CardFlip(
front: Card(child: Text('Front')),
back: Card(child: Text('Back')),
);
Tap flips the card by default. Use a CardFlipController for programmatic
control, or pass isFlipped to control it from your own state.
Success burst
A one-shot particle burst for confirmations. Trigger it from code with a controller, or let a tap trigger it:
final burst = SuccessBurstController();
SuccessBurst(
controller: burst,
child: Icon(Icons.check_circle, size: 48),
);
// After a successful action:
burst.burst();
Pulse highlight
Draw attention to a widget with expanding pulse rings, for example during onboarding:
PulseHighlight(
maxPulseCount: 5,
child: IconButton(icon: Icon(Icons.settings), onPressed: openSettings),
);
The pulse stops when the user taps the widget. Use a PulseHighlightController
to start and stop it from code.
Animated counter
// Numeric count from the old value to the new one
AnimatedCounter(value: total);
// Odometer style: changed digits slide into place
AnimatedCounter(value: total, mode: CounterAnimationMode.slideDigits);
Animated badge
A count badge attached to a corner of a widget. It appears, disappears, and bounces on changes:
AnimatedBadge(
count: cartItems.length,
child: Icon(Icons.shopping_cart),
);
Staggered list
Children animate in one after another the first time they appear:
StaggeredList(
children: [for (final item in items) ListTile(title: Text(item))],
);
Use StaggeredList.builder for long lists, or wrap items of any custom layout
in StaggeredItem.
Shimmer button
A call-to-action button with a periodic shimmer sweep:
ShimmerButton(
onPressed: checkout,
child: Text('Checkout'),
);
Slide to confirm
A slide control for actions that deserve friction, such as payments or deletions. Screen reader and keyboard users can confirm directly:
SlideToConfirm(
label: 'Slide to pay',
onConfirmed: processPayment,
);
Expandable floating action button
A floating action button that expands into a set of actions, in a line or in an arc:
ExpandableFab(
direction: ExpandableFabDirection.radial,
actions: [
ExpandableFabAction(icon: Icon(Icons.edit), label: 'Edit', onPressed: edit),
ExpandableFabAction(icon: Icon(Icons.share), label: 'Share', onPressed: share),
],
);
Animated check
A checkmark or cross drawn stroke by stroke, for the end of a loading flow:
AnimatedCheck(type: AnimatedCheckType.check, onCompleted: close);
Animated gradient border
AnimatedGradientBorder(
child: Card(child: Padding(padding: EdgeInsets.all(16), child: Text('Featured'))),
);
Typewriter text
TypewriterText(
strings: ['Build faster.', 'Animate with one widget.'],
loop: true,
);
Liquid progress
LiquidProgress(
value: downloadProgress,
showPercentage: true,
);
Accessibility
- All animations respect the platform "reduce motion" setting. Decorative animations stop, and transitions become instant.
- Interactive widgets are exposed as buttons to screen readers and can be activated with Enter or Space.
- Toasts and button state changes are announced to screen readers.
- Swipe and long press actions are available as custom accessibility actions.
Migration from 0.0.x
Version 0.1.0 renames classes that conflicted with the Flutter SDK and other popular packages, and replaces fragile APIs with controllers.
| Before | After |
|---|---|
ReorderableList |
MicroReorderableList |
PageTransition |
MicroPageRoute |
PageTransitionType |
MicroTransitionType |
TransitionRoute |
MicroNavigator helpers |
SwipeAction |
MicroSwipeAction (factories take context) |
MenuItem |
LongPressMenuItem |
ShapeType |
MicroShapeType |
ButtonState |
MicroButtonState |
ButtonStates.withTransitions |
ButtonStates with ButtonStateController |
SharedAxisTransition, HeroTransition |
Removed |
withCardFlip, ReorderableListExtension |
Removed |
See the changelog for the full list of changes.
Example
The example app shows every widget in action:
cd example
flutter run
License
See the LICENSE file.
Support
If you find this package useful, you can support its development here: buymeacoffee.com/dvillegas
Libraries
- flutter_micro_interactions
- Pre-built micro-interactions and animations for Flutter apps.