flutter_micro_interactions 0.2.0
flutter_micro_interactions: ^0.2.0 copied to clipboard
Pre-built micro-interactions and animations for Flutter: tap feedback, toasts, shimmer buttons, slide-to-confirm, staggered lists, typewriter text, and more.
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