island_snack 0.1.0
island_snack: ^0.1.0 copied to clipboard
Dynamic Island-style notifications for Flutter iOS apps. Spring physics animations that expand from the island position.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:island_snack/island_snack.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return IslandSnackTheme(
successColor: const Color(0xFF7E9B72), // sage
errorColor: const Color(0xFFB87068), // rose
warningColor: const Color(0xFFC9834E), // terracotta
infoColor: const Color(0xFFC8B9A0), // sand
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.light,
scaffoldBackgroundColor: const Color(0xFFF5F5EF),
useMaterial3: true,
),
home: const DemoScreen(),
),
);
}
}
class DemoScreen extends StatelessWidget {
const DemoScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 48),
const Text(
'Island Snack',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.w700,
color: Color(0xFF141310),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 6),
const Text(
'Notifications from the Dynamic Island',
style: TextStyle(fontSize: 14, color: Color(0xFF7A7568)),
textAlign: TextAlign.center,
),
const SizedBox(height: 36),
_Btn(
label: 'Success',
accent: const Color(0xFF7E9B72),
onTap: () => IslandSnack.success(
context,
title: 'Photo kept',
subtitle: 'Added to your favorites',
),
),
_Btn(
label: 'Error',
accent: const Color(0xFFB87068),
onTap: () => IslandSnack.error(
context,
title: 'Failed to delete',
subtitle: 'Please try again later',
),
),
_Btn(
label: 'Warning',
accent: const Color(0xFFC9834E),
onTap: () => IslandSnack.warning(
context,
title: 'You are offline',
subtitle: 'Changes will sync when connected',
),
),
_Btn(
label: 'Info',
accent: const Color(0xFFC8B9A0),
onTap: () => IslandSnack.info(
context,
title: '12 photos archived',
subtitle: 'Tap to review',
),
),
_Btn(
label: 'Loading -> Success',
accent: const Color(0xFF8E8E93),
onTap: () {
IslandSnack.loading(
context,
id: 'scan',
title: 'Scanning photos...',
subtitle: 'Analyzing your library',
);
Future.delayed(const Duration(seconds: 2), () {
if (!context.mounted) return;
IslandSnack.success(
context,
id: 'scan',
title: 'Scan complete!',
subtitle: '42 blurry photos found',
);
});
},
),
_Btn(
label: 'With Undo Action',
accent: const Color(0xFF7E9B72),
onTap: () => IslandSnack.success(
context,
title: '5 photos deleted',
duration: IslandSnackDuration.action,
action: IslandSnackAction(
label: 'Undo',
onPressed: () {
if (!context.mounted) return;
IslandSnack.info(context, title: 'Restored!');
},
),
),
),
_Btn(
label: 'With Progress Bar',
accent: const Color(0xFFC9834E),
onTap: () => IslandSnack.show(
context,
title: 'Scanning for blurry photos...',
subtitle: '128 / 342',
progress: 0.37,
style: const IslandSnackStyle(
accentColor: Color(0xFFC9834E),
iconData: Icons.blur_on_rounded,
),
duration: IslandSnackDuration.action,
),
),
],
),
),
),
);
}
}
class _Btn extends StatelessWidget {
const _Btn({
required this.label,
required this.accent,
required this.onTap,
});
final String label;
final Color accent;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 15),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: accent.withValues(alpha: 0.3),
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: accent,
shape: BoxShape.circle,
),
),
const SizedBox(width: 10),
Text(
label,
style: const TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
);
}
}