save_points_background_service_builder 0.0.3
save_points_background_service_builder: ^0.0.3 copied to clipboard
A Flutter package to easily build and manage background services and notifications, optimized for Save Points.
Save Points Notification Builder 🚀 #
A powerful and easy-to-use Flutter package for building and managing scheduled notifications. Designed with reliability in mind, specifically addressing common issues like MIUI battery optimizations and providing a modern stream-based API for notification interactions.
✨ Features #
- 📅 Flexible Scheduling: Support for one-time, daily, weekly, monthly, and yearly notifications.
- 👆 Interactive: Modern
Stream-based API to handle notification taps globally. - 🔄 Persistence: Automatically saves scheduled notifications using
SharedPreferences. - 🏗️ State Management: Built with Riverpod for robust and reactive state handling.
- 🧩 Modular Architecture: Cleanly separated logic for permissions, scheduling, timezone handling, and background processing.
- 📱 UI Components Included: Comes with ready-to-use
NotificationListScreen,NotificationSchedulerSheet, andMiuiWarningBanner. - 🔋 Battery Optimization Aware: Includes built-in instructions for aggressive battery optimization on devices like Xiaomi (MIUI).
- 🛠️ Developer Friendly: Simple API for scheduling, canceling, and testing notifications.
🚀 Quick Start (4 Simple Steps) #
1. Platform Setup #
Android
Add the following to your android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
</manifest>
Critical: Place an image named ic_notification.png in android/app/src/main/res/drawable/ to be used as the notification icon.
iOS
Add this to your ios/Runner/AppDelegate.swift:
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
2. Provider Initialization #
Wrap your app with ProviderScope and override the sharedPreferencesProvider. You can also optionally override notificationConfigProvider to provide initial notifications.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize SharedPreferences
final prefs = await SharedPreferences.getInstance();
runApp(
ProviderScope(
overrides: [
sharedPreferencesProvider.overrideWithValue(prefs),
// OPTIONAL: Provide initial configuration
notificationConfigProvider.overrideWithValue(
const SavePointsNotificationConfig(
initialNotifications: [
NotificationModel(
id: 'welcome',
title: 'Welcome! 👋',
body: 'Thanks for using our app!',
scheduleTime: DateTime.now(), // This is just a placeholder
type: NotificationScheduleType.oneTime,
),
],
),
),
],
child: const MyApp(),
),
);
}
3. Initialize System (Permissions) #
Trigger the internal setup (Timezones & Permission requests) once your app launches. This is usually done in your Home Screen's initState.
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// This requests permissions and sets up local timezones
ref.read(notificationsProvider.notifier).initializeSystem();
});
}
4. Schedule & Handle Taps #
Schedule a Notification:
final reminder = NotificationModel(
id: 'unique_id_123',
title: 'Hello! 🚀',
body: 'This is your scheduled reminder.',
scheduleTime: DateTime.now().add(const Duration(hours: 1)),
type: NotificationScheduleType.oneTime,
payload: 'any_custom_data',
);
ref.read(notificationsProvider.notifier).addNotification(reminder);
Listen to Taps Globally:
// Use this in any ConsumerWidget to react when a user clicks a notification
ref.listen(notificationTapProvider, (previous, next) {
next.whenData((response) {
print('Notification Tapped! Payload: ${response.payload}');
// Navigate to a specific screen here
});
});
🎨 Customization #
Notification Icon #
The package looks for a drawable resource named ic_notification by default.
- Android: Place your icon (white with transparency recommended) in
android/app/src/main/res/drawable/ic_notification.png. - Change Name: If you wish to use a different name, update the
NotificationConstants.notificationIconvalue.
Notification Sound #
By default, notifications use the system's default sound. To use a custom sound:
- Android:
- Place your sound file (e.g.,
mysound.mp3) inandroid/app/src/main/res/raw/. - Update
NotificationScheduler.dartto includesound: RawResourceAndroidNotificationSound('mysound')inAndroidNotificationDetails.
- Place your sound file (e.g.,
- iOS:
- Add the sound file to your Xcode project.
- Update
NotificationScheduler.dartto includepresentSound: trueand the sound filename inDarwinNotificationDetails.
Note: For Android, you must create a new Notification Channel or delete the existing app to see sound changes, as channels are immutable once created.
🛠️ Advanced Usage #
Modular Architecture #
The core notification logic is now split into specialized components for better maintainability:
NotificationService: The main high-level API.NotificationScheduler: Handles the complex date logic for different recurrence types.NotificationTimezoneHelper: Manages timezone initialization and local location detection.NotificationPermissionHandler: Handles platform-specific permission requests (Android 13+, iOS).NotificationChannelManager: Manages Android-specific notification channels.NotificationBackgroundHandler: Entry point for background notification interactions.
Using the Built-in UI #
The package includes a full management suite via NotificationListScreen. You can use it with the default state or provide your own external list of notifications.
Basic Usage (Using Global State):
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => NotificationListScreen(
itemBuilder: (context, notification) => ListTile(
title: Text(notification.title),
subtitle: Text(notification.body),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => ref.read(notificationsProvider.notifier).removeNotification(notification.id),
),
),
),
),
);
Custom Usage (External List & Custom FAB):
NotificationListScreen(
title: 'My Custom Reminders',
notifications: myExternalList, // List<NotificationModel>
showFab: true,
onAddPressed: () {
// Custom logic when "+" is clicked
},
itemBuilder: (context, notification) {
return MyCustomNotificationCard(notification: notification);
},
)
Syncing External Lists: If you have an external source of truth, you can sync it with the local repository:
await ref.read(notificationsProvider.notifier).setNotifications(myExternalList);
MIUI & Battery Optimization #
Many devices (Xiaomi, Huawei, Samsung) kill background alarms by default. We include a MiuiWarningBanner that helps users configure their device settings (Autostart, No Restrictions) to ensure notifications arrive on time.
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.