M3E Dismissible

A comprehensive Flutter package providing expressive, Material 3 card lists with dynamically rounded corners inside normal ListViews and CustomScrollViews (via slivers). It offers interactive dismissible cards featuring expressive M3 styling and spring physics.
It automatically calculates and draws the corners to fit exactly the Material 3 Expressive spec for adjacent items. It gives extensive customization options including customizable splash ripples, custom border colors, custom elevation and highly tunable haptic feedback along with stiffness and damping for animations.
🎮 Interactive Demo
You can try out the package demo here: m3e_core demo
🚀 Features
- Dynamic border radius: The first and last items get a larger outer radius while adjoining cards receive a smaller inner radius seamlessly.
- Reorderable Dismissible List (
M3EReorderableDismissibleList): Combines spring-physics vertical drag reordering with horizontal swipe-to-dismiss and action reveal. - Keyboard Navigation & Accessible Focus Rings: Full desktop & web keyboard support (Arrow keys, Space, Enter, Delete, Escape, Alt+Up/Down) with zero-layout-impact focus rings.
- Swipe Action Buttons: Expressive vertical pill action buttons (
M3ESwipeAction) with staggered reveal animation, threshold haptics, dismiss-on-tap animation, and primary auto-execute. - Pressed Scale Micro-interactions: Tactile squish feedback on touch/press down (
pressedScale&pressedMotion). - Modern InkSparkle Splash: Default
InkSparklesplash effect matching modern Material 3 specifications. - Direction Control: Configurable swipe direction (
DismissDirection) — horizontal, left-only, right-only, or disabled. - Accessibility Triggers: Single-tap, double-tap, or long-press to reveal hidden actions per Material 3 Accessibility Guidelines.
- Empty State Builder:
emptyBuilderparameter on all list variants for empty-state widgets. - Physics & Animations: Spring-driven physics for dragging. Neighbour-pull effects on swipe.
- Highly Customizable: Complete control over gaps, radii, colors, haptics, focus rings, and padding.
- Sliver & Column Support: Provides Slivers and Column wrappers out of the box to beautifully tie into complex layouts.
⚠️ Breaking Changes (v0.1.0)
- The
neighbourStiffnessandneighbourDampingproperties have been removed and replaced with a singleneighbourMotionproperty of typeM3EMotion. - Haptic properties (
hapticOnTapandhapticOnThreshold) now accept theM3EHapticFeedbackenum instead of integer constants for better type safety and clarity.
📦 Installation
Important
Flutter 3.47+ & material_ui Requirement (v1.0.0+):
Starting with v1.0.0, m3e_dismissible is migrated to use the standalone material_ui package decoupled in Flutter 3.47.0.
- Requires Flutter SDK
>=3.47.0. - Ensure your app imports
package:material_ui/material_ui.dart(or rundart fix --apply --code=migrate_design_widgets). - If you are on Flutter
< 3.47.0, please usem3e_dismissible: ^0.1.2.
Add m3e_dismissible and material_ui to your pubspec.yaml:
dependencies:
material_ui: ^1.1.1
m3e_dismissible: ^1.0.3
import 'package:material_ui/material_ui.dart';
import 'package:m3e_dismissible/m3e_dismissible.dart';
🧩 Components & Usage
M3E Dismissible Cards
Swipe-to-dismiss items with a beautiful spring-driven "neighbour pull" effect.
🔴 Dismissible M3E (Gmail Style)
🔴 Dismissible M3E (neighbourPull: 50.0, neighbourReach: 3, stiffness: 500, damping: 0.25, dismissThreshold: 0.6)
Usage:
M3EDismissibleCardList(
itemCount: items.length,
itemBuilder: (ctx, i) => Text(items[i].title),
onDismiss: (i, dir) async {
items.removeAt(i);
return true;
},
style: const M3EDismissibleCardStyle(
outerRadius: 24,
dismissThreshold: 0.3,
neighbourPull: 12.0,
),
)
M3EDismissibleCardList Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
itemCount |
int |
Required | Number of data items. |
itemBuilder |
IndexedWidgetBuilder |
Required | Builds content for each item. |
onDismiss |
Future<bool> Function(int, DismissDirection)? |
null |
Called when swipe exceeds threshold. Return true to dismiss. |
onTap |
void Function(int)? |
null |
Called on tap (blocked during drag). |
style |
M3EDismissibleCardStyle |
const M3EDismissibleCardStyle() |
Visual and interaction configuration. |
physics |
ScrollPhysics? |
null |
Scroll physics override. |
scrollController |
ScrollController? |
null |
Scroll controller. |
listPadding |
EdgeInsetsGeometry? |
null |
Padding around the entire list. |
shrinkWrap |
bool |
false |
Whether the list should shrink-wrap its children. |
clipBehavior |
Clip |
Clip.hardEdge |
Clip behavior for the list. |
M3EDismissibleCardStyle Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
outerRadius |
double |
18.0 |
Outer corner radius for first/last/single items. |
innerRadius |
double |
4.0 |
Inner corner radius for middle items. |
selectedBorderRadius |
double? |
null |
Radius applied to the dragged card once threshold is crossed. |
backgroundBorderRadius |
double? |
100 |
Radius applied to the background when swiping start-to-end. |
secondaryBackgroundBorderRadius |
double? |
100 |
Radius applied to the secondary background when swiping end-to-start. |
collapseSpeed |
double |
50 |
Speed of collapse animation after the card is dismissed. Higher Number = Faster Collapse, Lower Number = Slower Collapse |
gap |
double |
3.0 |
Vertical gap between cards. |
color |
Color? |
surfaceContainer |
Card background colour. |
padding |
EdgeInsetsGeometry? |
null |
Inner padding of each card's content area. |
margin |
EdgeInsetsGeometry? |
null |
Outer margin around each card. |
border |
BorderSide? |
null |
Optional border drawn on every card. |
elevation |
double |
0.0 |
Resting elevation. |
boxShadow |
List<BoxShadow>? |
null |
Custom shadow override. Set to empty list [] to remove shadow completely. |
pressedScale |
double? |
null |
Scale factor applied when card is pressed down (e.g. 0.97 for tactile squish). |
pressedMotion |
M3EMotion |
expressiveSpatialFast |
Spring motion for press-down and release scale animation. |
focusRingColor |
Color? |
null |
Custom focus ring color for keyboard navigation. |
focusRingGap |
double |
0.0 |
Gap between card edge and focus ring. |
focusRingWidth |
double |
2.0 |
Stroke width of the focus ring. |
background |
Widget? |
null |
Revealed background when swiping start-to-end. |
secondaryBackground |
Widget? |
null |
Revealed background when swiping end-to-start. |
splashColor |
Color? |
null |
Ink splash color. |
highlightColor |
Color? |
null |
Ink highlight color. |
splashFactory |
InteractiveInkFeatureFactory? |
InkSparkle.splashFactory |
Splash factory (InkSparkle by default). |
enableFeedback |
bool |
true |
Whether gestures provide acoustic/haptic feedback. |
hapticOnTap |
M3EHapticFeedback |
none |
Haptic feedback intensity on tap. |
dismissThreshold |
double |
0.2 |
Fraction of width before dismiss triggers. |
hapticOnThreshold |
M3EHapticFeedback |
light |
Haptic feedback level when crossing dismiss or action reveal threshold. |
dismissHapticStream |
bool |
false |
Fire continuous light haptics during drag. |
neighbourPull |
double |
8.0 |
Maximum pixel offset applied to neighbouring cards. |
neighbourReach |
int |
3 |
How many cards above + below the dragged card are affected. |
neighbourMotion |
M3EMotion |
800/0.7 |
Spring motion for neighbour snapping. |
snapBackMotion |
M3EMotion |
380/0.6 |
Motion for snapping back when drag is released below threshold. |
flyMotion |
M3EMotion |
400/0.8 |
Motion for flying off screen when drag is released above threshold. |
direction |
DismissDirection |
horizontal |
Allowed swipe directions. Use none to disable swipe gestures. |
actions |
List<M3ESwipeAction>? |
null |
Action buttons revealed when swiping start-to-end (right in LTR). |
secondaryActions |
List<M3ESwipeAction>? |
null |
Action buttons revealed when swiping end-to-start (left in LTR). |
actionSpacing |
double |
8.0 |
Spacing between revealed action buttons. |
autoExecutePrimaryOnFullSwipe |
bool |
true |
Auto-trigger primary action when swiping past threshold. |
actionRevealTrigger |
M3EActionRevealTrigger |
none |
Single-point interaction to reveal actions (tap, doubleTap, longPress). |
emptyBuilder |
WidgetBuilder? |
null |
Builder for empty-state widget when there are no items. |
Variants Available:
M3EReorderableDismissibleList,SliverM3EDismissibleCardList,M3EDismissibleCardColumn
🔄 Reorderable Dismissible List (M3EReorderableDismissibleList)
A unified Material 3 Expressive list component that seamlessly combines spring-physics vertical drag reordering (dynamic placeholder slot, bouncy spring neighbor displacement) with horizontal swipe-to-dismiss and action reveal.
M3EReorderableDismissibleList(
itemCount: items.length,
keyBuilder: (index) => ValueKey(items[index].id),
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) newIndex--;
final item = items.removeAt(oldIndex);
items.insert(newIndex, item);
});
},
onDismiss: (index, direction) async {
setState(() => items.removeAt(index));
return true;
},
style: const M3EDismissibleCardStyle(
outerRadius: 20.0,
innerRadius: 4.0,
pressedScale: 0.97,
actions: [
M3ESwipeAction(
icon: Icon(Icons.archive),
label: Text('Archive'),
),
],
),
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].title),
trailing: const Icon(Icons.drag_handle),
);
},
)
⌨️ Keyboard Navigation & Accessibility
m3e_dismissible provides comprehensive keyboard navigation and zero-layout-impact focus rings out of the box:
| Shortcut | Action |
|---|---|
Tab / Shift + Tab |
Focus next / previous card or action button. |
ArrowLeft / ArrowRight |
Reveal leading / trailing swipe action buttons on focused card. |
ArrowLeft / ArrowRight (in action drawer) |
Navigate focus between individual action buttons. |
Space / Enter |
Trigger focused action button or tap card. |
Escape |
Close revealed action drawer and return focus to the card. |
Delete / Backspace |
Dismiss the focused card (or open actions if actions configured). |
Alt + ArrowDown / Alt + ArrowUp |
Reorder item forward / backward in M3EReorderableDismissibleList. |
🏗️ Motion System (M3EMotion)
Spring physics presets for animations. All presets use specific stiffness and damping values to achieve their respective feels.
🏗️ Spatial Presets (Shape Morphing)
Used for animating the container shape and border radius.
| Preset Name | Stiffness | Damping | Description |
|---|---|---|---|
standardSpatialFast |
1400 |
0.9 |
Snappy spring for responsive feel. |
standardSpatialDefault |
700 |
0.9 |
Balanced spring for general use. |
standardSpatialSlow |
300 |
0.9 |
Relaxed spring for dramatic feel. |
aospSpatial |
380 |
1.0 |
Matches AOSP's notification list feel (no bounce). |
expressiveSpatialFast |
800 |
0.6 |
Bouncier spring for expressive feel. |
expressiveSpatialDefault |
380 |
0.75 |
Bouncy, balanced spring for expressive feel. |
expressiveSpatialSlow |
200 |
0.8 |
Very bouncy spring for dramatic expressive feel. |
✨ Effects Presets (Opacity/Scale)
Used for internal content animations like cross-fades.
| Preset Name | Stiffness | Damping | Description |
|---|---|---|---|
standardEffectsFast |
3800 |
1.0 |
Snappy effect animation. |
standardEffectsDefault |
1600 |
1.0 |
Balanced effect animation. |
standardEffectsSlow |
800 |
1.0 |
Relaxed effect animation. |
expressiveEffectsFast |
3800 |
1.0 |
Snappy expressive effect. |
expressiveEffectsDefault |
1600 |
1.0 |
Balanced expressive effect. |
expressiveEffectsSlow |
800 |
1.0 |
Relaxed expressive effect. |
🛠️ Custom Motion
Create a custom spring with specific physics:
M3EMotion.custom(stiffness: 380, damping: 0.75, snapToEnd: false)
M3ESwipeAction
Expressive vertical pill action buttons revealed when swiping a card.
| Parameter | Type | Default | Description |
|---|---|---|---|
icon |
Widget |
Required | The icon widget representing this action. |
label |
Widget? |
null |
Optional label text displayed below the icon. |
backgroundColor |
Color? |
secondaryContainer / primary |
Background color for the action button. |
foregroundColor |
Color? |
onSecondaryContainer / onPrimary |
Foreground color (icon & label). |
onTap |
VoidCallback? |
null |
Callback when this action is tapped or triggered by full swipe. |
isPrimary |
bool |
false |
Whether this is the primary end-aligned action (auto-fired on full swipe). |
dismissOnTap |
bool |
false |
Whether tapping this action animates card dismissal (fly-out and collapse) before calling onTap. |
width |
double |
52.0 |
Width of the vertical pill button. |
height |
double? |
null |
Explicit height (inherits card slot height if null). |
borderRadius |
BorderRadiusGeometry? |
BorderRadius.circular(100) |
Custom border radius. |
haptic |
M3EHapticFeedback |
medium |
Haptic feedback when tapped. |
M3EDismissibleCardList(
style: M3EDismissibleCardStyle(
actions: [
M3ESwipeAction(
icon: const Icon(Icons.archive_rounded),
label: const Text('Archive'),
backgroundColor: Colors.teal,
foregroundColor: Colors.white,
onTap: () => print('Archived!'),
),
M3ESwipeAction(
icon: const Icon(Icons.delete_rounded),
label: const Text('Delete'),
isPrimary: true,
dismissOnTap: true,
onTap: () => print('Deleted!'),
),
],
secondaryActions: [
M3ESwipeAction(
icon: const Icon(Icons.reply_rounded),
label: const Text('Reply'),
),
],
),
...
)
🎯 Check the Example App for more details.
🐞 Found a bug? or ✨ You have a Feature Request?
Feel free to open a Issue or Contribute to the project.
Hope You Love It!
Credits
- Motor Pub Package for Expressive Animations
- Claude and Gemini for helping me with the code and documentation.
Radhe Radhe 🙏
Libraries
- m3e_dismissible
- Material 3 Expressive dismissible cards for Flutter.