island_snack

pub package License: MIT

Dynamic Island-style notifications for Flutter iOS apps. Expands from the island position with spring physics animations, staggered content reveal, and swipe-to-dismiss.

On devices with a Dynamic Island the pill starts at the exact hardware cutout position. On older devices it falls back to a centered top-of-screen pill.

Features

  • Spring physics expand/collapse (underdamped overshoot + critically-damped retract)
  • Staggered per-element content reveal (icon, title, subtitle, action)
  • Preset styles: success, error, warning, info, loading
  • Queue system with normal and urgent priority
  • ID-based in-place updates (e.g. loading -> success)
  • Swipe-to-dismiss
  • Action buttons, progress bars, custom leading widgets
  • IslandSnackTheme for global color/font/duration/haptic overrides
  • onDismissed / onShown callbacks
  • Accessibility: VoiceOver live region, semantic labels

Platform support

Platform Supported
iOS Yes
Android No
Web No

Installation

dependencies:
  island_snack: ^0.1.0

Quick start

Wrap your app with IslandSnackTheme (optional) and call the static methods:

import 'package:island_snack/island_snack.dart';

// Optional: wrap your app for global theming
IslandSnackTheme(
  successColor: Color(0xFF7E9B72),
  fontFamily: 'SF Pro',
  child: MaterialApp(...),
);

// Show notifications from anywhere
IslandSnack.success(context, title: 'Saved');
IslandSnack.error(context, title: 'Upload failed', subtitle: 'Try again');
IslandSnack.warning(context, title: 'Offline');
IslandSnack.info(context, title: '12 items archived');
IslandSnack.loading(context, id: 'sync', title: 'Syncing...');

// Dismiss
IslandSnack.dismiss();
IslandSnack.dismissAll();

API reference

Preset methods

Method Icon Default color Default haptic
IslandSnack.success() checkmark #7E9B72 sage light
IslandSnack.error() x-circle #B87068 rose medium
IslandSnack.warning() warning #C9834E terracotta light
IslandSnack.info() info #C8B9A0 sand light
IslandSnack.loading() spinner #8E8E93 gray none

All preset methods accept:

IslandSnack.success(
  context,
  title: 'Saved',             // required
  subtitle: 'To favorites',   // optional
  id: 'save',                 // optional — for in-place updates
  duration: IslandSnackDuration.long,
  customDuration: Duration(seconds: 4),  // overrides enum duration
  haptic: IslandSnackHaptic.light,
  priority: IslandSnackPriority.normal,
  action: IslandSnackAction(label: 'Undo', onPressed: () {}),
  leading: Icon(Icons.star),  // custom leading widget
  onTap: () {},
  onDismissed: () {},
  onShown: () {},
);

IslandSnack.show()

Full control — same parameters as above plus:

IslandSnack.show(
  context,
  title: 'Processing',
  style: IslandSnackStyle(
    accentColor: Colors.purple,
    iconData: Icons.auto_awesome,
  ),
  progress: 0.65,      // 0.0 to 1.0
  isLoading: true,      // shows spinner instead of icon
);

Dismiss

IslandSnack.dismiss();     // dismiss current
IslandSnack.dismissAll();  // dismiss current + clear queue

State getters

IslandSnack.isShowing;       // bool — whether a notification is visible
IslandSnack.isDynamicIsland;  // bool? — null if not yet detected

ID-based updates

Assign the same id to replace a notification in-place:

IslandSnack.loading(context, id: 'upload', title: 'Uploading...');

// Later:
IslandSnack.success(context, id: 'upload', title: 'Upload complete!');

Queue behavior

  • Normal priority: queued — shows after the current notification dismisses.
  • Urgent priority: clears queue and replaces current immediately.
  • ID-based: if a notification with the same ID is showing, it is dismissed and replaced.

Customization

Theme

IslandSnackTheme(
  successColor: Color(0xFF7E9B72),
  errorColor: Color(0xFFB87068),
  warningColor: Color(0xFFC9834E),
  infoColor: Color(0xFFC8B9A0),
  loadingColor: Color(0xFF8E8E93),
  fontFamily: 'SF Pro',
  shortDuration: Duration(seconds: 2),
  longDuration: Duration(seconds: 3),
  actionDuration: Duration(seconds: 5),
  defaultHaptic: IslandSnackHaptic.light,
  child: MaterialApp(...),
);

Custom style

IslandSnack.show(
  context,
  title: 'Custom',
  style: IslandSnackStyle(
    accentColor: Colors.purple,
    iconData: Icons.auto_awesome,
  ),
);

Callbacks

IslandSnack.success(
  context,
  title: 'Done',
  onShown: () => print('Visible'),
  onDismissed: () => print('Gone'),
);

Accessibility

  • The notification pill is wrapped in a Semantics widget with liveRegion: true, so VoiceOver announces it automatically.
  • Action buttons have Semantics(button: true).
  • Decorative elements (accent line, debug overlay) are excluded from the semantic tree.

License

MIT — see LICENSE.

Libraries

island_snack
Dynamic Island-style notifications for Flutter iOS apps.