leulit_flutter_actionmanager 5.0.4 copy "leulit_flutter_actionmanager: ^5.0.4" to clipboard
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:

  • _latestEvent was declared as ActionEvent? instead of ActionEvent<dynamic>?
  • builder parameter was Widget Function(BuildContext, ActionEvent? event) (nullable) instead of Widget Function(BuildContext, ActionEvent event) (non-nullable)
  • Docstring example showed event?.action.name instead of event.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:

  1. event.context was always null on the first render, even when the upcoming dispatch includes context
  2. The builder appeared to "fire" for an action that nobody had dispatched yet

New behavior:

  • No placeholder, no dispatch yet β†’ renders SizedBox.shrink() silently
  • With placeholder, no dispatch yet β†’ renders the placeholder
  • After any dispatch β†’ renders builder with the real ActionEvent (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 in printSummary()
  • Fixed clear() not resetting handler IDs - _nextHandlerId is now reset to 0, giving deterministic IDs after clear() in tests
  • Fixed configureLogger() ignoring showTimestamp and showStackTrace - ActionLogger fields are now mutable; parameters were previously accepted but silently discarded
  • Fixed ActionManagerMultiWidget unnecessary 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.context null in ActionManagerWidget - _latestEvent is 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() β†’ Use ActionManager.on()
  • ❌ ActionManager.listenFull() β†’ Use ActionManager.listen()
  • ❌ ActionManagerWidget.builderFull β†’ Use ActionManagerWidget.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() vs onFull()
  • βœ… 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 ActionEvent without breaking changes

Internal Improvements

  • Removed _StreamHandlerWrapper class (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 - Using dispatch(action, data: null) without explicit type (infers ActionEvent<Null>) no longer silently drops handlers registered with listen<String>(). The dynamic dispatch bypasses the generic type cast while keeping actual data access type-safe (null is always valid for T?)
  • 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 and printSummary() showing stale empty entries
  • Fixed clear() not resetting handler IDs - Tests after clear() now get deterministic IDs starting from handler_0 again
  • Fixed configureLogger() ignoring showTimestamp and showStackTrace - These parameters were accepted but silently ignored. ActionLogger fields are now mutable and updated correctly
  • Fixed ActionManagerMultiWidget unnecessary 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(), and on() maintain their original signatures
  • βœ… New builderFull and listenFull() 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: onComplete executes 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() with await: Need to wait for all handlers (loading states, sequences)
  • dispatchAsync() without await: 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 _HandlerWrapper with callAsync() 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() after dispatch() 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:

  1. βœ… Add cleanup in dispose() for all on() and listen() calls
  2. βœ… Always check if (mounted) before setState() in async handlers
  3. βœ… Or migrate to ActionManagerWidget for 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 #

  • displayLoading parameter in ActionManager.dispatch()
  • LoadingOverlayController class
  • ActionManagerProvider widget
  • 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 of Overlay.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 ActionManagerProvider widget (must be placed inside MaterialApp)
    • Added displayLoading parameter to dispatch() method
    • Added LoadingOverlayController for manual control
    • Automatic show/hide management with try-finally protection
    • Customizable loading widget via loadingBuilder parameter
    • Thread-safe with counter-based concurrent requests handling

πŸ› 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 management
  • displayLoading parameter in ActionManager.dispatch()

4.3.0 - 2026-02-12 #

✨ New Features #

  • Added debug parameter to dispatch() 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.enabled setting

πŸ”§ Changes #

  • Logger now disabled by default - Changed ActionLogger.enabled default from kDebugMode to false
    • 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 debug parameter 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 action parameter to your builder function
    • Before: builder: (context, data) => Text('$data')
    • After: builder: (context, data, action) => Text('$data')

✨ 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 - Use ActionManager.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 Action class to ActionEvent to avoid naming conflicts with Flutter SDK's built-in Action class
  • This change affects all code that directly instantiates or references the Action class

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 to ActionEvent<T>
  • Updated FullActionHandler<T> typedef to use ActionEvent<T>
  • Updated internal references in ActionManager and ActionLogger
  • Updated all tests (48 tests passing)
  • Updated documentation and examples
  • No analysis issues

Fixed #

  • Eliminated potential naming conflicts with Flutter SDK's Action class
  • Improved clarity and semantic meaning of the action event class

2.0.0 - 2026-02-09 #

🚨 BREAKING CHANGES #

  • Renamed ActionDispatcher to ActionManager to avoid naming conflicts with Flutter's built-in ActionDispatcher class
  • Renamed file action_dispatcher.dart to action_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 ActionDispatcher references updated to ActionManager throughout 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
0
likes
160
points
759
downloads

Publisher

unverified uploader

Weekly Downloads

A lightweight, type-safe action dispatcher for Flutter applications. Works seamlessly across all layers - UI, domain, data, and services.

Repository (GitHub)
View/report issues

Topics

#state-management #architecture #events #actions #dispatcher

Documentation

API reference

License

MIT (license)

Dependencies

flutter, meta

More

Packages that depend on leulit_flutter_actionmanager