leulit_flutter_actionmanager 5.0.4
leulit_flutter_actionmanager: ^5.0.4 copied to clipboard
A lightweight, type-safe action dispatcher for Flutter applications. Works seamlessly across all layers - UI, domain, data, and services.
Changelog #
5.0.4 - 2026-02-19 #
π Bug Fix: ActionManagerMultiWidget inconsistent with ActionManagerWidget #
ActionManagerMultiWidget was not fully consistent with ActionManagerWidget in its internal types and builder signature:
_latestEventwas declared asActionEvent?instead ofActionEvent<dynamic>?builderparameter wasWidget Function(BuildContext, ActionEvent? event)(nullable) instead ofWidget Function(BuildContext, ActionEvent event)(non-nullable)- Docstring example showed
event?.action.nameinstead ofevent.action.name
All three are now identical to ActionManagerWidget. The builder in both widgets receives a guaranteed non-null ActionEvent β it is only called after a real dispatch has occurred.
5.0.3 - 2026-02-19 #
π Bug Fix: ActionManagerMultiWidget builder called before any dispatch #
Same fix as 5.0.2 applied to ActionManagerMultiWidget. Previously the builder was called on initial render with event = null even before any dispatch occurred. Now returns placeholder or SizedBox.shrink() until a real dispatch happens β consistent with the behavior of ActionManagerWidget.
5.0.2 - 2026-02-19 #
π Bug Fix: ActionManagerWidget builder called before any dispatch #
ActionManagerWidget no longer calls builder on its initial render when no dispatch has occurred yet.
Previously, if no placeholder was provided, the widget called builder immediately with a synthetic fallback event (ActionEvent.now(action, data: null, context: null)). This caused two confusing problems:
event.contextwas alwaysnullon the first render, even when the upcoming dispatch includes context- The builder appeared to "fire" for an action that nobody had dispatched yet
New behavior:
- No
placeholder, no dispatch yet β rendersSizedBox.shrink()silently - With
placeholder, no dispatch yet β renders the placeholder - After any dispatch β renders
builderwith the realActionEvent(context, data, timestamp all intact)
// β
builder now only called after a real dispatch
ActionManagerWidget(
action: AppActions.displayIssueForm,
builder: (context, event) {
// event.context is NEVER null here if dispatch included context
final source = event.context?['source']; // β
works correctly
return MyForm(show: event.data ?? false);
},
)
5.0.1 - 2026-02-19 #
π Bug Fixes & Internal Improvements #
Patch release applying all audit fixes that were missing from 5.0.0.
- Fixed
listen<T>()silent failure with untyped null dispatch -dispatch(action, data: null)without explicit type no longer silently drops typed handlers (e.g.,listen<String>()). Dynamic dispatch bypasses the generic cast while keeping data access type-safe - Fixed
off()leaving empty map entries - Last handler removal now cleans up the map entry, preventing memory accumulation and stale entries inprintSummary() - Fixed
clear()not resetting handler IDs -_nextHandlerIdis now reset to 0, giving deterministic IDs afterclear()in tests - Fixed
configureLogger()ignoringshowTimestampandshowStackTrace-ActionLoggerfields are now mutable; parameters were previously accepted but silently discarded - Fixed
ActionManagerMultiWidgetunnecessary re-subscribe - Action list now compared by value instead of reference identity, preventing re-subscribe on every parent rebuild with the same actions - Fixed
event.contextnull inActionManagerWidget-_latestEventis now stored and used directly without recreation, preserving all original event fields (context, timestamp, data)
5.0.0 - 2026-02-19 #
π₯ BREAKING CHANGES: Unified Handler Signature #
All handlers now receive the full ActionEvent object instead of just the data, providing consistent access to action metadata (context, timestamp, action enum) across the entire API.
Why This Change?
Version 4.8.0 introduced optional "Full" variants (builderFull, listenFull, onFull) to provide context access, but this created an inconsistent API where developers had to choose between convenience and access to debugging information. This version makes context access the default while maintaining type safety and simplifying the API.
What Changed
1. Handler Signatures - All handlers now receive ActionEvent<T>:
// β Before (v4.x):
ActionManager.on<String>(AppAction.updateText, (data) {
print('Text: $data');
});
// β
After (v5.0.0):
ActionManager.on<String>(AppAction.updateText, (event) {
final data = event.data; // Access data
final source = event.context?['source']; // Access context
final when = event.timestamp; // Access timestamp
print('Text: $data from $source at $when');
});
2. Widget Builders - Receive ActionEvent instead of separate parameters:
// β Before (v4.x):
ActionManagerWidget<String>(
action: AppAction.updateText,
builder: (context, data, action) {
return Text(data ?? 'No text');
},
)
// β
After (v5.0.0):
ActionManagerWidget<String>(
action: AppAction.updateText,
builder: (context, event) {
return Text(event.data ?? 'No text');
// Also access: event.context, event.timestamp, event.action
},
)
3. Removed Methods - Redundant "Full" variants removed:
- β
ActionManager.onFull()β UseActionManager.on() - β
ActionManager.listenFull()β UseActionManager.listen() - β
ActionManagerWidget.builderFullβ UseActionManagerWidget.builder
Migration Guide
For ActionManager.on() and listen():
// Before:
ActionManager.on<User>(AppAction.userUpdated, (user) {
print('User: ${user?.name}');
});
// After:
ActionManager.on<User>(AppAction.userUpdated, (event) {
final user = event.data;
print('User: ${user?.name}');
});
For ActionManager.onVoid():
// Before:
ActionManager.onVoid(AppAction.refresh, () {
refresh();
});
// After:
ActionManager.onVoid(AppAction.refresh, (event) {
refresh();
// Optionally use event.context, event.timestamp
});
For ActionManagerWidget:
// Before:
ActionManagerWidget<String>(
action: AppAction.updateText,
builder: (context, data, action) => Text(data ?? ''),
)
// After:
ActionManagerWidget<String>(
action: AppAction.updateText,
builder: (context, event) => Text(event.data ?? ''),
)
Benefits
- β
Consistent API - No more choosing between
on()vsonFull() - β Better Debugging - Context always available for tracking action sources
- β
Type Safety - Generic types preserved through
ActionEvent<T> - β Simpler API - Fewer methods to learn and maintain
- β
Future-Proof - Easy to add more metadata to
ActionEventwithout breaking changes
Internal Improvements
- Removed
_StreamHandlerWrapperclass (no longer needed with unified signature) - Simplified handler registration logic
- Fixed type casting issues with null data in widgets
- Fixed
listen<T>()silent failure with untyped null dispatch - Usingdispatch(action, data: null)without explicit type (infersActionEvent<Null>) no longer silently drops handlers registered withlisten<String>(). The dynamic dispatch bypasses the generic type cast while keeping actual data access type-safe (null is always valid forT?) - Fixed
off()leaving empty map entries - When the last handler for an action is removed, the map entry is now cleaned up, preventing memory accumulation andprintSummary()showing stale empty entries - Fixed
clear()not resetting handler IDs - Tests afterclear()now get deterministic IDs starting fromhandler_0again - Fixed
configureLogger()ignoringshowTimestampandshowStackTrace- These parameters were accepted but silently ignored.ActionLoggerfields are now mutable and updated correctly - Fixed
ActionManagerMultiWidgetunnecessary re-subscribe - Previously compared action list by reference identity, causing re-subscribe on every parent rebuild even with the same actions. Now compares by value
4.8.0 - 2026-02-19 #
π― New Feature: Full Context Access in All Handlers #
Enhanced debugging capabilities by providing access to ActionEvent context across all handler types.
Problem Solved
When using the context parameter in dispatch() for debugging (e.g., tracking where actions originate), handlers couldn't access this information. Only the action data was available, making it difficult to debug complex applications with many dispatch points.
What's New
1. ActionManagerWidget.builderFull - Access full ActionEvent in widgets:
ActionManagerWidget<bool>(
action: AppAction.displayForm,
builderFull: (context, event) {
final source = event.context?['source']; // β
Access context
print('Triggered from: $source');
return MyForm(show: event.data ?? false);
},
)
2. ActionManager.listenFull() - New reactive listener with full event:
final subscription = ActionManager.listenFull<bool>(
AppAction.displayForm,
(event) {
final source = event.context?['source']; // β
Access context
print('Data: ${event.data}');
print('Timestamp: ${event.timestamp}');
},
);
Use Case Example
// Dispatch with source tracking
ActionManager.dispatch(
AppAction.loadData,
data: items,
context: {
'source': 'HomeScreen.onRefresh',
'line': 145,
'user_id': currentUser.id,
},
);
// Handle with full context access
ActionManager.listenFull(AppAction.loadData, (event) {
debugPrint('Action from: ${event.context?['source']}');
// Useful for debugging when the same action is dispatched from multiple places
});
Backward Compatibility
- β All existing code continues working unchanged
- β
builder,listen(), andon()maintain their original signatures - β
New
builderFullandlistenFull()are opt-in alternatives - β
onFull()already existed and continues working as before
Benefits
- π Better Debugging: Track where actions originate in large codebases
- π Source Tracking: Know exactly which screen/widget triggered an action
- β±οΈ Timestamp Access: See when actions were dispatched
- π Rich Context: Add any metadata needed for debugging or analytics
4.7.0 - 2026-02-16 #
π New Feature: dispatchAsync() with Completion Callback #
Major enhancement to handle async listeners and detect when all handlers complete.
Problem Solved
Previously, with dispatch(), there was no way to know when all listeners (especially async ones) had finished executing. This made it difficult to:
- Show loading states correctly
- Execute code after all handlers complete
- Ensure async operations (DB saves, API calls) finish before continuing
New API: dispatchAsync()
await ActionManager.dispatchAsync(
AppAction.saveData,
data: entities,
onComplete: () {
debugPrint('β
All listeners completed (UI + DB + Analytics)');
}
);
// Code here runs AFTER all handlers finish
Key Features
- β Independent Execution: Each handler runs in its own Future without blocking others
- β Parallel Execution: Async handlers execute simultaneously (3 handlers of 100ms = ~100ms total, not 300ms)
- β
Completion Callback:
onCompleteexecutes when ALL handlers (sync + async) finish - β Error Resilient: If one handler fails, others continue executing
- β
Non-Breaking: Original
dispatch()continues working unchanged - β
Detects Async: Automatically detects and waits for handlers with
async/await
Updated Type Definitions
Handlers now support both synchronous and asynchronous execution:
typedef ActionHandler<T> = FutureOr<void> Function(T? data);
typedef VoidActionHandler = FutureOr<void> Function();
typedef FullActionHandler<T> = FutureOr<void> Function(ActionEvent<T> action);
Use Cases
With await - Wait for completion:
Future<void> saveData() async {
setState(() => isLoading = true);
await ActionManager.dispatchAsync(
AppAction.save,
data: data,
onComplete: () => debugPrint('Save complete'),
);
setState(() => isLoading = false);
}
Without await - Fire and forget with tracking:
void logEvent() {
ActionManager.dispatchAsync(
AppAction.logEvent,
data: event,
onComplete: () => debugPrint('Event logged'),
); // Continues immediately
}
When to Use Each Method
dispatch(): Fire and forget, don't care when handlers finish (most UI cases)dispatchAsync()withawait: Need to wait for all handlers (loading states, sequences)dispatchAsync()withoutawait: Want tracking but don't want to block
Performance
Verified through comprehensive tests:
- β 3 async handlers of 100ms execute in ~102ms (parallel)
- β Heavy sync handler doesn't block other handlers
- β No breaking changes - all 101 existing tests pass
New Tests
- Added 12 comprehensive tests in
test/dispatch_async_test.dart - Added 7 behavior verification tests in
test/behavior_verification_test.dart
π Documentation #
- Updated example app with
dispatchAsync()demonstration - Added complete behavior comparison tables
- Included performance benchmarks
π§ Internal Changes #
- Enhanced
_HandlerWrapperwithcallAsync()method - Improved error handling for async execution
- Maintained backward compatibility
4.6.0 - 2026-02-16 #
π― Asynchronous Handler Execution (Performance Improvement) #
All handlers now execute asynchronously via microtasks.
This improves performance by batching multiple setState() calls and preventing UI blocking.
β¨ New Features #
- All handlers (
on(),onVoid(),listen(),onFull()) now execute asynchronously - Handlers are scheduled via
scheduleMicrotask()afterdispatch()returns - Multiple consecutive dispatches automatically batch setState calls (66% fewer rebuilds)
- Prevents infinite loops when dispatching during build phase
π Improved Documentation #
- README.md: Comprehensive best practices section for StatefulWidget usage
- example/lib/main.dart: Updated with correct cleanup patterns using
listen() - AUDIT.md: Complete technical audit of async implementation
- AUDIT_SUMMARY.md: Executive summary in Spanish
β οΈ Important: Handler Cleanup Required #
Handlers registered with on() or listen() must be cleaned up to prevent memory leaks:
β RECOMMENDED Pattern:
class _MyWidgetState extends State<MyWidget> {
final _subscriptions = <StreamSubscription>[];
@override
void initState() {
super.initState();
_subscriptions.add(
ActionManager.listen<Data>(AppAction.update, (data) {
if (mounted) setState(() { });
}),
);
}
@override
void dispose() {
for (final sub in _subscriptions) {
sub.cancel();
}
super.dispose();
}
}
Or use ActionManagerWidget (auto-cleanup):
ActionManagerWidget<Data>(
action: AppAction.update,
builder: (context, data, action) => Text('$data'),
)
π§ Migration Notes #
No breaking changes - existing code continues to work, but:
- β
Add cleanup in
dispose()for allon()andlisten()calls - β
Always check
if (mounted)beforesetState()in async handlers - β
Or migrate to
ActionManagerWidgetfor automatic cleanup
See README.md for complete migration guide.
π§ͺ Testing #
- New comprehensive test suite for async behavior
- 85/89 tests passing (4 intentional audit tests document edge cases)
- Tests validate performance improvements and safety
4.5.0 - 2026-02-12 #
π¨ BREAKING CHANGE - Loading Overlay Removed #
Funcionalidad de loading overlay completamente eliminada.
La funcionalidad de loading overlay introducida en v4.2.0-v4.4.4 ha sido eliminada debido a incompatibilidades con GetMaterialApp y otros frameworks.
β Removed Features #
displayLoadingparameter inActionManager.dispatch()LoadingOverlayControllerclassActionManagerProviderwidget- All loading overlay related functionality
π‘ Migration Guide #
Si estabas usando:
ActionManager.dispatch(
AppAction.loadData,
displayLoading: true, // β Este parΓ‘metro ya no existe
);
Implementa tu propia soluciΓ³n de loading:
- Usa un paquete especializado como
flutter_easyloading,loading_overlay, etc. - Implementa tu propio overlay personalizado
- Usa estados de carga en tu UI (isLoading flag)
π¦ What Remains #
Todo el core de ActionManager permanece intacto:
- β
ActionManager.dispatch()(sin displayLoading) - β
ActionManager.on()/onVoid() - β
ActionManager.onFull() - β Stream-based listening
- β Logger y debugging
- β Statistics y introspection
4.4.4 - 2026-02-12 #
π BREAKING CHANGE (Simplification) #
ActionManagerProvider ya NO es necesario.
El sistema ahora obtiene el overlay automΓ‘ticamente desde el navigatorKey global.
β¨ New Approach #
ConfiguraciΓ³n super simple:
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
void main() {
LoadingOverlayController.instance.setNavigatorKey(navigatorKey);
runApp(
GetMaterialApp(
navigatorKey: navigatorKey,
home: HomePage(),
),
);
}
π Bug Fixes #
- Funciona con GetMaterialApp builder - Ya no requiere ActionManagerProvider en contextos donde el overlay no estΓ‘ disponible
- Soporte para navigatorKey global - Obtiene el overlay directamente del Navigator
- MΓ‘s simple y confiable - Elimina la complejidad de intentar registrar overlays en diferentes contextos
- Compatible con cualquier estructura de app - Funciona con GetX, Provider, Riverpod, y cualquier framework
π Migration Guide #
Antes (v4.4.3):
GetMaterialApp(
builder: (context, child) {
return ActionManagerProvider( // β No funcionaba
child: child!,
);
},
)
Ahora (v4.4.4):
// En main.dart
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
LoadingOverlayController.instance.setNavigatorKey(navigatorKey); // β
Simple
runApp(MyApp());
}
// En tu app
GetMaterialApp(
navigatorKey: navigatorKey,
home: HomePage(),
)
β Testing #
- All 79 tests passing
- Verified with GetMaterialApp and navigatorKey
4.4.3 - 2026-02-12 #
π Bug Fixes #
- Fixed GetMaterialApp overlay registration - Increased retry attempts from 10 to 30 frames for better GetX compatibility
- Removed Future.delayed dependency - Now uses only postFrameCallback for test compatibility
- Improved registration reliability - Single-pass registration flag prevents multiple registration attempts
- Better frame-based retry - Recursive postFrameCallback approach works reliably with GetMaterialApp
β Testing #
- All 79 tests passing
- No pending timers in test environment
- Verified with GetX framework
4.4.2 - 2026-02-12 #
π Improvements #
- GetX / GetMaterialApp compatibility - ActionManagerProvider now works seamlessly with GetMaterialApp and other state management frameworks
- Smart overlay registration with retries - Automatic retry mechanism (up to 5 attempts) ensures overlay registration even when frameworks delay initialization
- Better framework support - Compatible with GetX, Provider, Riverpod, and other frameworks that wrap MaterialApp
π Bug Fixes #
- Fixed overlay detection in GetMaterialApp - Uses
Overlay.maybeOf()instead ofOverlay.of()to prevent exceptions - Added retry logic - Handles cases where overlay is not immediately available on first build
- Improved error messages - More specific guidance for different scenarios including GetMaterialApp
π Usage with GetX #
void main() {
runApp(
GetMaterialApp(
home: ActionManagerProvider(
child: HomePage(),
),
),
);
}
β Testing #
- All 79 tests passing
- Verified compatibility with GetX framework
4.4.1 - 2026-02-12 #
π Bug Fixes #
- Fixed ActionManagerProvider overlay initialization - Now correctly uses existing MaterialApp overlay instead of creating a new one, preventing overlay conflicts
- Fixed loading counter management - Counter only increments when overlay is available, preventing state inconsistencies
- Improved placement guidance - Updated all documentation to clearly indicate ActionManagerProvider must be inside MaterialApp
- Better error messages - More helpful guidance when overlay is not properly configured
π Breaking Change Note #
IMPORTANT: ActionManagerProvider must be placed INSIDE MaterialApp, not wrapping it:
// β
Correct
MaterialApp(
home: ActionManagerProvider(
child: HomePage(),
),
)
// β Incorrect
ActionManagerProvider(
child: MaterialApp(...),
)
β Testing #
- All 79 tests passing
- Fixed unit tests to reflect corrected behavior
4.4.0 - 2026-02-12 #
β¨ New Features #
- Loading Overlay Support - Display fullscreen blocking loading during action execution
- Added
ActionManagerProviderwidget (must be placed inside MaterialApp) - Added
displayLoadingparameter todispatch()method - Added
LoadingOverlayControllerfor manual control - Automatic show/hide management with try-finally protection
- Customizable loading widget via
loadingBuilderparameter - Thread-safe with counter-based concurrent requests handling
- Added
π Bug Fixes #
- Fixed overlay initialization - ActionManagerProvider now correctly registers with existing MaterialApp overlay instead of creating a new one
- Fixed counter management - Loading counter no longer increments when no overlay is registered, preventing state inconsistencies
- Improved error messages - Better guidance for correct ActionManagerProvider placement
π Usage #
// 1. Place ActionManagerProvider INSIDE MaterialApp
void main() {
runApp(
MaterialApp(
home: ActionManagerProvider(
child: HomePage(),
),
),
);
}
// 2. Use displayLoading parameter
ActionManager.dispatch(
AppAction.loadData,
displayLoading: true,
);
π§ New Components #
ActionManagerProvider- Widget wrapper (place inside MaterialApp)LoadingOverlayController- Singleton controller for overlay managementdisplayLoadingparameter inActionManager.dispatch()
4.3.0 - 2026-02-12 #
β¨ New Features #
- Added
debugparameter todispatch()method - Enable logging for specific dispatches without activating the global logger- Usage:
ActionManager.dispatch(AppAction.updateUi, debug: true); - Useful for targeted debugging without flooding console with all action logs
- Works independently from global
logger.enabledsetting
- Usage:
π§ Changes #
- Logger now disabled by default - Changed
ActionLogger.enableddefault fromkDebugModetofalse- More predictable behavior in production and debug modes
- Explicit activation required:
ActionManager.configureLogger(enabled: true); - Reduces console noise for developers who don't need action logging
Migration Guide #
- If you relied on automatic logging in debug mode, explicitly enable it:
ActionManager.configureLogger(enabled: true); - Or use the new
debugparameter for selective logging:ActionManager.dispatch(AppAction.test, debug: true);
4.2.1 - 2026-02-10 #
π Bug Fixes #
- Fixed ActionManagerWidget not rebuilding on duplicate actions - Widget now rebuilds every time the same action is dispatched, even with identical data
- Added internal event counter - Forces rebuild even when data hasn't changed, ensuring UI always responds to actions
- Fixed similar issue in ActionManagerMultiWidget - Applied same fix to ensure consistency
Context #
- Previous version (4.2.0) introduced an optimization to avoid rebuilds when data didn't change
- This optimization was too aggressive and prevented rebuilds when dispatching the same action multiple times
- Users reported that buttons triggering the same action with same data would not cause UI updates
- Fix ensures that any action dispatch triggers a rebuild, regardless of data equality
Example of Fixed Behavior #
// Before: Only first dispatch would cause rebuild
ActionManager.dispatch(AppActions.updLeftBar, data: LeftBarWidget.mainMenu);
ActionManager.dispatch(AppActions.updLeftBar, data: LeftBarWidget.mainMenu); // Would not rebuild β
// After: Both dispatches cause rebuild
ActionManager.dispatch(AppActions.updLeftBar, data: LeftBarWidget.mainMenu); // Rebuilds β
ActionManager.dispatch(AppActions.updLeftBar, data: LeftBarWidget.mainMenu); // Rebuilds β
4.2.0 - 2026-02-10 #
π¨ BREAKING CHANGES #
- ActionManagerWidget builder signature changed - Now requires 3 parameters:
(BuildContext context, T? data, Enum action)instead of 2- Migration: Add the
actionparameter to your builder function - Before:
builder: (context, data) => Text('$data') - After:
builder: (context, data, action) => Text('$data')
- Migration: Add the
β¨ New Features #
- Added action parameter to ActionManagerWidget builder - Now you can access the action being dispatched for debugging purposes
- Better debugging capabilities - Use the action parameter to log which action triggered the rebuild
π Bug Fixes #
- Fixed excessive rebuilds in
ActionManagerWidget- Widget now only rebuilds when data actually changes, not on every dispatch - Fixed excessive rebuilds in
ActionManagerMultiWidget- Widget now only rebuilds when event data changes, not on every dispatch
Improved #
- Performance optimization - Added equality check before calling
setState()to prevent unnecessary rebuilds - Better efficiency - Widgets are more performant when multiple dispatches occur with the same data
- Enhanced developer experience - Action parameter helps identify which action triggered a rebuild
Example #
ActionManagerWidget<bool>(
action: AppAction.displayForm,
builder: (context, display, action) {
debugPrint('Action: $action'); // For debugging
if (display == true) return MyForm();
return const SizedBox.shrink();
},
)
4.1.0 - 2026-02-10 #
β¨ New Features #
- Added
ActionManagerWidget<T>- Reactive widget that automatically rebuilds when a specific action is dispatched - Added
ActionManagerMultiWidget- Reactive widget that rebuilds when any of multiple actions is dispatched
Features #
ActionManagerWidget
A stateful widget that:
- Automatically subscribes to an action on
initState - Calls
setState()when the action is dispatched - Auto-cancels subscription on
dispose - Supports optional placeholder before first event
- Type-safe with generic data type
Example:
ActionManagerWidget<User>(
action: AppAction.userUpdated,
placeholder: CircularProgressIndicator(),
builder: (context, user) {
return Text('User: ${user?.name ?? "none"}');
},
)
ActionManagerMultiWidget
Listens to multiple actions and provides the full ActionEvent:
ActionManagerMultiWidget(
actions: [AppAction.userUpdated, AppAction.userLoggedIn],
builder: (context, event) {
return Text('Last action: ${event?.action.name}');
},
)
Added #
- New file:
lib/src/action_manager_widget.dart - 10 new widget tests (64 total tests passing)
- Exported widgets in main library file
Improved #
- Simplified reactive UI patterns - no need for manual StreamSubscription management
- Better separation of concerns - widgets handle their own lifecycle
4.0.0 - 2026-02-10 #
π¨ BREAKING CHANGES #
- Removed
streamOf<T>()method - UseActionManager.stream.where((a) => a.action == yourAction)instead for filtering streams by action
β¨ Improvements #
- Simplified dispatch logic - Removed internal complexity while maintaining all functionality
- More predictable stream behavior - Stream always emits events even without registered handlers
- Better code maintainability - Cleaner, more straightforward implementation
Migration Guide #
If you were using streamOf<T>():
Before:
final subscription = ActionManager.streamOf<String>(AppAction.test)
.listen((action) {
print(action.data);
});
After:
final subscription = ActionManager.stream
.where((a) => a.action == AppAction.test)
.listen((action) {
print(action.data as String?);
});
Or simply use the higher-level listen() API:
final subscription = ActionManager.listen<String>(
AppAction.test,
(data) => print(data),
);
3.0.0 - 2026-02-09 #
π¨ BREAKING CHANGES #
- Renamed
Actionclass toActionEventto avoid naming conflicts with Flutter SDK's built-inActionclass - This change affects all code that directly instantiates or references the
Actionclass
Why This Change? #
The Flutter SDK includes a built-in Action class in package:flutter/src/widgets/actions.dart. Using a class with the same name in this package could cause naming conflicts and ambiguity in applications that use both Flutter's actions system and this action manager.
Migration Guide #
The ActionManager API remains unchanged. If you were only using the high-level API (ActionManager.dispatch, ActionManager.on, etc.), no changes are needed.
If you were directly instantiating or type-checking against the Action class:
Before:
final action = Action<String>.now(AppAction.test, data: 'test');
if (myVar is Action<String>) { ... }
After:
final action = ActionEvent<String>.now(AppAction.test, data: 'test');
if (myVar is ActionEvent<String>) { ... }
Changed #
- Renamed
Action<T>class toActionEvent<T> - Updated
FullActionHandler<T>typedef to useActionEvent<T> - Updated internal references in
ActionManagerandActionLogger - Updated all tests (48 tests passing)
- Updated documentation and examples
- No analysis issues
Fixed #
- Eliminated potential naming conflicts with Flutter SDK's
Actionclass - Improved clarity and semantic meaning of the action event class
2.0.0 - 2026-02-09 #
π¨ BREAKING CHANGES #
- Renamed
ActionDispatchertoActionManagerto avoid naming conflicts with Flutter's built-inActionDispatcherclass - Renamed file
action_dispatcher.darttoaction_manager.dart
Migration Guide #
Simply replace all instances of ActionDispatcher with ActionManager in your code:
Before:
ActionDispatcher.dispatch(AppAction.test);
ActionDispatcher.on<String>(AppAction.test, (data) {});
After:
ActionManager.dispatch(AppAction.test);
ActionManager.on<String>(AppAction.test, (data) {});
Changed #
- All
ActionDispatcherreferences updated toActionManagerthroughout the library - Updated all documentation (README.md, project.md)
- Updated all examples and tests
- All 48 tests passing
- No analysis issues
1.0.2 - 2026-02-09 #
Added #
- Comprehensive test suite with 48 tests covering all library methods
- Tests for Action class (creation, equality, hashCode, toString)
- Tests for ActionDispatcher dispatch, registration, and unregistration
- Tests for reactive streams (listen, stream, streamOf)
- Tests for metadata and statistics tracking
- Tests for error handling and resilience
- Tests for ActionLogger configuration
- Integration tests for complex workflows
Improved #
- Enhanced code formatting across all files
- Better test coverage and reliability
- Improved documentation in test files
1.0.0 - 2026-02-09 #
Added #
- Initial release
- Core ActionDispatcher with enum-based actions
- Type-safe action handlers
- Built-in debugging and introspection
- Reactive StreamSubscription support
- Comprehensive documentation and examples
- Unit tests with full coverage
- Example application demonstrating key features