https://github.com/hamed-rezaee/bloc_dependency_manager
https://github.com/hamed-rezaee/bloc_dependency_manager
Last synced: 3 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/hamed-rezaee/bloc_dependency_manager
- Owner: hamed-rezaee
- License: mit
- Created: 2024-10-19T10:22:26.000Z (12 months ago)
- Default Branch: develop
- Last Pushed: 2024-12-19T12:27:09.000Z (10 months ago)
- Last Synced: 2025-04-09T13:11:11.472Z (6 months ago)
- Language: Dart
- Size: 45.9 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Bloc Dependency Manager
**Bloc Dependency Manager** is a centralized dependency management package designed to simplify the handling, registration, and lifecycle management of Blocs/Cubits in Dart and Flutter applications. It provides an organized way to manage BLoC dependencies, add custom state listeners, and handle event dispatching through a core state management system.
This package aims to help developers avoid repetitive setup and cleanup tasks, minimize dependency conflicts, and create scalable and maintainable architectures in Flutter applications.
## How It Works
The **Bloc Dependency Manager** package manages Blocs and their state communications through a central `BlocManager`, using several key classes and design patterns to simplify dependency handling, state changes, and event-driven communication.
### Architecture Overview
1. **`BlocManager`** - A singleton class that provides centralized BLoC registration, and listener management.
2. **`StateDispatcher`** - Manages and dispatches state emitters, which are used to handle and distribute BLoC state changes.
3. **`BaseStateEmitter`** - An abstract base class for creating custom state emitters that define how different states should be handled for specific listeners.
4. **`BaseStateListener`** - An interface to define actions for listeners to respond to BLoC state changes.### Core Design Patterns
- **Singleton Pattern**: Ensures only one `BlocManager` instance exists across the app.
- **Dependency Injection**: Blocs are registered with `BlocManager` and can be lazily or eagerly instantiated.
- **Observer Pattern**: `BlocManager` notifies listeners of state changes, allowing them to respond independently.
- **Strategy Pattern**: `StateEmitters` handle different strategies for managing state changes.## Key Features
- **Centralized BLoC Management:** Register and manage all Blocs through a single BlocManager instance.
- **State Emission and Custom Listeners:** Attach state listeners and create custom emitters to manage and track state changes across Blocs.
- **Automated Resource Disposal:** Dispose of Blocs and related listeners automatically to prevent memory leaks.
- **Seamless Integration with BLoC Library:** Works smoothly with the bloc package to manage state across applications.## How It Works
The **Bloc Dependency Manager** operates through several primary components:
1. **BlocManager:** A singleton class that manages the lifecycle of Blocs, storing them in a repository and handling their registration and disposal.
2. **BaseStateEmitter:** A base class for custom state emitters, which trigger specific actions when a BLoC's state changes.
3. **StateDispatcher:** Registers and manages custom emitters, allowing multiple state listeners to react to changes in the central BlocManager.
4. **BaseStateListener:** An abstract class for defining behaviors for state listeners, allowing you to implement custom response methods to BLoC state changes.## Getting Started
Here's a complete walkthrough to demonstrate how to set up and use the **Bloc Dependency Manager**.
### 1. Create a BLoC
Define a BLoC (`CounterBloc`) that will emit states based on user actions:
```dart
enum CounterState { increment, decrement, reset }class CounterBloc extends Cubit {
CounterBloc() : super(CounterState.reset);void reset() {
emit(CounterState.reset);
}void increment() {
emit(CounterState.increment);
}void decrement() {
emit(CounterState.decrement);
}
}
```In this example, the `CounterBloc` emits `CounterState.increment`, `CounterState.decrement`, or `CounterState.reset` based on the method called.
### 2. Define a State Listener Interface
Create a listener interface for responding to changes in the `CounterBloc` state.
```dart
abstract class CounterStateListener extends BaseStateListener {
void onCounterStateReset();
void onCounterStateChange(CounterState state);
}
```Here, `CounterStateListener` defines the methods that will be triggered when specific states are emitted by the `CounterBloc`.
### 3. Implement a Custom State Emitter
Use a `CounterStateEmitter` to broadcast state changes to listeners:
```dart
class CounterStateEmitter
extends BaseStateEmitter {
CounterStateEmitter(super.blocManager);@override
void handleStates({
required CounterStateListener stateListener,
required Object? state,
}) =>
switch (state) {
CounterState.reset => stateListener.onCounterStateReset(),
CounterState.increment =>
stateListener.onCounterStateChange(CounterState.increment),
CounterState.decrement =>
stateListener.onCounterStateChange(CounterState.decrement),
_ => throw UnimplementedError(),
};
}
```The `CounterStateEmitter` listens to `CounterBloc` changes and invokes methods in `CounterStateListener` depending on the current state of `CounterBloc`.
### 4. Create a Logger Bloc for Tracking Changes
Implement a `LoggerBloc` that listens to `CounterStateListener` and logs state changes.
```dart
class LoggerBloc extends Cubit implements CounterStateListener {
LoggerBloc() : super('');@override
void onCounterStateReset() {
emit('Counter state has been reset.');
}@override
void onCounterStateChange(CounterState counterState) {
emit('Counter state changed to $counterState.');
}
}
```In `LoggerBloc`, the state is emitted as a log message whenever the counter state changes.
### 5. Set up and Use BlocManager
Register `LoggerBloc`, `CounterBloc`, and the custom `CounterStateEmitter` in `BlocManager` and dispatch some events to test the setup.
```dart
Future main() async {
// Register all the blocs.
BlocManager().register(LoggerBloc());
BlocManager().register(CounterBloc());// Register the state emitter for the [CounterBloc].
StateDispatcher(BlocManager()).register(
(BaseBlocManager blocManager) =>
CounterStateEmitter(blocManager as BlocManager),
);// Resolve the [LoggerBloc] and listen to its state changes.
BlocManager().resolve().stream.listen(print);// Resolve the [CounterBloc] and dispatch some events.
BlocManager().resolve().decrement();
await Future.delayed(const Duration(seconds: 1));
BlocManager().resolve().increment();
await Future.delayed(const Duration(seconds: 1));
BlocManager().resolve().reset();
await Future.delayed(const Duration(seconds: 1));// Dispose [BlocManager] to clean up resources.
await BlocManager().dispose();
await BlocManager().dispose();print('All blocs disposed.');
}
```### Explanation of the Flow
1. **Bloc Registration:** Both `CounterBloc` and `LoggerBloc` are registered with `BlocManager` for centralized access.
2. **State Emitter Registration:** The `CounterStateEmitter` is registered to handle state emissions for `CounterBloc` by using `StateDispatcher`.
3. **Event Dispatch:** Actions are dispatched to `CounterBloc` (increment, decrement, reset), causing state changes.
4. **State Listening:** `LoggerBloc`, registered as a `CounterStateListener`, responds to each state change and logs the output.
5. **Cleanup:** Finally, `BlocManager().dispose()` is called to clean up all registered blocs and listeners.## API Reference
### BlocManager
The main singleton class for managing the lifecycle of Blocs and providing centralized access to registered instances.
#### Methods
- **`register()`**
- Registers a BLoC instance of type `B`.
- **Parameters**:
- `B bloc`: The BLoC instance to register.
- `String key` (optional): The identifier for the BLoC; defaults to `defaultKey`.
- **Returns**: The registered BLoC instance.- **`resolve()`**
- Retrieves a registered BLoC by its type and optional key.
- **Parameters**:
- `String key` (optional): Identifier for the BLoC to resolve; defaults to `defaultKey`.
- **Returns**: The resolved BLoC instance.
- **Throws**: `BlocManagerException` if the BLoC is not registered.- **`isBlocRegistered()`**
- Checks if a BLoC of a certain type and key is registered.
- **Parameters**:
- `String key`: The identifier for the BLoC.
- **Returns**: `true` if the BLoC is registered, `false` otherwise.- **`addListener()`**
- Adds a listener to a registered BLoC to listen for state changes.
- **Parameters**:
- `String listenerKey`: Identifier for the listener.
- `BlocManagerListenerHandler handler`: Callback to execute when the BLoC's state changes.
- `String key` (optional): Identifier for the BLoC; defaults to `defaultKey`.- **`hasListener()`**
- Checks if a listener with a specific key exists for a registered BLoC.
- **Parameters**:
- `String key`: The identifier for the listener.
- **Returns**: `true` if the listener exists, `false` otherwise.- **`removeListener()`**
- Removes a listener associated with a specific BLoC and key.
- **Parameters**:
- `String key` (optional): Identifier for the listener; defaults to `defaultKey`.
- **Returns**: A `Future` that completes when the listener is removed.- **`registerStateEmitter()`**
- Registers a custom state emitter that listens for and processes specific state changes.
- **Parameters**:
- `GenericStateEmitter stateEmitter`: The state emitter to register.- **`emitCoreStates()`**
- Dispatches states to all registered state emitters for a specific BLoC.
- **Parameters**:
- `GenericBloc bloc`: The BLoC for which the state is emitted.
- `Object? state`: The state to emit to listeners.- **`dispose()`**
- Disposes a registered BLoC and removes any associated listeners.
- **Parameters**:
- `String key` (optional): Identifier for the BLoC to dispose; defaults to `defaultKey`.
- **Returns**: A `Future` that completes when the BLoC is disposed.### StateDispatcher
The `StateDispatcher` class is a helper that registers state emitters for specified Blocs, allowing listeners to be triggered on BLoC state changes.
#### Methods
- **`register()`**
- Registers a state emitter of type `E` for a BLoC of type `B`, enabling state emission to be managed centrally.
- **Parameters**:
- `StateEmitterBuilder stateEmitterBuilder`: A builder function to initialize the state emitter with the `BlocManager`.### BaseStateEmitter
The `BaseStateEmitter` is an abstract class for creating custom state emitters that broadcast state changes to listeners. It’s designed to be extended to implement specific behaviors based on BLoC state changes.
#### Methods
- **`handleStates()`**
- Defines how states are handled for a specific state listener.
- **Parameters**:
- `BaseStateListener stateListener`: The listener that will respond to state changes.
- `Object state`: The state being emitted by the BLoC.- **`call()`**
- Triggers the emission of the current state to the listener, with optional handling for a custom state.
- **Parameters**:
- `BaseStateListener stateListener`: The listener to receive the state.
- `Object? state`: The state to emit. Defaults to the BLoC’s current state if not provided.### BaseStateListener
An abstract class for creating listener interfaces to respond to specific state changes in Blocs. Implementing classes define the actions that occur in response to BLoC state changes.
#### Methods
- Implement custom methods in classes that extend `BaseStateListener` to define responses to specific states (e.g., `onCounterStateReset`, `onCounterStateChange` in a `CounterStateListener` implementation).
## Contributing
Contributions to the **Bloc Dependency Manager** package are welcome.
Feel free to submit issues, feature requests, or pull requests to help improve the package and make it more useful for the Flutter community.