flutter_swipe_actions 0.3.1
flutter_swipe_actions: ^0.3.1 copied to clipboard
Smooth iOS Mail-inspired swipe actions for Flutter lists, with a reusable controller and customizable action buttons.
flutter_swipe_actions #
Smooth swipe actions for Flutter lists, inspired by the iOS Mail interaction.
The package includes:
SwipeActions: wraps a list item and reveals leading or trailing actions.SwipeController: keeps only one row open at a time and exposes open/close helpers.SwipeActionButton: a reusable circular action button with a label.- RTL-aware direction handling.
- Animated item decoration and action transitions.
- Optional auto-close when an action is tapped.
Usage #
final swipeController = SwipeController();
SwipeActions(
controller: swipeController,
leadingActions: [
SwipeActionButton(
icon: const Icon(Icons.mark_email_unread_outlined),
label: 'Unread',
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
onPressed: () {},
),
],
trailingActions: [
SwipeActionButton(
icon: const Icon(Icons.flag_outlined),
label: 'Flag',
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
onPressed: () {},
),
SwipeActionButton(
icon: const Icon(Icons.archive_outlined),
label: 'Archive',
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
onPressed: () {},
),
],
child: const ListTile(
title: Text('Weekly update'),
subtitle: Text('Your message is ready to review.'),
),
)
Controller #
Use a shared SwipeController for every row in the same list:
final swipeController = SwipeController();
// Programmatically close the currently open item.
swipeController.closeAll();
// Read stable state without depending on an internal State object.
final hasOpenItem = swipeController.hasOpenItem;
final openSide = swipeController.openSide;
This prevents multiple rows from staying open at the same time.
Action behavior #
SwipeActionButton closes the nearest SwipeActions row after it is tapped.
This matches the usual Mail-style interaction:
SwipeActionButton(
icon: const Icon(Icons.archive_outlined),
label: 'Archive',
onPressed: archiveMessage,
)
For multi-step actions, loading states, or confirmation flows, keep the row open:
SwipeActionButton(
icon: const Icon(Icons.more_horiz),
label: 'More',
closeOnPressed: false,
onPressed: showMoreActions,
)
Custom action widgets can close the nearest row with:
SwipeActions.maybeOf(context)?.closeActions();
Callbacks #
onOpened and onClosed fire after the animation reaches its final state.
Use onOpenChanged when you need the side that opened:
SwipeActions(
onOpenChanged: (side) {
if (side == null) {
debugPrint('Closed');
} else {
debugPrint('Opened $side');
}
},
trailingActions: actions,
child: child,
)
Visual tuning #
SwipeActions accepts any Widget as an action, but SwipeActionButton is the
recommended default for normal list actions. Keep custom widgets for special
cases such as loading states, badges, or multi-step actions.
By default, action widths are adaptive: the row starts with
estimatedActionWidth, measures each visible action, and then adjusts the
reveal distance to the real content width. If your design needs a strict fixed
width, pass actionWidth.
The default closed surface uses Theme.of(context).colorScheme.surface, so the
item does not visually change before the swipe starts. You can customize both
closed and opened colors without rebuilding full decorations:
SwipeActions(
// Optional. Remove this line to keep adaptive action widths.
actionWidth: 72,
actionSpacing: 4,
closedBackgroundColor: Theme.of(context).colorScheme.surface,
openedBackgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
itemBorderRadius: BorderRadius.circular(20),
trailingActions: const [],
child: child,
)
When actionWidth is null, these values control the adaptive width behavior:
SwipeActions(
estimatedActionWidth: 96, // first-frame fallback before measurement
minActionWidth: 56,
maxActionWidth: 96,
actionSpacing: 6, // also used between item and first action
trailingActions: actions,
child: child,
)
Compatibility #
actions is kept as an alias for trailing actions:
SwipeActions(
actions: [SwipeActionButton(...)],
child: child,
)
For new code, prefer leadingActions and trailingActions.