mayr_events 2.0.0
mayr_events: ^2.0.0 copied to clipboard
A lightweight, expressive event and listener system for Dart, inspired by Laravel's event architecture.
mayr_events #
A lightweight, expressive event and listener system for Dart β inspired by Laravel's event architecture.
Mayr Events helps you decouple logic in your app using an elegant, easy-to-read syntax while supporting async listeners, isolates, middleware hooks, and more.
π Features #
- β Simple functional API - no class extension needed
- β Event-level hooks (beforeHandle, shouldHandle, onError)
- β Global keyed handlers for cross-cutting concerns
- β Async listeners with isolate support
- β Queued listeners with retry and timeout support
- β Once-only listeners
- β Pure Dart - works everywhere
π§© Installation #
Add to your pubspec.yaml
:
dependencies:
mayr_events: ^2.0.0
Then import:
import 'package:mayr_events/mayr_events.dart';
π‘ New to mayr_events? Check out the Quick Start Guide for a 5-minute tutorial!
βοΈ Setup #
Create a function to register your listeners and handlers:
void setupEvents() {
// Register listeners
MayrEvents.on<UserRegisteredEvent>(SendWelcomeEmailListener());
MayrEvents.on<OrderPlacedEvent>(ProcessOrderListener());
// Add global handlers
MayrEvents.beforeHandle('logger', (event, listener) async {
print('Handling ${event.runtimeType}');
});
MayrEvents.onError('error_logger', (event, error, stack) async {
print('Error: $error');
});
MayrEvents.shouldHandle('validator', (event) {
// Return false to prevent listener execution
return true;
});
}
Call this function before firing any events (typically in main()
):
void main() {
setupEvents();
runApp(MyApp());
}
π§ Defining Events #
Events are simple data classes extending MayrEvent
:
class UserRegisteredEvent extends MayrEvent {
final String userId;
final String email;
const UserRegisteredEvent(this.userId, this.email);
}
Event-Level Hooks #
Events can define their own hooks:
class UserRegisteredEvent extends MayrEvent {
final String userId;
final String email;
const UserRegisteredEvent(this.userId, this.email);
@override
Future<void> Function(MayrEvent, MayrListener)? get beforeHandle =>
(event, listener) async {
print('About to handle user registration');
};
@override
bool Function(MayrEvent)? get shouldHandle =>
(event) => (event as UserRegisteredEvent).userId.isNotEmpty;
@override
Future<void> Function(MayrEvent, Object, StackTrace)? get onError =>
(event, error, stack) async {
print('Registration failed: $error');
};
}
π Defining Listeners #
Listeners handle events:
class SendWelcomeEmailListener extends MayrListener<UserRegisteredEvent> {
@override
Future<void> handle(UserRegisteredEvent event) async {
await EmailService.sendWelcome(event.userId);
print('Welcome email sent to ${event.email}');
}
}
Once-Only Listeners #
class TrackAppLaunchListener extends MayrListener<AppLaunchedEvent> {
@override
bool get once => true;
@override
Future<void> handle(AppLaunchedEvent event) async {
print('This listener runs only once.');
}
}
π Firing Events #
Anywhere in your app:
await MayrEvents.fire(UserRegisteredEvent('U123', 'user@example.com'));
π§ Advanced Features #
Global Handlers with Keys #
Register multiple handlers using unique keys:
// Add handlers
MayrEvents.beforeHandle('logger', loggerCallback);
MayrEvents.beforeHandle('metrics', metricsCallback);
MayrEvents.onError('sentry', sentryCallback);
MayrEvents.shouldHandle('rate_limiter', rateLimitCallback);
// Remove specific handlers
MayrEvents.removeBeforeHandler('logger');
MayrEvents.removeErrorHandler('sentry');
MayrEvents.removeShouldHandle('rate_limiter');
Listener Management #
// Remove specific listener
final listener = SendWelcomeEmailListener();
MayrEvents.on<UserRegisteredEvent>(listener);
MayrEvents.remove<UserRegisteredEvent>(listener);
// Remove all listeners for an event
MayrEvents.removeAll<UserRegisteredEvent>();
// Clear everything
MayrEvents.clear();
// Check listeners
bool hasListeners = MayrEvents.hasListeners<UserRegisteredEvent>();
int count = MayrEvents.listenerCount<UserRegisteredEvent>();
Run Listeners in Isolates #
For CPU-intensive operations:
class HeavyProcessingListener extends MayrListener<DataEvent> {
@override
bool get runInIsolate => true;
@override
Future<void> handle(DataEvent event) async {
// CPU-intensive work runs in separate isolate
}
}
Queued Listeners #
Queue listeners for background processing with automatic retry and timeout support:
void setupEvents() {
// Setup queue system first
MayrEvents.setupQueue(
fallbackQueue: 'default',
queues: ['emails', 'notifications', 'orders'],
defaultTimeout: Duration(seconds: 60),
);
// Register queued listeners
MayrEvents.on<OrderPlacedEvent>(ProcessOrderListener());
}
class ProcessOrderListener extends MayrListener<OrderPlacedEvent> {
@override
bool get queued => true; // Enable queuing
@override
String get queue => 'orders'; // Specify queue name
@override
Duration get timeout => Duration(seconds: 60); // Job timeout
@override
int get retries => 5; // Max retries (capped at 30)
@override
Future<void> handle(OrderPlacedEvent event) async {
// This runs asynchronously in the background
await processOrder(event.orderId);
}
}
Queue Features:
- Multiple named queues for organizing different types of jobs
- Automatic fallback queue for undefined queue names
- Configurable timeout per listener
- Automatic retry with configurable retry count (max 30)
- Queue workers auto-cleanup when empty
- Mix queued and non-queued listeners for the same event
π Complete Example #
import 'package:mayr_events/mayr_events.dart';
// Define event
class OrderPlacedEvent extends MayrEvent {
final String orderId;
final double total;
const OrderPlacedEvent(this.orderId, this.total);
}
// Define listener
class ProcessOrderListener extends MayrListener<OrderPlacedEvent> {
@override
Future<void> handle(OrderPlacedEvent event) async {
print('Processing order ${event.orderId}');
}
}
void setupEvents() {
MayrEvents.on<OrderPlacedEvent>(ProcessOrderListener());
MayrEvents.beforeHandle('logger', (event, listener) async {
print('[${DateTime.now()}] Event fired');
});
}
void main() async {
setupEvents();
await MayrEvents.fire(OrderPlacedEvent('ORD_123', 99.99));
}
π Migration from v1.x #
See MIGRATION.md for detailed upgrade instructions.
Key Changes in v2.0:
- No class extension required - use functional API
- Event-level hooks available
- Keyed handler system for better control
- Pure Dart package (no Flutter dependency)
π Documentation #
π€ Contributing #
Contributions are welcome! See CONTRIBUTING.md for guidelines.
π License #
MIT License - see LICENSE for details.
π Links #
- Repository: https://github.com/MayR-Labs/dart_events
- Homepage: https://mayrlabs.com
- Issues: https://github.com/MayR-Labs/dart_events/issues