Event DDI Package
A Dart package designed for event management. It provides tools to create, destroy, and listen to events, making it easier to decouple different parts of your application and handle communication efficiently.
Features
-
Event Management: Create, manage, and destroy events effortlessly.
-
Flexible Subscriptions: Support for synchronous, asynchronous, and isolate-based event subscriptions.
-
Replay and Undo: Support for event replay and undo functionalities.
-
Qualifiers: Use qualifiers to distinguish between different event types.
Packages
- Flutter DDI - This package is designed to facilitate the dependency injection process in your Flutter application.
- Dart DDI - This package is a robust and flexible dependency injection mechanism.
Projects
- Budgetopia - An intuitive personal finance app that helps users track expenses.
Creating and Managing Events
The Events follow a straightforward flow. Functions or methods subscribe
to specific events using the subscribe method of the DDIEvent
class. Events are fired using the fire
or fireWait
methods, triggering the execution of all subscribed callbacks. Subscribed callbacks are then executed, handling the event data and performing any specified tasks. Subscriptions can be removed using the unsubscribe
function.
Subscribing an Event
When subscribing to an event, you have the option to choose from three different types of subscriptions:
DDIEvent.instance.subscribe
It's the common type, working as a simples callback.DDIEvent.instance.subscribeAsync
Runs the callback as a Future.DDIEvent.instance.subscribeIsolate
Runs as a Isolate.
Subscribe
The common subscription type, subscribe
, functions as a simple callback. It allows you to respond to events in a synchronous manner, making it suitable for most scenarios.
Obs: If you register an event that uses async and await, it Won't be possible to wait even using fireWait
. For this scenario, use subscribeAsync
.
Parameters:
event:
The callback function to be executed when the event is fired.qualifier:
Optional qualifier name to distinguish between different events of the same type.canRegister:
A FutureOrcanUnsubscribe:
Indicates if the event can be unsubscribe. Ignored ifautoRun
is used.priority:
Priority of the subscription relative to other subscriptions (lower values indicate higher priority). Ignored ifautoRun
is used.unsubscribeAfterFire:
If true, the subscription will be automatically removed after the first time the event is fired. Ignored ifautoRun
is used.lock
: Indicates if the event should be locked. Running only one event simultaneously. Cannot be used in combination withautoRun
.onError
: The callback function to be executed when an error occurs.onComplete
: The callback function to be executed when the event is completed. It's called even if an error occurs.expirationDuration
: The duration after which the subscription will be automatically removed.retryInterval
: Adds the ability to automatically retry the event after the interval specified.defaultValue
: The default value to be used when the event is fired. Required ifretryInterval
is used.maxRetry
: The maximum number of times the subscription will be automatically fired ifretryInterval
is used.- Can be used in combination with
autoRun
andonError
. - If
maxRetry
is 0 andautoRun
is true, will run forever. - If
maxRetry
is greater than 0 andautoRun
is true, the subscription will be removed when the maximum number of retries is reached. - If
maxRetry
is greater than 0,autoRun
is false andonError
is used, the subscription will stop retrying when the maximum number is reached. - If
expirationDuration
is used, the subscription will be removed when the first rule is met, either when the expiration duration is reached or when the maximum number of retries is reached.
- Can be used in combination with
autoRun
: If true, the event will run automatically when the subscription is created.- Only one event is allowed.
canUnsubscribe
is ignored.unsubscribeAfterFire
is ignored.priority
is ignored.- Cannot be used in combination with
lock
. - Requires the
defaultValue
parameter. - If
maxRetry
is 0, will run forever.
filter
: Allows you to filter events based on their value. Only events when the filter returns true will be fired.
void myEvent(String message) {
print('Event received: $message');
}
DDIEvent.instance.subscribe<String>(
myEvent,
qualifier: 'exampleEvent',
canRegister: () => true,
canUnsubscribe: true,
priority: 0
unsubscribeAfterFire: false,
lock: false,
onError: (Object? error, StackTrace stacktrace, String valor){},
onComplete: (){},
expirationDuration: const Duration(seconds: 5),
retryInterval: const Duration(seconds: 4),
defaultValue: 'defaultValue',
maxRetry: 1,
autoRun: false,
filter: (value) => true,
);
Subscribe Async
The subscribeAsync
type runs the callback as a Future, allowing for asynchronous event handling. Making it suitable for scenarios where asynchronous execution is needed without waiting for completion.
Note that it not be possible to await this type of subscription.
Obs: If you want to await for the event to be completed, fire it using fireWait
.
Parameters are the same as for subscribe
.
void myEvent(String message) {
print('Event received: $message');
}
DDIEvent.instance.subscribeAsync<String>(
myEvent,
qualifier: 'exampleEvent',
canRegister: () => true,
canUnsubscribe: true,
unsubscribeAfterFire: false,
lock: false,
onError: (Object? error, StackTrace stacktrace, String valor){},
onComplete: (){},
expirationDuration: const Duration(seconds: 5),
retryInterval: const Duration(seconds: 4),
defaultValue: 'defaultValue',
maxRetry: 1,
autoRun: false,
filter: (value) => true,
);
Subscribe Isolate
The subscribeIsolate
type runs the callback in a separate isolate, enabling concurrent event handling. This is particularly useful for scenarios where you want to execute the event in isolation, avoiding potential interference with the main application flow.
Parameters are the same as for subscribe
.
void myEvent(String message) {
print('Event received: $message');
}
DDIEvent.instance.subscribeIsolate<String>(
myEvent,
qualifier: 'exampleEvent',
canRegister: () => true,
canUnsubscribe: true,
unsubscribeAfterFire: false,
lock: false,
onError: (Object? error, StackTrace stacktrace, String valor){},
onComplete: (){},
expirationDuration: const Duration(seconds: 5),
retryInterval: const Duration(seconds: 4),
defaultValue: 'defaultValue',
maxRetry: 1,
autoRun: false,
filter: (value) => true,
);
Unsubscribing an Event
To unsubscribe from an event, use the unsubscribe
function:
DDIEvent.instance.unsubscribe<String>(
myEvent,
qualifier: 'exampleEvent',
);
Firing an Event
To fire an event, use the fire
or fireWait
function. Using fireWait
makes it possible to wait for all events to complete.
qualifier:
Optional qualifier name to distinguish between different events of the same type.canReplay:
A boolean that indicates if the value canundo
. The max history allowed is 5 events.
DDIEvent.instance.fire('Hello, Dart DDI!', qualifier: 'exampleEvent', canReplay: false);
await DDIEvent.instance.fireWait('Hello, Dart DDI!', qualifier: 'exampleEvent', canReplay: true);
Undo an Event
The undo
method reverts the last fired event if it was marked with canReplay: true. The max history allowed is 5 events.
DDIEvent.instance.undo<EventType>(qualifier: 'exampleEvent');
Redo an Event
The redo
method re-executes the last undone
event if it exists. This allows users to redo actions that were previously undone.
- Requires to call
undo
first. - After
fire
orfireWait
, theredo
history is cleared.
DDIEvent.instance.redo<EventType>(qualifier: 'exampleEvent');
Clear Event History
The clearHistory
method clears the entire history of fired events, removing the ability to undo
or redo
any prior events.
DDIEvent.instance.clearHistory<EventType>(qualifier: 'exampleEvent');
Get Current Value
The getValue
method retrieves the current value of the last event fired. This can be helpful for accessing the state of the last event without firing it again.`
final EventType value = DDIEvent.instance.getValue<EventType>(qualifier: 'exampleEvent');
DDIEvent Sender Mixin
The DDIEventSender
mixin was designed to simplify the process of sending events values to listeners. They provide the method fire
to send the specified value to an event.
Example Usage:
class MyEvent with DDIEventSender<String> {
void businessLogic() {
fire('Hello World');
}
}
Events Considerations
When using the Event System, consider the following:
Event Granularity
: Design events with appropriate granularity to ensure they represent meaningful actions or states in the application.
Modularity
: Keep events and their handlers modular and self-contained.
Single Responsibility
: Ensure each event and its handler have a single responsibility.
Possible Problems
: Be cautious of potential issues such as race conditions and excessive use of isolate-based event handling, which may impact performance.
Unnecessary Locking
: Applying locks to events unnecessarily may hinder the application's responsiveness. Use locking only when essential to prevent conflicting event executions.
Event Looping
: Carefully manage scenarios where events trigger further events, as this can lead to infinite loops or excessive event cascades.
See the considerations about Qualifiers.
Use Cases
Application Lifecycle
: Manage events related to the application's lifecycle.
Data Synchronization
: Handle data synchronization events between local and remote data sources.
Background Task
: Coordinate background tasks and events for efficient task execution.
Custom Event Bus
: Build a custom event bus for inter-component communication, allowing different parts of the application to communicate without tight coupling.
Notifications
: Implement notifications for updates in various parts of the application, such as new messages, alerts, or data changes.