M3E Dismissible

M3E Intro

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, and primary auto-execute.
  • Pressed Scale Micro-interactions: Tactile squish feedback on touch/press down (pressedScale & pressedMotion).
  • Modern InkSparkle Splash: Default InkSparkle splash 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: emptyBuilder parameter 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 neighbourStiffness and neighbourDamping properties have been removed and replaced with a single neighbourMotion property of type M3EMotion.
  • Haptic properties (hapticOnTap and hapticOnThreshold) now accept the M3EHapticFeedback enum 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 run dart fix --apply --code=migrate_design_widgets).
  • If you are on Flutter < 3.47.0, please use m3e_dismissible: ^0.1.2.

Add m3e_dismissible and material_ui to your pubspec.yaml:

dependencies:
  material_ui: ^1.1.1
  m3e_dismissible: ^1.0.2
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 List

🔴 Dismissible M3E (neighbourPull: 50.0, neighbourReach: 3, stiffness: 500, damping: 0.25, dismissThreshold: 0.6)

Dismissible M3E High Pull

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).
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,
        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.