Events
- Introduction
- Configuration
- Defining Events
- Defining Listeners
- Registering Events & Listeners
- Dispatching Events
- Inline Listeners
- Framework Events
Introduction
Magic provides a simple observer implementation, allowing you to subscribe and listen for various events that occur in your application. Events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other.
// Dispatch an event when an order ships
await Event.dispatch(OrderShipped(order));
// Listeners react to the event (send email, update analytics, etc.)
Configuration
Enabling Event Support
Add EventServiceProvider to your providers in config/app.dart:
'providers': [
(app) => EventServiceProvider(app),
(app) => AppEventServiceProvider(app), // Your custom events
// ... other providers
],
Create the event directories:
lib/app/
├── events/
│ └── order_shipped.dart
└── listeners/
└── send_shipment_notification.dart
Defining Events
An event class is a simple data container holding information related to the event:
import 'package:fluttersdk_magic/fluttersdk_magic.dart';
import '../models/order.dart';
class OrderShipped extends MagicEvent {
final Order order;
final String trackingNumber;
OrderShipped(this.order, {required this.trackingNumber});
}
Events are simple data classes—they don't contain any logic. The listener is responsible for processing the event.
Defining Listeners
Event listeners receive the event instance in their handle method:
import 'package:fluttersdk_magic/fluttersdk_magic.dart';
import '../events/order_shipped.dart';
class SendShipmentNotification extends MagicListener {
@override
Future handle(OrderShipped event) async {
// Send push notification
await NotificationService.send(
to: event.order.user,
title: 'Order Shipped!',
body: 'Your order #${event.order.id} is on its way.',
);
}
}
class UpdateAnalytics extends MagicListener {
@override
Future handle(OrderShipped event) async {
await Analytics.track('order_shipped', {
'order_id': event.order.id,
'value': event.order.total,
});
}
}
Registering Events & Listeners
Register mappings in your AppEventServiceProvider:
import 'package:fluttersdk_magic/fluttersdk_magic.dart';
import '../events/order_shipped.dart';
import '../events/user_registered.dart';
import '../listeners/send_shipment_notification.dart';
import '../listeners/update_analytics.dart';
import '../listeners/send_welcome_email.dart';
class AppEventServiceProvider extends EventServiceProvider {
AppEventServiceProvider(super.app);
@override
Map> get listen => {
// An event can have multiple listeners
OrderShipped: [
() => SendShipmentNotification(),
() => UpdateAnalytics(),
],
UserRegistered: [
() => SendWelcomeEmail(),
],
};
}
Dispatching Events
Use the Event facade to dispatch events:
import 'package:fluttersdk_magic/fluttersdk_magic.dart';
import '../events/order_shipped.dart';
class OrderController extends MagicController {
Future shipOrder(Order order) async {
// Update order status
order.status = 'shipped';
await order.save();
// Dispatch event - listeners handle the rest
await Event.dispatch(OrderShipped(
order,
trackingNumber: generateTrackingNumber(),
));
Magic.success('Shipped', 'Order has been shipped!');
}
}
Framework Events
Magic fires several system events automatically.
Authentication Events
| Event | Fired When |
|---|---|
AuthLogin |
User successfully logs in |
AuthLogout |
User logs out |
AuthFailed |
Authentication attempt fails |
Event.listen((event) {
Log.info('User logged in: ${event.user.email}');
});
Model Lifecycle Events
| Event | Fired When |
|---|---|
ModelSaving |
Before model is saved |
ModelSaved |
After model is saved |
ModelCreating |
Before new model is created |
ModelCreated |
After new model is created |
ModelUpdating |
Before existing model is updated |
ModelUpdated |
After existing model is updated |
ModelDeleted |
After model is deleted |
Event.listen((event) {
if (event.model is User) {
final user = event.model as User;
Log.info('New user registered: ${user.email}');
}
});
Gate Events
| Event | Fired When |
|---|---|
GateAbilityDefined |
Ability registered with Gate.define() |
GateAccessChecked |
After any ability check |
GateAccessDenied |
When access is denied |
// Log denied access attempts
Event.listen((event) {
Log.warning('Access denied: ${event.ability} for user ${event.user?.id}');
});
Database Events
| Event | Fired When |
|---|---|
DatabaseConnected |
Database connection established |
QueryExecuted |
After query execution (if enabled) |
[!TIP] Use events to decouple your application logic. Instead of calling multiple services directly, dispatch an event and let listeners handle it independently.