save_points_background_service_builder 0.0.5
save_points_background_service_builder: ^0.0.5 copied to clipboard
A Flutter package to easily build and manage background services and notifications, optimized for Save Points.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:save_points_background_service_builder/save_points_background_service_builder.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize SharedPreferences for persistence
final prefs = await SharedPreferences.getInstance();
runApp(
ProviderScope(
overrides: [
// Required for the package to access local storage
sharedPreferencesProvider.overrideWithValue(prefs),
],
child: const NotificationExampleApp(),
),
);
}
class NotificationExampleApp extends StatelessWidget {
const NotificationExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Background Service Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF673AB7),
),
useMaterial3: true,
fontFamily: 'Inter',
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF673AB7),
brightness: Brightness.dark,
),
useMaterial3: true,
fontFamily: 'Inter',
),
home: const ExampleDashboard(),
);
}
}
class ExampleDashboard extends ConsumerWidget {
const ExampleDashboard({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final notifications = ref.watch(notificationsProvider);
// Listen to notification taps globally
ref.listen(notificationTapProvider, (previous, next) {
next.whenData((response) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Notification Tapped: ${response.payload}'),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
),
);
});
});
return Scaffold(
appBar: AppBar(
title: const Text('Notification Center'),
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildQuickActionCard(
context,
title: 'Schedule New Notification',
subtitle: 'Create a one-time or recurring reminder',
icon: Icons.add_alarm_rounded,
color: Theme.of(context).colorScheme.primaryContainer,
onTap: () => NotificationSchedulerSheet.show(context),
),
const SizedBox(height: 16),
_buildQuickActionCard(
context,
title: 'View Active Notifications',
subtitle: 'Manage and cancel your scheduled alerts',
icon: Icons.list_alt_rounded,
color: Theme.of(context).colorScheme.secondaryContainer,
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => NotificationListScreen(
itemBuilder: (context, notification) {
return Card(
margin: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 6,
),
child: ListTile(
title: Text(
notification.title,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
'${notification.body}\nType: ${notification.type.name.toUpperCase()}',
),
isThreeLine: true,
trailing: IconButton(
icon: const Icon(
Icons.delete_outline,
color: Colors.redAccent,
),
onPressed: () {
ref
.read(notificationsProvider.notifier)
.removeNotification(notification.id);
},
),
),
);
},
),
),
),
),
const SizedBox(height: 32),
Text(
'Quick Status',
style: Theme.of(
context,
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
notifications.when(
data: (list) => _buildStatusGrid(context, list.length),
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Text('Error: $e'),
),
const SizedBox(height: 32),
_buildTestSection(context, ref),
],
),
),
);
}
Widget _buildQuickActionCard(
BuildContext context, {
required String title,
required String subtitle,
required IconData icon,
required Color color,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(20),
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: color.withValues(alpha: 0.3),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white24,
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, size: 32),
),
const SizedBox(width: 20),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(subtitle, style: Theme.of(context).textTheme.bodySmall),
],
),
),
const Icon(Icons.chevron_right_rounded),
],
),
),
);
}
Widget _buildStatusGrid(BuildContext context, int count) {
return GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 1.5,
physics: const NeverScrollableScrollPhysics(),
children: [
_buildStatItem(
context,
'Active Notifications',
count.toString(),
Icons.notifications_active_outlined,
),
_buildStatItem(
context,
'System Status',
'Healthy',
Icons.check_circle_outline_rounded,
),
],
);
}
Widget _buildStatItem(
BuildContext context,
String label,
String value,
IconData icon,
) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).dividerColor),
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 20, color: Theme.of(context).colorScheme.primary),
const SizedBox(height: 8),
Text(
value,
style: Theme.of(
context,
).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold),
),
Text(label, style: Theme.of(context).textTheme.labelSmall),
],
),
);
}
Widget _buildTestSection(BuildContext context, WidgetRef ref) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(20),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
const Icon(Icons.bug_report_outlined),
const SizedBox(width: 12),
Text(
'Developer Tools',
style: Theme.of(context).textTheme.titleMedium,
),
],
),
const SizedBox(height: 16),
FilledButton.icon(
onPressed: () async {
final service = ref.read(notificationServiceProvider);
await service.showImmediateNotification(
title: 'Instant Test',
body: 'The notification system is working perfectly!',
);
},
icon: const Icon(Icons.flash_on_rounded),
label: const Text('Test Immediate Notification'),
),
const SizedBox(height: 8),
OutlinedButton.icon(
onPressed: () {
ref.read(notificationsProvider.notifier).initializeSystem();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Permissions re-requested')),
);
},
icon: const Icon(Icons.security_rounded),
label: const Text('Re-initialize Permissions'),
),
],
),
);
}
}