magic_notifications 0.0.3
magic_notifications: ^0.0.3 copied to clipboard
Multi-channel notification system for Magic Framework. Supports database (in-app), push (OneSignal), and mail channels.
Magic Notifications
Multi-channel notifications for the Magic Framework.
Database, Push & Mail — one unified API.
Website · Docs · pub.dev · Issues · Discussions
Alpha —
magic_notificationsis under active development. APIs may change between minor versions until1.0.0.
Why Magic Notifications? #
Managing notifications in Flutter means juggling multiple channels — database polling, platform-specific push setup for iOS/Android/Web, email delivery, and user preference logic scattered across your codebase. Every project reinvents the same boilerplate.
Magic Notifications gives you a single, unified API for every channel. One config file drives everything. One CLI command sets up your project. Channels and drivers are swappable — switch from OneSignal to another push provider without touching application code.
Config-driven notifications. Define your channels, drivers, and preferences once. Magic Notifications handles the rest.
Features #
| Feature | Description | |
|---|---|---|
| 🔔 | Multi-channel | Database, Push, and Mail channels through one API |
| 📱 | OneSignal Push | iOS, Android, and Web push via onesignal_flutter |
| 🔄 | Real-time Polling | Background polling with pause/resume/stop lifecycle |
| 📡 | Socket Delivery | Take notification state from a broadcast channel instead, with polling as the fallback |
| 🎯 | User Preferences | Global and per-type channel preference management |
| 🛠 | CLI Tools | Interactive install, configure, doctor, test, and more |
| ⚙ | Config-Driven | All settings in one Dart config file via ConfigRepository |
| 💬 | Soft Prompt | Custom permission dialog before OS prompt |
| 🌐 | Web Support | Full web push via conditional JS interop |
Quick Start #
1. Add the dependency #
dependencies:
magic_notifications: ^0.0.3
2. Install configuration #
dart run <app>:artisan notifications:install
This generates lib/config/notifications.dart, injects NotificationServiceProvider into lib/config/app.dart, wires the notificationConfig factory into lib/main.dart, and configures platform-specific setup for your selected platforms.
3. Boot the provider #
The NotificationServiceProvider is automatically registered during install. On app boot, it:
- Creates the configured channels (database, push, mail)
- Initializes the push driver with your config
- Sets up background polling for database notifications
- Registers notification preferences
That's it — notifications now work across all configured channels.
Configuration #
After running the install command, edit lib/config/notifications.dart:
Map<String, dynamic> get notificationConfig => {
'notifications': {
'push': {
'driver': 'onesignal',
'app_id': const String.fromEnvironment('ONESIGNAL_APP_ID'),
'notify_button_enabled': false,
},
'database': {
'enabled': true,
'polling_interval': 30, // seconds
},
'mail': {
'enabled': false,
},
'soft_prompt': {
'enabled': true,
'title': 'Stay Updated',
'message': 'Get notified about important events.',
},
},
};
Add to .env:
ONESIGNAL_APP_ID=your-onesignal-app-id-here
All values are read at runtime via ConfigRepository — no hardcoded strings scattered across your codebase.
Usage #
Initialize Push After Login #
import 'package:magic_notifications/magic_notifications.dart';
Future<void> onLoginSuccess(User user) async {
await Notify.requestPushPermission();
await Notify.initializePush('user_${user.id}');
// Prefer the socket; startPolling() is the fallback and no-ops when the
// socket is live, so both calls are safe in either order.
await Notify.startRealtime(channel: 'App.Models.User.${user.id}');
Notify.startPolling();
}
External ID Format: Always use a prefix like
user_before the user ID. OneSignal blocks simple numeric values as external_id.
Display Notifications in UI #
NotificationDropdownWithStream(
notificationStream: Notify.notifications(),
onMarkAsRead: (id) => Notify.markAsRead(id),
onMarkAllAsRead: () => Notify.markAllAsRead(),
onNavigate: (path) => MagicRoute.to(path),
)
Clean Up on Logout #
Future<void> onLogout() async {
Notify.stopRealtime();
Notify.stopPolling();
await Notify.logoutPush();
}
Receive Notifications Over a Socket #
Notify.startRealtime() subscribes to the notifiable's private broadcast channel
and applies each notification.created frame directly to the stream, so a new
notification appears when the server publishes it instead of up to one polling
interval later. It returns false and changes nothing when the app has no
broadcast driver configured, which is what keeps startPolling() meaningful on a
deployment without a socket.
final bool live = await Notify.startRealtime(
channel: 'App.Models.User.${user.id}',
);
Behaviour worth knowing:
- The existing list is fetched ONCE on start. A socket only carries what happens next, so the rows that already exist still have to be read.
- A dropped connection falls back to polling; a reconnect drops the fallback and refetches once, because Reverb has no replay.
- A redelivered id replaces the held row rather than appending a duplicate.
The server half (the broadcast channel on the notification, the event name, and
the payload shape) is in
doc/basics/laravel-backend-setup.md.
CLI Tools #
All commands use the host app's artisan binary: dart run <app>:artisan notifications:[command]
| Command | Description |
|---|---|
notifications:install |
Interactive wizard to set up notifications |
notifications:configure |
Update notification configuration |
notifications:doctor |
Check installation and configuration health |
notifications:test |
Send test notifications to verify setup |
notifications:channels |
List all channels and their status |
notifications:publish |
Copy config stub to your project |
notifications:uninstall |
Remove plugin integration |
The notifications:doctor and notifications:channels commands are also available as read-only MCP tools for AI agents.
See the CLI Reference for all flags and options.
Architecture #
App launch → NotificationServiceProvider.boot()
→ reads config via ConfigRepository
→ creates channels (database, push, mail)
→ initializes OneSignalDriver
→ Notify facade delegates to NotificationManager
→ DatabaseChannel: stream + polling lifecycle
→ PushChannel: permission → initializePush → OneSignal
Key patterns:
| Pattern | Implementation |
|---|---|
| Singleton Manager | NotificationManager — central orchestrator |
| Strategy (Driver) | OneSignalDriver implements push driver contract |
| Facade | Notify — static API over NotificationManager |
| Service Provider | Two-phase bootstrap: register() (sync) → boot() (async) |
| IoC Container | All bindings via app.singleton() / app.make() |
Documentation #
| Document | Description |
|---|---|
| Installation | Adding the package and running the installer |
| Configuration | Config file reference and options |
| Channels | Database, Push, and Mail channel details |
| Drivers | Push driver contract and OneSignal implementation |
| Preferences | User notification preference management |
| CLI Tools | All CLI commands and flags |
| Laravel Backend | Laravel backend implementation guide |
| Notification Manager | Manager singleton and dispatch flow |
| Service Provider | Bootstrap lifecycle and IoC bindings |
Contributing #
Contributions are welcome! Please see the issues page for open tasks or to report bugs.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Write tests following the TDD flow — red, green, refactor
- Ensure all checks pass:
flutter test,dart analyze,dart format . - Submit a pull request
License #
Magic Notifications is open-sourced software licensed under the MIT License.
Built with care by FlutterSDK
If Magic Notifications helps your project, consider giving it a star on GitHub.