global_event_bus 1.3.1
global_event_bus: ^1.3.1 copied to clipboard
A high-performance, type-safe global event bus system for Flutter applications with priority support, batch processing, comprehensive logging, and visual debugging tools.
๐ Global Event Bus #
A high-performance, type-safe global event bus system for Flutter applications, enabling decoupled communication between different modules. Built on the observer pattern and stream processing architecture, it supports event priorities, batch processing, delayed sending, statistics monitoring, reactive Widgets, BLoC integration, and other advanced features.
โจ Core Features #
๐ Type Safety #
- Generic-based type-safe event system
- Compile-time type checking to avoid runtime errors
- Automatic type inference for better development experience
โก High Performance #
- Efficient event distribution based on Dart Stream
- Batch processing mode for high-frequency event scenarios
- Built-in event statistics and performance monitoring
๐ฏ Event Priority #
- Four-level priority system: critical, high, normal, low
- High-priority events are processed first
- Suitable for event classification in different business scenarios
๐ Comprehensive Logging System #
- Multi-level log configuration (debug, info, warning, error, none)
- Configurable log output format
- Support for custom log handlers
- Event type and listener filtering
๐ง Flexible Listening #
- Type listening: only listen for specific event types
- One-time listening: automatically removed after first trigger
- Multi-type listening: listen for multiple event types simultaneously
- Delayed sending: support sending events after a specified delay
๐ Statistics Monitoring #
- Real-time event statistics
- Send/receive counting
- Event type distribution statistics
- Performance monitoring data
๐จ Reactive Widget #
- GebBuilder: StreamBuilder-like reactive Widget
- Automatic subscription lifecycle management
- Support for initial data from history
๐ BLoC Integration #
- GebBlocBridge: Bidirectional bridge between BLoC and event bus
- GebBlocMixin: Direct event bus integration in BLoC classes
- GebBlocMapper: Event mapping utility class
๐ Event History #
- GebHistory: Event history manager
- Support querying history by type and count
- Configurable maximum records and enable/disable
๐๏ธ Architecture #
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Global Event Bus โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ GlobalEventManager (Singleton) โ
โ โโโ Event Sending & Dispatching โ
โ โโโ Listener Management โ
โ โโโ Batch Processing โ
โ โโโ Statistics Monitoring โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ GebBaseEvent (Base Event Class) โ
โ โโโ Event Type Identifier โ
โ โโโ Timestamp โ
โ โโโ Priority โ
โ โโโ Metadata โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ GebEvent<T> (Generic Event Class) โ
โ โโโ Type-safe Data Transfer โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ GebLogger (Logging System) โ
โ โโโ Multi-level Logging โ
โ โโโ Configurable Output Format โ
โ โโโ Custom Handler โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ฆ Installation #
Add the dependency to your pubspec.yaml:
dependencies:
global_event_bus: <latest_version>
Then run:
flutter pub get
๐ Quick Start #
1. Import Package #
import 'package:global_event_bus/global_event_bus.dart';
2. Configure Logging (Optional) #
void main() {
// Configure global event bus logging
globalEventBus.configureLogging(
const GebLogConfig(
level: GebLogLevel.debug,
showEventData: true,
showEventId: true,
showListenerInfo: true,
),
);
runApp(MyApp());
}
3. Send Events #
// Send simple event
globalEventBus.sendEvent<String>(
type: 'user_message',
data: 'Hello, World!',
);
// Send event with priority
globalEventBus.sendEvent<Map<String, dynamic>>(
type: 'user_login',
data: {
'userId': '12345',
'username': 'john_doe',
'loginTime': DateTime.now().toIso8601String(),
},
priority: GebPriority.high,
);
// Send delayed event
globalEventBus.sendEventDelayed<String>(
type: 'delayed_notification',
data: 'This is a delayed message',
delay: Duration(milliseconds: 3000), // Send after 3 seconds
);
// Send event without data
globalEventBus.sendEventWithoutData(type: 'app_started');
4. Listen to Events #
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late StreamSubscription<GebBaseEvent> _subscription;
@override
void initState() {
super.initState();
// Listen for specific event type
_subscription = globalEventBus.listen<String>(
listenerId: 'my_widget_listener',
onEvent: (GebEvent<String> event) {
print('Received event: ${event.type}, data: ${event.data}');
// Handle event data
setState(() {
// Update UI
});
},
);
}
@override
void dispose() {
_subscription.cancel(); // Cancel subscription
super.dispose();
}
}
5. Use GebBuilder (Recommended) #
// Use reactive Widget to listen to events, auto-manage lifecycle
class CounterDisplay extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GebBuilder<int>(
eventType: 'counter_updated',
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Text('Counter: 0');
}
return Text('Counter: ${snapshot.data}');
},
);
}
}
6. Use GebListener Mixin #
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with GebListener {
String _message = 'Waiting for events...';
@override
void initState() {
super.initState();
// Subscribe to events using Mixin method
gebSubscribe<String>(
listenerId: 'message_listener',
eventType: 'user_message',
onEvent: (GebEvent<String> event) {
setState(() {
_message = event.data;
});
},
);
}
@override
Widget build(BuildContext context) {
return Text(_message);
}
// All subscriptions automatically cancelled on dispose
}
API Reference #
Event Sending Methods
sendEvent - Send event with data
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| type | String | โ | - | Event type identifier |
| data | T | โ | - | Event data |
| priority | GebPriority | โ | GebPriority.normal | Event priority |
| metadata | Map<String, dynamic>? | โ | null | Event metadata |
sendEventWithoutData - Send event without data
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| type | String | โ | - | Event type identifier |
| priority | GebPriority | โ | GebPriority.normal | Event priority |
| metadata | Map<String, dynamic>? | โ | null | Event metadata |
sendEventSafe - Safely send event (no exceptions thrown)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| type | String | โ | - | Event type identifier |
| data | T | โ | - | Event data |
| priority | GebPriority | โ | GebPriority.normal | Event priority |
| metadata | Map<String, dynamic>? | โ | null | Event metadata |
sendEventDelayed - Send event with delay
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| type | String | โ | - | Event type identifier |
| data | T | โ | - | Event data |
| delay | Duration | โ | - | Delay duration |
| priority | GebPriority | โ | GebPriority.normal | Event priority |
| metadata | Map<String, dynamic>? | โ | null | Event metadata |
Event Listening Methods
listen - Listen to events
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| listenerId | String | โ | - | Unique listener identifier |
| onEvent | Function(GebEvent | โ | - | Event handler callback |
| eventTypes | List | โ | null | List of event types to listen for |
| onError | Function(Object)? | โ | null | Error callback |
listenOnce - Listen to event once
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| listenerId | String | โ | - | Unique listener identifier |
| onEvent | Function(GebEvent | โ | - | Event handler callback |
| eventTypes | List | โ | null | List of event types to listen for |
removeListener - Remove listener
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| listenerId | String | โ | - | ID of listener to remove |
removeAllListeners - Remove all listeners
cleanupExpiredListeners - Clean up expired listeners
Logging Configuration
configureLogging - Configure logging
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| config | GebLogConfig | โ | - | Log configuration object |
GebLogConfig Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| level | GebLogLevel | โ | GebLogLevel.info | Log level |
| enabled | bool | โ | true | Enable logging |
| showTimestamp | bool | โ | true | Show timestamp |
| showEventId | bool | โ | false | Show event ID |
| showPriority | bool | โ | true | Show priority |
| showEventData | bool | โ | false | Show event data |
| showListenerInfo | bool | โ | true | Show listener info |
| logPrefix | String | โ | '[GlobalEvent]' | Custom log prefix |
| eventTypeFilter | List | โ | null | Event type filter |
| listenerIdFilter | List | โ | null | Listener ID filter |
| customLogger | Function(String)? | โ | null | Custom log handler |
GebLogConfig Presets
| Configuration | Description |
|---|---|
| GebLogConfig.defaultConfig | Default: info level |
| GebLogConfig.debugConfig | Debug: debug level with detailed info |
| GebLogConfig.productionConfig | Production: error level, no sensitive info |
| GebLogConfig.silentConfig | Silent: all logging disabled |
๐ Documentation #
- Getting Started - Installation and basic usage
- API Reference - Complete API documentation
- GebBuilder Widget - Reactive Widget
- Debug Panel - Development debugging tools
- GebListener Mixin - Simplified event subscription
- GebBlocMixin - BLoC integration
- BLoC Integration Guide - Complete integration
- Event Priority - Priority system
- Batch Mode - Performance optimization
๐ Detailed Usage #
Event Priority #
Event priority determines processing order. There are four levels:
enum GebPriority {
low(0), // Low priority - background tasks like logging, statistics
normal(1), // Normal priority - default, general business events
high(2), // High priority - user interactions, UI updates
critical(3); // Critical priority - system-level important events
}
// Usage example
globalEventBus.sendEvent<String>(
type: 'emergency_logout',
data: 'Session expired',
priority: GebPriority.critical,
);
Batch Processing Mode #
Enable batch mode for high-frequency event scenarios:
// Enable batch mode, process events every 50ms
globalEventBus.manager.setBatchMode(true, intervalMs: 50);
// Send many events
for (int i = 0; i < 1000; i++) {
globalEventBus.sendEvent<int>(
type: 'batch_event',
data: i,
);
}
// Disable batch mode
globalEventBus.manager.setBatchMode(false);
Listening Patterns #
One-Time Listening
// Listen once, automatically removed after receiving event
globalEventBus.listenOnce<String>(
listenerId: 'splash_page',
onEvent: (GebEvent<String> event) {
print('App initialization complete');
// Navigate to main page
},
);
Multi-Type Listening
// Listen for multiple event types simultaneously
globalEventBus.listen<String>(
listenerId: 'notification_handler',
eventTypes: ['user_login', 'user_logout', 'message_received'],
onEvent: (GebEvent<String> event) {
switch (event.type) {
case 'user_login':
handleUserLogin(event);
break;
case 'user_logout':
handleUserLogout(event);
break;
case 'message_received':
handleMessage(event);
break;
}
},
);
Conditional Listening
// Conditional event listening
globalEventBus.listen<Map<String, dynamic>>(
listenerId: 'admin_listener',
onEvent: (GebEvent<Map<String, dynamic>> event) {
// Only handle admin-related events
if (event.data['userRole'] == 'admin') {
handleAdminEvent(event);
}
},
);
Logging Configuration #
Preset Configurations
// Development environment - detailed logging
globalEventBus.configureLogging(GebLogConfig.debugConfig);
// Production environment - only errors
globalEventBus.configureLogging(GebLogConfig.productionConfig);
// Disable all logging
globalEventBus.configureLogging(GebLogConfig.silentConfig);
Custom Configuration
// Detailed custom logging configuration
globalEventBus.configureLogging(GebLogConfig(
level: GebLogLevel.info, // Log level
enabled: true, // Enable logging
showTimestamp: true, // Show timestamp
showEventId: false, // Hide event ID
showPriority: true, // Show priority
showEventData: false, // Hide event data (recommended for production)
showListenerInfo: true, // Show listener info
logPrefix: '[MyApp-Events]', // Custom log prefix
eventTypeFilter: ['user_action', 'api_call'], // Only log specific event types
customLogger: (message) { // Custom log handler
// Write to file or send to server
print('Custom: $message');
},
));
Performance Monitoring #
// Get event statistics
final stats = globalEventBus.stats;
print('Total sent: ${stats.totalEventsSent}');
print('Total received: ${stats.totalEventsReceived}');
print('Last event time: ${stats.lastEventTime}');
// Check event type counts
stats.eventTypeCount.forEach((type, count) {
print('Event type $type: $count times');
});
// Get performance info
final performanceInfo = globalEventBus.performanceInfo;
print('Active listeners: ${performanceInfo['listenerCount']}');
print('Batch mode: ${performanceInfo['batchEnabled']}');
Event History #
// Configure history (optional, defaults to enabled with max 100 records)
globalEventBus.configureHistory(GebHistoryConfig(
enabled: true,
maxHistorySize: 500,
));
// Get recent 10 events
final recentEvents = globalEventBus.getRecentEvents(count: 10);
// Get recent 5 events of specific type
final loginEvents = globalEventBus.getRecentEventsByType('user_login', count: 5);
// Get last event of specific type
final lastLogin = globalEventBus.getLastEventByType('user_login');
// Get all recorded event types
final allTypes = globalEventBus.eventTypes;
// Clear history
globalEventBus.clearHistory();
๐ฏ Real-World Use Cases #
1. User State Management
// User login
globalEventBus.sendEvent<UserInfo>(
type: 'user_login',
data: UserInfo(id: '123', name: 'John', email: 'john@example.com'),
priority: GebPriority.high,
);
// Listen for user state changes and update UI immediately after login
globalEventBus.listen<UserInfo>(
listenerId: 'user_profile_page',
onEvent: (GebEvent<UserInfo> event) {
// Update user interface
updateUserProfile(event.data);
},
);
2. Shopping Cart Management
// Add item to cart
globalEventBus.sendEvent<CartItem>(
type: 'cart_add_item',
data: CartItem(
productId: 'p001',
productName: 'iPhone 15',
price: 999.99,
quantity: 1,
),
);
// Listen for cart changes
globalEventBus.listen<CartItem>(
listenerId: 'cart_badge',
eventTypes: ['cart_add_item', 'cart_remove_item', 'cart_update_quantity'],
onEvent: (GebEvent<CartItem> event) {
// Update cart badge
updateCartBadge();
},
);
3. Network Status Monitoring
// Network status changed
globalEventBus.sendEvent<bool>(
type: 'network_status_changed',
data: isConnected,
priority: GebPriority.critical,
);
// Global network status listener
globalEventBus.listen<bool>(
listenerId: 'global_network_monitor',
onEvent: (GebEvent<bool> event) {
if (event.data) {
showSnackBar('Network connected');
} else {
showSnackBar('Network disconnected');
}
},
eventTypes: ['network_status_changed'],
);
4. Theme Switching
// Switch theme
globalEventBus.sendEvent<ThemeMode>(
type: 'theme_changed',
data: ThemeMode.dark,
);
// Listen for theme changes
globalEventBus.listen<ThemeMode>(
listenerId: 'main_app',
onEvent: (GebEvent<ThemeMode> event) {
// Update app theme
updateAppTheme(event.data);
},
eventTypes: ['theme_changed'],
);
๐ง Advanced Features #
GebBuilder Reactive Widget
// Build reactive UI with GebBuilder
class NotificationWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GebBuilder<String>(
eventType: 'notification_received',
useHistoryForInitialData: true,
initialData: 'No notifications',
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (!snapshot.hasData) {
return const CircularProgressIndicator();
}
return Card(
child: ListTile(
title: Text(snapshot.data!),
subtitle: Text('Event ID: ${snapshot.event?.eventId}'),
),
);
},
);
}
}
GebListener Mixin
// Use Mixin for simplified event subscription
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with GebListener {
String _userName = 'Not logged in';
@override
void initState() {
super.initState();
// Subscribe to user login event
gebSubscribe<Map<String, dynamic>>(
listenerId: 'home_user_login',
eventType: 'user_login',
onEvent: (GebEvent<Map<String, dynamic>> event) {
setState(() {
_userName = event.data['name'];
});
},
);
// Subscribe to one-time event
gebSubscribeOnce<void>(
listenerId: 'home_first_load',
eventType: 'app_initialized',
onEvent: (_) {
print('App first load complete');
},
);
}
@override
Widget build(BuildContext context) {
return Text('Welcome, $_userName');
}
// All subscriptions automatically cancelled on dispose
}
BLoC Integration
// Bridge BLoC and event bus using GebBlocBridge
final bridge = GebBlocBridge<CounterEvent, CounterState>(
bloc: counterBloc,
stateEventType: 'counter_state_changed',
);
// Start bridge
bridge.start();
// Forward event bus events to BLoC
bridge.forwardEventToBloc<int>(
eventType: 'external_increment',
mapper: (event) => IncrementEvent(event.data),
);
// Manually publish current state
bridge.publishCurrentState();
// Stop bridge (usually called in dispose)
bridge.dispose();
// Direct integration in BLoC using GebBlocMixin
class CounterBloc extends Bloc<CounterEvent, CounterState> with GebBlocMixin<CounterState> {
CounterBloc() : super(CounterState(0)) {
// Initialize event bus integration
gebInit(
eventBus: globalEventBus,
stateEventType: 'counter_state',
);
}
// ... other BLoC logic ...
@override
Future<void> close() {
gebDispose(); // Clean up resources
return super.close();
}
}
BLoC Event Mapping
// Map events using GebBlocMapper
bridge.forwardEventToBloc<String>(
eventType: 'user_input',
mapper: GebBlocMapper.direct, // Direct mapping
);
// Use custom mapping
bridge.forwardEventToBloc<Map<String, dynamic>>(
eventType: 'complex_event',
mapper: (event) => CustomEvent(
id: event.data['id'],
name: event.data['name'],
),
);
๐งช Testing Support #
// Usage in tests
void main() {
group('Global Event Bus Tests', () {
setUp(() {
// Clean up previous listeners
globalEventBus.removeAllListeners();
});
test('should send and receive events', () async {
String? receivedData;
// Set up listener
final subscription = globalEventBus.listen<String>(
listenerId: 'test_listener',
onEvent: (GebEvent<String> event) {
receivedData = event.data;
},
);
// Send event
globalEventBus.sendEvent<String>(
type: 'test_event',
data: 'test_data',
);
// Wait for event processing
await Future.delayed(const Duration(milliseconds: 10));
// Verify result
expect(receivedData, equals('test_data'));
// Clean up
subscription.cancel();
});
});
}
๐ Performance Tips #
-
Use Event Priority Wisely
- Use
criticalpriority for system-critical events - Use
highpriority for user interaction events - Use
normalpriority for general business events - Use
lowpriority for logging and statistics events
- Use
-
Optimize for High-Frequency Events
- Enable batch processing mode for high-frequency events (scrolling, animations)
- Set reasonable batch interval (recommended 50-200ms)
- Avoid time-consuming operations in event handlers
-
Memory Management
- Use
GebListenerMixin orGebBuilderWidget for automatic subscription lifecycle - Cancel subscriptions in Widget
dispose() - Avoid creating too many listeners
- Use
-
Logging Configuration
- Use
GebLogConfig.productionConfigin production - Use event type filtering to reduce log volume
- Consider using custom log handlers
- Use
-
History Configuration
- Set reasonable maximum history size based on business needs
- Use
GebHistoryConfig.disabledwhen history is not needed
๐ค Contributing #
We welcome contributions in any form! Please see CONTRIBUTING.md for details.
Development Setup
# Clone repository
git clone https://gitee.com/pedro-labs/global_event_bus.git
# Enter project directory
cd global_event_bus
# Install dependencies
flutter pub get
# Run example
cd example && flutter run
# Run tests
flutter test
๐ License #
This project is licensed under the MIT License.
๐ Links #
๐ Class Naming Reference #
For backward compatibility, old class names are preserved via typedef. New projects should use the Geb-prefixed class names.
| Old Name | New Name | Status |
|---|---|---|
| GlobalEvent<T> | GebEvent<T> | @Deprecated |
| BaseGlobalEvent | GebBaseEvent | @Deprecated |
| EventPriority | GebPriority | @Deprecated |
| EventStats | GebStats | @Deprecated |
| GlobalEventLogger | GebLogger | @Deprecated |
| GlobalEventLogConfig | GebLogConfig | @Deprecated |
| EventLogLevel | GebLogLevel | @Deprecated |
| EventHistory | GebHistory | @Deprecated |
| EventHistoryConfig | GebHistoryConfig | @Deprecated |
| EventBusBuilder<T> | GebBuilder<T> | @Deprecated |
| EventBusSnapshot<T> | GebSnapshot<T> | @Deprecated |
| EventBusConnectionState | GebConnectionState | @Deprecated |
| EventBusListener | GebListener | @Deprecated |
| EventBusNoDataListener | GebNoDataListener | @Deprecated |
| EventBusBlocBridge<E,S> | GebBlocBridge<E,S> | @Deprecated |
| EventBusBlocMixin<S> | GebBlocMixin<S> | @Deprecated |
| BlocEventMapper | GebBlocMapper | @Deprecated |