mono_bloc library
Code generation library for simplified BLoC pattern with event methods, automatic error handling, concurrency control, and side-effect management.
Basic Usage
import 'package:mono_bloc/mono_bloc.dart';
part 'counter_bloc.g.dart';
@MonoBloc()
class CounterBloc extends _$CounterBloc<int> {
CounterBloc() : super(0);
@event
int _onIncrement() => state + 1;
@event
int _onDecrement() => state - 1;
@event
int _onAdd(int value) => state + value;
}
// Usage
final bloc = CounterBloc();
bloc.increment();
bloc.decrement();
bloc.add(5);
Event Methods (@event)
Event methods handle business logic and state transitions. MonoBloc generates event classes and public dispatch methods automatically.
Return Types
1. Synchronous State Return
@event
int _onIncrement() => state + 1;
@event
TodoState _onAddTodo(String title) {
return state.copyWith(todos: [...state.todos, Todo(title: title)]);
}
2. Asynchronous State Return
@event
Future<TodoState> _onLoadTodos() async {
final todos = await repository.fetchTodos();
return state.copyWith(todos: todos);
}
3. Stream State Return
@event
Stream<SearchState> _onSearch(String query) async* {
yield state.copyWith(loading: true);
final results = await api.search(query);
yield state.copyWith(loading: false, results: results);
}
4. Void with Emitter (Multiple Emissions)
@event
void _onLoadData(_Emitter emit) {
emit(state.copyWith(loading: true));
emit(state.copyWith(loading: false));
}
5. Future<void> with Emitter (Async Multiple Emissions)
@event
Future<void> _onLoadData(_Emitter emit) async {
emit(state.copyWith(loading: true));
final data = await api.fetch();
emit(state.copyWith(loading: false, data: data));
}
Method Parameters
Positional Parameters:
@event
State _onAdd(int value) => state.copyWith(count: state.count + value);
// Generated: bloc.add(5)
Named Parameters:
@event
State _onUpdate({required String name, int? age}) {
return state.copyWith(name: name, age: age);
}
// Generated: bloc.update(name: 'John', age: 30)
Mixed Parameters:
@event
State _onAddTodo(
String title,
String description, {
required TodoPriority priority,
List<String> tags = const [],
DateTime? dueDate,
}) {
return state.copyWith(
todos: [...state.todos, Todo(
title: title,
description: description,
priority: priority,
tags: tags,
dueDate: dueDate,
)],
);
}
// Generated: bloc.addTodo('Title', 'Desc', priority: TodoPriority.high)
Complex Types (Preserved):
// Typedefs preserved
typedef Callback = void Function(String result);
@event
State _onProcess(Callback onComplete) { ... }
// Records preserved
@event
State _onUpdateCoords((double lat, double lng) location) { ... }
// Generics preserved
@event
Future<Map<String, List<Todo>>> _onGroupByTag() async { ... }
Event Naming
Private Events (Recommended):
@event
State _onIncrement() => ...; // → bloc.increment()
@event
State _onAddItem() => ...; // → bloc.addItem()
@event
State _loadData() => ...; // → bloc.loadData()
Public Events (Base Classes):
@event
@protected
State onReset() => ...; // → Must start with 'on' and be @protected
Concurrency Control
Control how multiple events are processed using four concurrency modes.
Sequential Mode (Bloc-Level)
All events without explicit concurrency are queued and processed one at a time:
@MonoBloc(sequential: true)
class BankBloc extends _$BankBloc<BankState> {
BankBloc() : super(const BankState(balance: 0));
@event
Future<BankState> _onDeposit(double amount) async {
// Queued sequentially
return state.copyWith(balance: state.balance + amount);
}
@event
Future<BankState> _onWithdraw(double amount) async {
// Queued sequentially after deposit completes
return state.copyWith(balance: state.balance - amount);
}
@restartableEvent // Bypasses sequential queue
Future<BankState> _onRefresh() async {
// Runs independently with restartable behavior
final balance = await api.fetchBalance();
return state.copyWith(balance: balance);
}
}
Default Concurrency (Bloc-Level)
Set a default concurrency mode for all events:
@MonoBloc(concurrency: MonoConcurrency.restartable)
class SearchBloc extends _$SearchBloc<SearchState> {
@event
Future<SearchState> _onSearch(String query) async {
// Uses restartable (from bloc default)
return await api.search(query);
}
@droppableEvent // Override: uses droppable
Future<SearchState> _onLoadMore() async {
return await api.loadMore();
}
}
Event-Level Concurrency
1. Sequential - Queue and Process in Order
@sequentialEvent // or @MonoEvent(MonoConcurrency.sequential)
Future<State> _onProcess() async {
// Events queued, processed one at a time
return await processData();
}
2. Concurrent - Process in Parallel (Default)
@concurrentEvent // or @event
Future<State> _onLoadUsers() async {
// Multiple events run in parallel
return await api.fetchUsers();
}
3. Restartable - Cancel Previous, Start New
@restartableEvent // or @MonoEvent(MonoConcurrency.restartable)
Future<SearchState> _onSearch(String query) async {
// Previous search cancelled when user types again
final results = await api.search(query);
return state.copyWith(results: results);
}
4. Droppable - Ignore New While Processing
@droppableEvent // or @MonoEvent(MonoConcurrency.droppable)
Future<FormState> _onSubmit() async {
// Additional clicks ignored until this completes
await api.submit(state.data);
return state.copyWith(submitted: true);
}
Comparison
| Mode | New Event While Processing | Use Case |
|---|---|---|
| Sequential | Queued | Database transactions, state machines |
| Concurrent | Runs in parallel | Independent operations |
| Restartable | Cancels current, starts new | Search, filters |
| Droppable | Ignored | Rate limiting, prevent double-submit |
Async State Management (@AsyncMonoBloc)
Wraps state in MonoAsyncValue<T> for automatic loading/error/data handling.
Basic Usage
@AsyncMonoBloc()
class UserBloc extends _$UserBloc<User> {
UserBloc() : super(const MonoAsyncValue.loading());
@event
Future<User> _onLoadUser(String id) async {
return await api.fetchUser(id); // Auto-wrapped
}
}
Return Types
1. Return T Directly (Automatic Wrapping)
@event
Future<User> _onLoadUser() async {
return await api.fetchUser();
// Wrapped in MonoAsyncValue.withData(user)
}
@event
List<Todo> _onFilterCompleted() {
return state.dataOrNull?.where((t) => t.completed).toList() ?? [];
// Wrapped immediately
}
2. Return MonoAsyncValue<T> (Manual Control)
@event
Future<MonoAsyncValue<User>> _onLoadUser() async {
return MonoAsyncValue.withData(await api.fetchUser());
}
3. Return Stream<T> (Streaming Data)
@event
Stream<List<Todo>> _onWatchTodos() {
return repository.watchTodos();
// Each item wrapped in MonoAsyncValue.withData()
}
4. Return Stream<MonoAsyncValue<T>> (Full Control)
@event
Stream<MonoAsyncValue<List<Todo>>> _onSearchProgressive(String query) async* {
yield loading();
for (final source in sources) {
final results = await source.search(query);
yield withData(results);
}
}
5. Use Emitter (Multiple States)
@event
Future<void> _onLoadUser(_Emitter emit) async {
emit(loading()); // MonoAsyncValue.loading()
try {
final user = await api.fetchUser();
emit(withData(user)); // MonoAsyncValue.withData(user)
} catch (e, stack) {
emit(withError(e, stack));
}
}
Helper Methods (Available in Event Handlers)
loading() - Loading state with current data preserved:
emit(loading()); // MonoAsyncValue(data: currentData, isLoading: true)
loadingClearData() - Loading state without data:
emit(loadingClearData()); // MonoAsyncValue(data: null, isLoading: true)
withData(T) - Success state:
emit(withData(user)); // MonoAsyncValue.withData(user)
withError(error, stack, [data]) - Error state:
emit(withError(error, stack, state.dataOrNull));
Accessing Current Data
state.dataOrNull // T? - null if loading/error
state.data // T - throws if not data state
state.hasData // bool - true if data available
state.isLoading // bool - true if loading
state.hasError // bool - true if error
state.error // Object - error if available
state.errorOrNull // Object? - null if no error
UI Pattern Matching
BlocBuilder<UserBloc, MonoAsyncValue<User>>(
builder: (context, state) {
return state.when(
loading: () => CircularProgressIndicator(),
data: (user) => UserProfile(user: user),
error: (error, stack) => ErrorWidget(error: error),
);
},
)
With Concurrency
@AsyncMonoBloc(MonoConcurrency.restartable)
class SearchBloc extends _$SearchBloc<List<Result>> {
@event // Uses restartable from bloc default
Future<List<Result>> _onSearch(String query) async {
return await api.search(query);
}
}
Error Handling (@onError)
Errors are automatically caught and passed to error handlers with filtered stack traces combining dispatch location and error location.
Handler Priority
- Event-specific handler:
_onError<EventName> - General handler:
_onError - Default behavior: rethrow
Method Signatures
1. Error and Stack Trace
@onError
State _onError(Object error, StackTrace stack) {
return state.copyWith(error: error.toString());
}
2. Error Only
@onError
State _onError(Object error) {
return state.copyWith(error: error.toString());
}
3. With Event Reference
@onError
State _onError(Object error, StackTrace stack, _Event event) {
print('Error in: ${event.runtimeType}');
return state.copyWith(error: error.toString());
}
4. With Emitter
@onError
void _onError(Object error, _Emitter emit) {
emit(state.copyWith(error: error.toString()));
}
5. Return void (No State Change)
@onError
void _onError(Object error, StackTrace stack) {
logger.error('Event failed', error, stack);
// No state emission
}
6. Return Nullable State (Optional Recovery)
@onError
State? _onError(Object error) {
if (error is NetworkException) {
return state.copyWith(offline: true);
}
return null; // Don't emit for other errors
}
Event-Specific Handlers
@MonoBloc()
class PaymentBloc extends _$PaymentBloc<PaymentState> {
@event
Future<PaymentState> _onProcessPayment() async {
return await paymentService.process();
}
@event
Future<PaymentState> _onRefund(String id) async {
return await paymentService.refund(id);
}
// Handles only processPayment errors
@onError
PaymentState _onErrorProcessPayment(Object error, StackTrace stack) {
if (error is InsufficientFundsException) {
return PaymentState.failed('Insufficient funds');
}
return PaymentState.failed('Payment failed');
}
// Handles only refund errors
@onError
PaymentState _onErrorRefund(Object error) {
return PaymentState.failed('Refund failed');
}
// General fallback for other events
@onError
PaymentState _onError(Object error, StackTrace stack) {
return state.copyWith(error: error.toString());
}
}
With AsyncMonoBloc
@AsyncMonoBloc()
class UserBloc extends _$UserBloc<User> {
@event
Future<User> _onLoadUser(String id) async {
return await api.fetchUser(id);
}
@onError
MonoAsyncValue<User> _onError(Object error, StackTrace stack) {
// Keep current data, add error
return withError(error, stack, state.dataOrNull);
}
}
Stack Trace Filtering
MonoBloc automatically filters stack traces to remove:
- Dart core frames (
dart:async,dart:core) - Framework packages (
package:bloc,package:flutter) - MonoBloc internals (
package:mono_bloc) - Generated code (
.g.dart)
The filtered trace combines:
- Where the event was dispatched (call site)
- Where the error occurred (implementation)
Actions (@MonoActions)
Actions represent side effects that don't modify state. They're dispatched
through a separate actions stream for handling in the UI layer.
Requirements
Actions must be defined in a private mixin annotated with @MonoActions():
- Create a private mixin (name starts with
_) with@MonoActions()annotation - Define abstract void methods for each action
- The generated
_$Blocbase class automatically includes the actions mixin
// Define actions in a private mixin with @MonoActions()
@MonoActions()
mixin _MyBlocActions {
void myAction();
}
// The generated _$MyBloc includes _MyBlocActions automatically
@MonoBloc()
class MyBloc extends _$MyBloc<MyState> {
MyBloc() : super(initialState);
}
Action Parameters
No Parameters:
void navigateToHome();
Positional Parameters:
void showDialog(String title, String message);
Named Parameters:
void showNotification({
required String message,
required NotificationType type,
});
Mixed Parameters:
void trackEvent(
String eventName,
Map<String, dynamic> properties, {
required String category,
int? value,
});
Complex Types (Type Preservation):
// Typedef preserved
typedef OnComplete = void Function(bool success);
void showConfirmDialog({
required String message,
required OnComplete onComplete,
});
// Record type preserved
void updateLocation((double lat, double lng) coords);
Pure Dart Usage
Listen to actions stream directly:
// Define actions in a private mixin
@MonoActions()
mixin _LoggingBlocActions {
void logEvent(String eventName, Map<String, dynamic> params);
void trackPageView(String pageName);
}
// The generated base class includes the actions mixin automatically
@MonoBloc()
class LoggingBloc extends _$LoggingBloc<int> {
LoggingBloc() : super(0);
@event
int _onIncrement() {
logEvent('counter_incremented', {'value': state + 1});
return state + 1;
}
}
// Listen to actions using pattern matching
void main() {
final bloc = LoggingBloc();
bloc.actions.listen((action) {
switch (action) {
case _LogEventAction(:final eventName, :final params):
analytics.logEvent(eventName, parameters: params);
case _TrackPageViewAction(:final pageName):
analytics.logPageView(pageName);
}
});
bloc.increment(); // Triggers logEvent action
}
Flutter Usage (requires mono_bloc_flutter)
Use MonoBlocActionListener widget:
// Define actions in a private mixin
@MonoActions()
mixin _CartBlocActions {
void showNotification(String message, NotificationType type);
void navigateToCheckout();
void vibrate();
}
// The generated base class includes the actions mixin automatically
@MonoBloc()
class CartBloc extends _$CartBloc<CartState> {
CartBloc() : super(const CartState());
@event
CartState _onAddItem(CartItem item) {
showNotification('Added ${item.name}', NotificationType.success);
vibrate();
return state.copyWith(items: [...state.items, item]);
}
}
// In Flutter UI:
MonoBlocActionListener<CartBloc>(
actions: CartBlocActions.when(
showNotification: (context, message, type) {
final color = type == NotificationType.success
? Colors.green
: Colors.red;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: color),
);
},
navigateToCheckout: (context) {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => CheckoutPage()),
);
},
vibrate: (context) {
HapticFeedback.mediumImpact();
},
),
child: CartPage(),
)
Using of() with Interface Implementation
For cleaner action handling, implement the generated actions interface:
class CartPage extends StatelessWidget implements CartBlocActions {
const CartPage({super.key});
@override
void showNotification(BuildContext context, String message, NotificationType type) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
}
@override
void navigateToCheckout(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CheckoutPage()));
}
@override
void vibrate(BuildContext context) {
HapticFeedback.mediumImpact();
}
@override
Widget build(BuildContext context) {
return MonoBlocActionListener<CartBloc>(
actions: CartBlocActions.of(this), // Wire this instance as handler
child: CartContent(),
);
}
}
When to use which pattern:
when()- Inline callbacks, good for simple pages with few actionsof()- Interface implementation, better for complex pages or reusable handlers
Generated Code
MonoBloc generates:
- Sealed action hierarchy for type safety
- Action stream in base class
- Implementation class with action methods
- Pattern matching with
when()andof()
Use Cases
- Navigation between screens
- Showing dialogs, snackbars, bottom sheets
- Playing sounds, vibrations
- Logging and analytics
- Calling platform services (camera, file picker)
- External API calls that don't affect state
Event Queues
Group related events into named queues with independent transformers. Events in different queues can run in parallel.
Basic Queue Setup
@MonoBloc()
class FileTransferBloc extends _$FileTransferBloc<FileState> {
static const uploadQueue = 'upload';
static const downloadQueue = 'download';
static const syncQueue = 'sync';
FileTransferBloc() : super(
const FileState(),
queues: {
uploadQueue: MonoEventTransformer.sequential,
downloadQueue: MonoEventTransformer.sequential,
syncQueue: MonoEventTransformer.droppable,
},
);
// Upload queue (sequential)
@MonoEvent.queue(uploadQueue)
Future<FileState> _onUpload(File file) async {
await uploadApi.upload(file);
return state.copyWith(uploaded: [...state.uploaded, file]);
}
@MonoEvent.queue(uploadQueue)
Future<FileState> _onCancelUpload(String id) async {
await uploadApi.cancel(id);
return state.copyWith(
uploaded: state.uploaded.where((f) => f.id != id).toList(),
);
}
// Download queue (sequential, runs parallel to upload queue)
@MonoEvent.queue(downloadQueue)
Future<FileState> _onDownload(String url) async {
final file = await downloadApi.download(url);
return state.copyWith(downloaded: [...state.downloaded, file]);
}
// Sync queue (droppable)
@MonoEvent.queue(syncQueue)
Future<FileState> _onSync() async {
await api.syncAll();
return await _getCurrentState();
}
// No queue: Independent event
@restartableEvent
Stream<FileState> _onRefresh() async* {
yield state.copyWith(refreshing: true);
final data = await api.fetchAll();
yield state.copyWith(refreshing: false, data: data);
}
}
Queue Transformers
Each queue can have its own transformer:
static const queue1 = 'queue1';
static const queue2 = 'queue2';
static const queue3 = 'queue3';
static const queue4 = 'queue4';
queues: {
queue1: MonoEventTransformer.sequential, // Process one at a time
queue2: MonoEventTransformer.concurrent, // Process in parallel
queue3: MonoEventTransformer.restartable, // Cancel previous
queue4: MonoEventTransformer.droppable, // Ignore new
}
Use Cases
- Separate upload/download queues
- Priority queues (high/low priority events)
- Transaction groups (read vs write operations)
- Resource-specific queues (network vs database)
Event Interception (@onEvent)
Intercept and filter events before they're processed. Return false to block.
Basic Interceptor
@MonoBloc()
class TodoBloc extends _$TodoBloc<TodoState> {
@event
Future<TodoState> _onLoadTodos() async { ... }
@onEvent
bool _onEvents(_Event event) {
if (state.isLoading) {
return false; // Block all events while loading
}
return true; // Allow event
}
}
Method Signatures
Event Only:
@onEvent
bool _onEvents(_Event event) {
print('Event: ${event.runtimeType}');
return true;
}
With Emitter:
@onEvent
bool _onEvents(_Event event, _Emitter emit) {
if (event is _LoadEvent) {
emit(state.copyWith(loading: true));
}
return true;
}
Emitter Only:
@onEvent
bool _onEvents(_Emitter emit) {
emit(state.copyWith(eventCount: state.eventCount + 1));
return true;
}
Use Cases
Rate Limiting:
DateTime? _lastRequestTime;
@onEvent
bool _onEvents(_Event event) {
if (event is _FetchDataEvent) {
final now = DateTime.now();
if (_lastRequestTime != null &&
now.difference(_lastRequestTime!) < Duration(seconds: 1)) {
return false; // Too soon
}
_lastRequestTime = now;
}
return true;
}
Loading Guard:
@onEvent
bool _onEvents(_Event event) {
if (state.isLoading) {
print('Blocked ${event.runtimeType}: already loading');
return false;
}
return true;
}
Permission Check:
@onEvent
bool _onEvents(_Event event) {
if (event is _DeleteEvent && !authService.canDelete) {
return false;
}
return true;
}
Initialization (@onInit)
Run code automatically when bloc is created.
@MonoBloc()
class TodoBloc extends _$TodoBloc<TodoState> {
TodoBloc() : super(const TodoState());
@onInit
void _onInit() {
// Called automatically after constructor
loadTodos();
}
@event
Future<TodoState> _onLoadTodos() async {
final todos = await repository.fetchTodos();
return state.copyWith(todos: todos);
}
}
Multiple Init Methods:
@onInit
void _initializeAnalytics() {
analytics.initialize();
}
@onInit
void _subscribeToNotifications() {
notificationService.stream.listen((n) => showNotification(n));
}
Advanced Patterns
Progressive Stream Updates
@restartableEvent
Stream<TodoState> _onSearchAcrossSources(String query) async* {
yield state.copyWith(loading: true, results: []);
for (final source in sources) {
final results = await source.search(query);
// Yield progressive results as each source completes
yield state.copyWith(
results: [...state.results, ...results],
loadedSources: [...state.loadedSources, source.name],
);
}
yield state.copyWith(loading: false);
}
Multiple State Emissions with Emitter
@event
Future<void> _onComplexOperation(_Emitter emit) async {
// Phase 1: Loading
emit(state.copyWith(loading: true, progress: 0.0));
// Phase 2: Fetch users
final users = await api.fetchUsers();
emit(state.copyWith(users: users, progress: 0.5));
// Phase 3: Fetch posts
final posts = await api.fetchPosts();
emit(state.copyWith(posts: posts, progress: 1.0));
// Phase 4: Complete
emit(state.copyWith(loading: false));
}
Base Classes with Public Events
// Base bloc with reusable events
abstract class AppBaseBloc<T> extends _$AppBaseBloc<AppState<T>> {
AppBaseBloc(AppState<T> initialState) : super(initialState);
@event
@protected // Required for public events
AppState<T> onReset() { // Must start with 'on'
return AppState(value: null);
}
@event
@protected
AppState<T> onSetValue(T value) {
return AppState(value: value);
}
}
// Child bloc can use base events
class CounterBloc extends AppBaseBloc<int> {
CounterBloc() : super(AppState(value: 0));
@event
AppState<int> _onIncrement() => AppState(value: state.value! + 1);
void reset() => onReset(); // Call base event
}
Combining Multiple Features
// Define actions in a private mixin
@MonoActions()
mixin _TodoBlocActions {
void showNotification(String message);
}
// The generated base class includes the actions mixin automatically
@AsyncMonoBloc(MonoConcurrency.restartable)
class TodoBloc extends _$TodoBloc<List<Todo>> {
TodoBloc() : super(const MonoAsyncValue.loading());
@onInit
void _onInit() {
loadTodos();
}
@event
Future<List<Todo>> _onLoadTodos() async {
return await repository.fetchTodos();
}
@restartableEvent
Stream<List<Todo>> _onSearch(String query) async* {
for (final source in sources) {
final results = await source.search(query);
yield results;
}
}
@event
Future<List<Todo>> _onAddTodo(String title) async {
await repository.add(Todo(title: title));
showNotification('Todo added');
return await repository.fetchTodos();
}
@onError
MonoAsyncValue<List<Todo>> _onError(Object error, StackTrace stack) {
showNotification('Error: $error');
return withError(error, stack, state.dataOrNull);
}
@onEvent
bool _onEvents(_Event event) {
if (state.isLoading && event is! _SearchEvent) {
return false; // Block events while loading (except search)
}
return true;
}
}
Type Preservation
MonoBloc preserves exact type notation from source code:
Typedefs:
typedef OnComplete = void Function(bool success);
// In @MonoActions() mixin:
void showDialog({required String title, required OnComplete callback});
// Generated code preserves OnComplete, not expanded void Function(bool)
Records:
// In @MonoActions() mixin:
void processData((String name, int age) data);
// Preserves record type notation
Complex Generics:
@event
Future<Map<String, List<Todo>>> _onGroupByTag() async { ... }
// Exact generic notation preserved
Shorthand Constants
@event // Same as @MonoEvent()
@restartableEvent // Same as @MonoEvent(MonoConcurrency.restartable)
@sequentialEvent // Same as @MonoEvent(MonoConcurrency.sequential)
@concurrentEvent // Same as @MonoEvent(MonoConcurrency.concurrent)
@droppableEvent // Same as @MonoEvent(MonoConcurrency.droppable)
@onError // Same as @MonoOnError()
@MonoActions() // Annotation for action mixins
Classes
- ActionStreamable
- Interface for objects that provide an action stream.
- AsyncMonoBloc
- Marks a class for MonoBloc code generation with async state management.
-
Bloc<
Event, State> -
Takes a
StreamofEventsas input and transforms them into aStreamofStatesas output. -
BlocBase<
State> - An interface for the core functionality implemented by both Bloc and Cubit.
-
BlocEventSink<
Event extends Object?> - An ErrorSink that supports adding events.
- BlocObserver
- An interface for observing the behavior of Bloc instances.
-
Change<
State> -
A Change represents the change from one
Stateto another. A Change consists of the currentState and nextState. - Closable
- An object that must be closed when no longer in use.
-
Cubit<
State> - A Cubit is similar to Bloc but has no notion of events and relies on methods to emit new states.
-
Emittable<
State extends Object?> - An object that can emit new states.
-
EmittableStateStreamableSource<
State> - A StateStreamableSource that can emit new states.
-
Emitter<
State> - An Emitter is a class which is capable of emitting new states.
- ErrorSink
- A generic destination for errors.
-
FutureOr<
T> -
A type representing values that are either
Future<T>orT. - MonoActions
- Marks a mixin as containing action methods for MonoBloc.
-
MonoAsyncEmitter<
S> -
MonoAsyncSeqEmitter<
S> - Wrapper around Emitter that provides convenient methods for async operations.
-
MonoAsyncValue<
T> - Represents an asynchronous operation state with loading, data, or error. Similar to AsyncSnapshot but with additional error handling utilities.
- MonoBloc
- Marks a class for MonoBloc code generation.
- MonoBlocActions
- Base class for action handlers in pure Dart projects.
- MonoEvent
- Marks a method as an event handler in MonoBloc.
- MonoEventTransformer
- Wrapper for bloc_concurrency transformers to avoid type conflicts and provide type-safe transformers for MonoBloc queues.
- MonoInit
- Marks a method to be called during bloc initialization.
- MonoOnError
- Marks a method as an error handler for event errors.
- MonoOnEvent
- Marks a method as an event interceptor that runs before event processing.
- MonoStackTrace
- Utility for filtering and combining stack traces in MonoBloc error handling.
- MultiBlocObserver
-
A BlocObserver which supports registering multiple BlocObserver
instances. This is useful when maintaining multiple BlocObserver instances
for different functions e.g.
LoggingBlocObserver,ErrorReportingBlocObserver. -
StateStreamable<
State> - A Streamable that provides synchronous access to the current state.
-
StateStreamableSource<
State> - A StateStreamable that must be closed when no longer in use.
-
Streamable<
State extends Object?> - An object that provides access to a stream of states over time.
-
Transition<
Event, State> - A Transition is the change from one state to another. Consists of the currentState, an event, and the nextState.
Enums
- MonoConcurrency
- Defines how events are processed when multiple events are dispatched.
Mixins
-
MonoBlocActionMixin<
A, S> - Mixin that adds action stream capability to a Bloc.
Extension Types
- MonoSeqEmitter
- Extension type that wraps Emitter for sequential blocs.
Constants
- concurrentEvent → const MonoEvent
- Shorthand constant for a concurrent event (processes in parallel)
- droppableEvent → const MonoEvent
- Shorthand constant for a droppable event (ignores new events while processing)
- event → const MonoEvent
- Shorthand constant for marking a method as an event handler
- immutable → const Immutable
- Annotation on an immutable class.
- onError → const MonoOnError
- Shorthand constant for marking a method as an error handler
- onEvent → const MonoOnEvent
- Shorthand constant for marking a method as an event interceptor
- onInit → const MonoInit
- Shorthand constant for marking a method as an initialization handler
- protected → const Object
- Annotation on instance member that should only be used by subclasses.
- restartableEvent → const MonoEvent
- Shorthand constant for a restartable event (cancels previous events)
- sequentialEvent → const MonoEvent
- Shorthand constant for a sequential event (processes in order)
Typedefs
-
EventHandler<
Event, State> = FutureOr< void> Function(Event event, Emitter<State> emit) -
An event handler is responsible for reacting to an incoming
Eventand can emit zero or more states via the Emitter. -
EventMapper<
Event> = Stream< Event> Function(Event event) - Signature for a function which converts an incoming event into an outbound stream of events. Used when defining custom EventTransformers.
-
EventTransformer<
Event> = Stream< Event> Function(Stream<Event> events, EventMapper<Event> mapper) - Used to change how events are processed. By default events are processed concurrently.
- MonoBlocConcurrency = MonoEventTransformer
- Deprecated alias for MonoEventTransformer.