pkg_state_management version: 1.7.9

Published 2026-09-22

ruud

sdk | dart flutter
repository | svn
platform | generic
status | n/a
  • Readme
  • Changelog
  • Versions

pkg_state_management

Scoped, widget-based state management inspired by provider.

graph

For ChangeNotifier based data models that can trigger updates for your UI, or just plain objects for dependency injection.

More or less backwards compatible with the provider package. While it is (still) possible to use Provider and ChangeNotifierProvider from that package, it is not recommended to do so.

If you want to provide static resources like (singleton) service clients, settings or otherwise, use a service locator like GetIt.

pkg_state_management

Model

Models must all extend or implement ChangeNotifier.

class Model extends ChangeNotifier {
  late Timer _timer;
  late DateTime _dateTime;

  Model() : super() {
    _timer = Timer.periodic(const Duration(seconds: 1), _ON_Timer);
  }

  DateTime get datetime => _dateTime;

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }

  void _ON_Timer(final Timer timer) {
    _dateTime = DateTime.now();
    notifyListeners();
  }
}

Provider

MyProvider<Model>(
  create: (final _) => Model(),
  child: ...,
),

Children will not update when the model triggers if they are not wrapped with a consumer.

MyProvider<Model>(
  create: (final _) => Model(),
  child: Text('Will not update: ${DateTime.now().toIso8601String()}'),
),

Static models

Note that when the provider is disposed, the model is also disposed (if the model is a ChangeNotifier), (unless you set shouldDispose: false) so if you construct your provider from a static resource like create: (final _) => GetIt.instance.get<Model>(), the resource is not valid anymore. Should not be a problem if the dispose happens when the application exits.

MyProvider<Model>(
  create: (final _) => GetIt.instance.get<Model>(),
  shouldDispose: false,
  child: Text('Will not update: ${DateTime.now().toIso8601String()}'),
),

Providing an interface

Providing a model through an interface will also work:

MyProvider<ISomeModelInterface>(
  create: (final _) => SomeModelImpl(),
  child: ...
),

Multiple providers

Use MyMultiProvider to provide multiple providers. The providers must not have a child widget, while the multi-provider must have one.

MyMultiProvider(
  providers: <SingleChildWidget>[
    MyProvider<Model>(
      create: (final _) => Model(),
    ),
    MyProvider<OtherModel>(
      create: (final _) => OtherModel(),
    ),
    MyProvider<ISomeModelInterface>(
      create: (final _) => SomeModelImpl(),
    ),
    ...
  ],
  child: Wrap(
    children: <Widget>[
      Text('Will not update: ${DateTime.now().toIso8601String()}'),
    ],
  ),
),

Consumer

Always update

Wrap your widget in a MyConsumer regardless of what model properties are changed:

MyConsumer<Model>(
  builder: (context, model, child) =>
    Text(DateTime.now().toIso8601String()),
),

Never update

MyConsumer<Model>(
  shouldUpdate: false,
  builder: (context, model, child) =>
    Text(DateTime.now().toIso8601String()),
),

Selector

Always update

Wrap your widget in a MySelector regardless of what model properties are changed:

MySelector<Model, void>(
  builder: (context, model, select, child) =>
    Text(DateTime.now().toIso8601String()),
),

Specific property

When a specific property changes:

MySelector<Model, DateTime>(
  select: (current) => current.datetime,
  builder: (context, model, select, child) =>
    Text(select.toIso8601String()),
),

Conditional

When a condition is applied to a selected property:

MySelector<Model, DateTime>(
  select: (current) => current.datetime,
  shouldUpdate: (previous, current) =>
      current.second.isEven,
  builder: (context, model, select, child) =>
      Text(select.toIso8601String()),
),

Records

When a select returns a record, it is advised to implement the shouldUpdate callback, because the select will always return a new object and thus the selector always triggers:

MySelector<Model, ({bool busy, bool done})>(
  select: (current) => (busy: current.busy, done: current.done),
  shouldUpdate: (previous, current) =>
      previous.busy != current.busy || previous.done != current.done,
  builder: (context, model, select, child) =>
      model.done ? Text('done') : Text(select.busy ? 'busy' : 'idle'),
),

Changelog

1.7.9 - 2026-09-22

Changed

  • Version bump from 1.7.8 to 1.7.9 (material_ui -> 1.4.0 (was 1.3.0)).

1.7.8 - 2026-09-16

Changed

  • Version bump from 1.7.7 to 1.7.8 (material_ui -> 1.3.0 (was 1.2.0)).

1.7.7 - 2026-09-09

Changed

  • Version bump from 1.7.6 to 1.7.7 (material_ui -> 1.2.0 (was 1.1.1)).

1.7.6 - 2026-09-05

Changed

  • Version bump from 1.7.5 to 1.7.6 (lint -> 2.14.0 (was 2.8.0)).

1.7.5 - 2026-09-03

Changed

  • Version bump from 1.7.4 to 1.7.5 (material_ui -> 1.1.1 (was 1.1.0)).

1.7.4 - 2026-08-29

Changed

  • Version bump from 1.7.3 to 1.7.4 (flutter 3.47.2, dart 3.13.2, serverpod 3.4.13).

1.7.3 - 2026-08-25

Changed

  • Version bump from 1.7.2 to 1.7.3 (material_ui -> 1.1.0 (was 1.0.1)).

1.7.2 - 2026-08-06

Breaking

  • Reverted ConsumerBuilder1/2/3/4 and SelectorBuilder1/2/3/4 typedef return types from Widget back to dynamic, restoring support for non-Widget return values (e.g., DataRow for DataTable). Returning a non-renderable object (including null) compiles silently but crashes at runtime.
  • Changed HHSelector (and variants 2–4) shouldUpdate predicate signature from bool Function(S? previous, S current)? to bool Function(S previous, S current)? — previous is no longer nullable; it is always initialised before the first _ON_Change fires.

Added

  • HHProvider: added optional onCreated: void Function(T)? callback, invoked immediately after the model is created.
  • HHProvider: added optional onDisposed: void Function()? callback, invoked immediately after the model is disposed.
  • HHSelector (and variants 2–4): builder parameter is now optional (nullable); the widget renders SizedBox.shrink() when builder is null, enabling use with onUpdate-only patterns.

Changed

  • AGENTS.md: updated Builder Type Safety convention to reflect the dynamic typedef; updated Selector correctness description to document the eager _previous update in _ON_Change and the capturedPrev capture.
  • SKILL.md: added Installation section, isDisposed/isNotDisposed property table, onCreated/onDisposed callback example, HHProvider.of<T>()/maybeOf<T>() static method reference table, and a Gotchas and Common Mistakes section. Fixed stray embedded line numbers in Step 5.

Fixed

  • HHSelector (and variants 2–4): _disposeUpdateHandler now removes _ON_Change unconditionally regardless of widget.shouldUpdate, closing the dangling-listener window when a parent rebuilds with shouldUpdate: false after it was true.

Tests

  • Added null-builder tests for HHSelector, HHSelector3, and HHSelector4 ("renders SizedBox.shrink when builder is null").
  • Expanded hh_selector_rapid_changes_test.dart with additional rapid-change transition scenarios for all selector variants.
  • Expanded hh_selector_3_4_test.dart with null-builder and shouldUpdate coverage for HHSelector3 and HHSelector4.
  • Expanded hh_selector_test.dart with non-nullable shouldUpdate predicate cases.

1.7.1 - 2026-08-03

Changed

  • Improved documentation on all ConsumerBuilder and SelectorBuilder typedefs: absorbed the loose inline comment into each typedef's doc, corrected "Must return a Widget" to accurately reflect the generic R return type, and added type parameter descriptions ([R], [T], [T1]/[T2]/…, [S]).

1.7.0 - 2026-08-03

Fixed

  • Fixed HHSelector variants (1–4): _updateNotifier now captures the selected value at the moment _ON_Change fires (capturedSelect) instead of reading _select from the instance field inside the deferred closure. Previously, two rapid consecutive model changes before the microtask queue drained caused onUpdate to report (A, C) for the first notification instead of (A, B).
  • Fixed context.watch<T>(): now mirrors HHProvider.of by checking widget presence via getInheritedWidgetOfExactType before throwing, so providers whose value is null are not incorrectly treated as absent when T is a nullable type.
  • Fixed HHConsumer variants (1–4): _disposeUpdateHandler now removes _ON_Change unconditionally, regardless of widget.shouldUpdate. Previously a runtime change of shouldUpdate from true to false could leave a dangling listener attached to the model.
  • Fixed HHSelector variants (1–4): added _initialized bool flag so debugFillProperties no longer accesses _select or _previous before didChangeDependencies has initialised them, preventing LateInitializationError in the Flutter Widget Inspector.
  • Fixed ObjectNotChangeNotifierException constructor doc: was incorrectly labelled "ProviderNotFound".

Changed

  • Changed all ConsumerBuilder and SelectorBuilder typedefs from dynamic to Widget return type, restoring compile-time type safety for builder callbacks.
  • Expanded futureWrapper doc comment with an execution-timing diagram, design rationale comparing microtasks to addPostFrameCallback, and an explanation of the async while drainer.

Added

  • Added library-level doc comment to pkg_state_management.dart covering all key classes, context extensions, and utilities.
  • Added comprehensive documentation to ConsumerBuilder/SelectorBuilder typedefs, HHValueListenableBuilder, HHValueNotifier, Selector<T, S>, and HHValueSelectorBuilder class.
  • Added class-level doc to _HHOptionalConsumerState.
  • Added AGENTS.md and SKILL.md entries for Selector<T, S> typedef, corrected HHValueSelectorBuilder widget type (was StatelessWidget, is StatefulWidget), documented nullable-T watch correctness, capturedSelect invariant, and unconditional listener cleanup.
  • Added test files: hh_consumer_3_4_test.dart, hh_selector_3_4_test.dart, hh_selector_rapid_changes_test.dart (regression tests for the capturedSelect fix across all selector variants), hh_value_selector_builder_3_4_test.dart, read_context_nullable_test.dart (regression tests for the nullable-T watch fix). Total test count: 78 → 135.

1.6.0 - 2026-07-28

Changed

  • Optimized HHProvider.maybeOf lookup performance (O(1) common case) using manual stack trace scanning.
  • Optimized HHChangeNotifier stack trace parsing for debug logging using high-performance string scanning.
  • Optimized HHSelector variants (1-4) by caching selection results immediately during notification to eliminate redundant re-evaluations during microtask build phase.
  • Refactored all HHConsumer and HHSelector variants (1-4) to use context.watch<T>() and instance comparison in didChangeDependencies for reactive model swap support.
  • Enabled updateShouldNotify in _HHProvider to trigger dependent updates only when the model instance identity changes.

Fixed

  • Fixed reactive model swapping: widgets now correctly detach listeners from old model instances and attach to new ones when a provider's model is replaced.
  • Fixed HHValuesNotifier implementation to follow IDisposable pattern and documented lifecycle safety to prevent memory leaks.
  • Fixed various type-safety issues and improved documentation across the entire library.

Added

  • Added comprehensive unit and widget tests for model swapping, provider lifecycle, and performance optimizations.

1.5.5 - 2026-07-23

Changed

  • Allow context.read and context.maybeRead from initState after setting the allow flag to true.
  • Changed didChangeDependencies initializers to initState.

1.5.4 - 2026-07-09

Changed

  • Changed 'Widget Function' back to 'dynamic Function' on widget builders.

1.5.3 - 2026-07-08

Changed

  • Changed all ConsumerBuilder and SelectorBuilder typedefs from dynamic return type to Widget.
  • Changed context.read<T>() to use HHProvider.of<T>() for proper nullable type handling.
  • Changed context.watch<T>() to use dependOnInheritedWidgetOfExactType directly.
  • Changed HHOptionalConsumer to check for inherited widget presence instead of using maybeRead.
  • Changed HHProvider.of<T>() to check widget presence rather than value nullability, fixing false "not found" errors for nullable types.
  • Changed HHProvider to call onDisposed callback after successful disposal (was before).
  • Changed HHProvider.maybeOf to optimize stack trace inspection by joining only top 10 lines once.
  • Changed HHValueSelectorBuilder from StatelessWidget to StatefulWidget to properly track selected values and only rebuild when selection changes.
  • Changed HHValuesNotifier to use internal ValueNotifier for listener management and proper disposal.

Fixed

  • Fixed HHChangeNotifier.dispose() to call super.dispose() only after clearing debouncers and marking as disposed.
  • Fixed HHChangeNotifier.notifyListeners() to check _disposed before calling super.notifyListeners().
  • Fixed HHChangeNotifier._contextOf() to guard against out-of-bounds access when stack trace is too short.
  • Fixed HHConsumer variants (1-4) to capture current model(s) at scheduling time to prevent stale callbacks after provider swap.
  • Fixed HHSelector variants (1-4) to simplify value incrementing logic and re-evaluate selection inside microtask to guard against model bouncing.
  • Fixed HHSelector variants (1-4) to update _previous after notification instead of before.
  • Fixed futureWrapper to execute each callback inside try/catch to prevent one failure from blocking remaining callbacks.

Added

  • Added clearPendingMicrotasks() helper function for test cleanup.
  • Added comprehensive unit tests for core components (HHChangeNotifier, HHProvider, HHConsumer, HHSelector, HHValueSelectorBuilder, HHValuesNotifier, futureWrapper).

1.5.2 - 2026-04-15

Removed

  • Dependency of pkg_core_flutter for the lints file only that caused a circular dependency.

1.5.1 - 2026-04-10

Changed

  • Wrap future microtasks.

1.5.0 - 2026-04-09

Added

  • HHValueNotifier (alias for ValueNotifier).
  • HHValueSelectorBuilder for selective building.
  • HHValueListenableBuilder (2, 3, 4) (alias for ValueListenableBuilder).

1.4.6 - 2026-03-25

Changed

  • Cache isWeb and isDebugMode lookups.

1.4.5 - 2025-11-13

Changed

  • Reinstated future calls (to prevent state updates within widget builders).

1.4.4 - 2025-11-11

Changed

  • Do not inspect huge stack trace strings.

1.4.3 - 2025-11-08

Changed

  • Removed future calls.

1.4.2 - 2025-09-03

Fixed

  • Be sure to create a provider only once.
  • Unawaited futures.

1.4.1 - 2025-07-10

Added

  • Context maybeWatch and watch (like maybeRead and read, but with update hooks).

1.4.0 - 2025-06-30

Changed

  • Provider maybeOf:context.dependOnInheritedWidgetOfExactType<_HHProvider<T>>()?.value; to maybeOf:context.getInheritedWidgetOfExactType<_HHProvider<T>>()?.value; because dependOnInheritedWidgetOfExactType registers a dependency on a particular type by calling this method, and getInheritedWidgetOfExactType does not (our consumers handle everything with listeners, not implicit updates).

1.3.9 - 2025-06-16

Added

  • A logger in the HHChangeNotifier to show notifyListeners calls in debug mode.

1.3.8 - 2025-06-12

Changed

  • Made the builder of HHConsumer optional, renders const SizedBox() if not given. This way you can handle changes in the onChange callback only, while not rendering any widget.

1.3.7 - 2025-05-28

Changed

  • Version bump from 1.3.6 to 1.3.7 (flutter_lints -> 6.0.0 (was 5.0.0)).

1.3.6 - 2025-05-16

Fixed

  • HHValuesNotifier, internal val now private.

1.3.5 - 2025-05-09

Changed

  • Refactored some construct from if value != null ? something() : otherwise() to something?.call() ?? otherwise().
  • Selectors update _previous after the notifier trigger and onChange, otherwise previous and current would always be the equal since the trigger is within a Future.microtask call.

1.3.4 - 2025-05-01

Added

  • HHValuesNotifier.

1.3.3 - 2025-04-24

Changed

  • Do not throw but log only when notifyListeners is called from within a setState call.

1.3.2 - 2025-04-12

Removed

  • material dependency.

1.3.1 - 2025-04-08

Changed

  • Always use Future<void>.microtask in consumers and selectors.

1.3.0 - 2025-04-08

Added

  • HHOptionalConsumer that renders its child only if the requested provider is available.

1.2.9 - 2025-04-05

Changed

  • Version bump from 1.2.8 to 1.2.9 (Fixed provider lints).

1.2.8 - 2025-04-04

Changed

  • Reinstated Future<void>.microtask for consumers and selectors.

1.2.7 - 2025-03-20

Changed

  • Clarified the usage of a HHSelector with records in README.md.

1.2.6 - 2025-02-20

Fixed

  • Clear notifyListenersDebounced from HHChangeNotifier when disposed.

1.2.5 - 2024-12-19

Changed

  • Version bump from 1.2.4 to 1.2.5 (Support Apple silicon (arm64)).

1.2.4 - 2024-11-05

Changed

  • HHChangeNotifier, super.notifyListeners in a microtask.
  • Moved provider create call to Provider.didChangeDependencies from Provider.initState so a provider constructor can lookup other providers by using context.read<YourProvider>() because that was not possible from initState.

1.2.3 - 2024-11-01

Changed

  • Removed SingleChildWidget export in separate library file.

1.2.2 - 2024-10-10

Changed

  • Changed all BuildContext _ parameters to BuildContext context.
  • Removed futures from notification triggers.

1.2.1 - 2024-10-08

Added

  • HHChangeNotifier::notifyListenersDebounced.

1.2.0 - 2024-09-02

Added

  • HHChangeNotifier that blocks notifications when the model is disposed.

1.1.7 - 2024-08-22

Changed

  • Version bump from 1.1.6 to 1.1.7 (SDK update 3.5.1).

1.1.6 - 2024-08-12

Added

  • pkg_core interface for IDisposable.

1.1.5 - 2024-07-09

Changed

  • Do not use context from a Builder.

1.1.4 - 2024-06-23

Changed

  • HHProvider without Builder in buildWithChild.

1.1.3 - 2024-02-01

Changed

  • Version bump from 1.1.2 to 1.1.3 (Resolve package version confusions).

1.1.2 - 2023-12-04

Fixed

  • Update state after unmount.

1.1.1 - 2023-12-01

Changed

  • Moved onChange callback to notifier function, if debounced gets triggered once.

1.1.0 - 2023-11-09

Changed

  • Removed provider dependency.

1.0.5 - 2023-10-05

Changed

  • Dispose debouncer and change handlers when a didChangeDependencies was triggered.
  • Removed microtask in consumer updaters.

1.0.4 - 2023-07-24

Changed

  • select property of HHSelector is now mandatory.
  • Providers must be present for consumers and selectors.
  • Changed dynamic internals are now typed S.

1.0.3 - 2023-07-21

Added

  • Flag to HHProvider.of and HHProvider.maybeOf to allow calling from suspicious contexts.

1.0.2 - 2023-06-27

Changed

  • Update consumers with Future.microtask.

1.0.1 - 2023-05-09

Changed

  • Triggers are now ints.

Added

  • onUpdate callbacks to consumers.

1.0.0 - 2023-04-13

Changed

  • Renamed all My to HH tokens in class names.

0.0.11 - 2023-02-28

Changed

  • Update consumer only when mounted.

0.0.10 - 2023-02-15

Changed

  • The result of the builder functions is now dynamic (not Widget), in case your builder must return for example a DataRow for a PlutoGrid.

0.0.9 - 2023-02-15

Removed

  • MyChangeNotifier.

0.0.8 - 2023-02-09

Changed

  • Version bump from 0.0.7 to 0.0.8 (Version confusion after compiler bug chaos).

0.0.7 - 2023-01-19

Added

  • MySelector4 and MyConsumer4.
  • onUpdate callback on MyConsumer.

Changed

  • Split MyConsumer into separate MySelector for conditional updating.

Fixed

  • shouldUpdate, previous on the first try should be null, otherwise we're missing the initial update.

0.0.6 - 2023-01-19

Added

  • Consumers for more than one model.

0.0.5 - 2023-01-19

Changed

  • Models are not forced to be of type ChangeNotifier anymore.

0.0.4 - 2023-01-18

Changed

  • Typos in README.md.

0.0.3 - 2023-01-18

Changed

  • Removed doc from .pubignore.

0.0.2 - 2023-01-18

Changed

  • README.md link to screenshot.

0.0.1 - 2023-01-18

Changed

  • Initial version.

1.7.9

2026-09-22 download

1.7.8

2026-09-16 download

1.7.7

2026-09-09 download

1.7.4

2026-08-29 download

1.5.5

2026-07-23 download

1.5.4

2026-07-09 download

1.5.2

2026-04-15 download

1.5.1

2026-04-10 download

1.4.6

2026-03-25 download

1.4.5

2025-11-13 download

1.4.4

2025-11-11 download

1.4.3

2025-11-08 download

1.4.2

2025-09-03 download

1.4.1

2025-07-10 download

1.4.0

2025-06-30 download

1.3.9

2025-06-16 download

1.3.8

2025-06-12 download

1.3.7

2025-05-28 download

1.3.6

2025-05-16 download

1.3.5

2025-05-09 download

1.3.3

2025-04-24 download

1.3.2

2025-04-12 download

1.2.9

2025-04-05 download

1.2.8

2025-04-04 download

1.2.7

2025-03-20 download

1.2.6

2025-02-20 download

1.2.5

2024-12-19 download

1.2.4

2024-11-05 download

1.2.3

2024-11-01 download

1.2.2

2024-10-10 download

1.2.0

2024-09-02 download

1.1.7

2024-08-22 download

1.1.6

2024-08-12 download

1.1.5

2024-07-09 download

1.1.4

2024-06-23 download

1.1.3

2024-02-01 download

1.1.2

2023-12-04 download

1.1.0

2023-11-09 download

1.0.5

2023-10-05 download

1.0.4

2023-07-24 download

1.0.2

2023-06-27 download

1.0.1

2023-05-09 download

1.0.0

2023-04-13 download

0.0.11

2023-02-28 download

0.0.10

2023-02-15 download

0.0.8

2023-02-09 download

0.0.7

2023-01-19 download

0.0.4

2023-01-18 download

0.0.3

2023-01-18 download

0.0.2

2023-01-18 download

0.0.1

2023-01-18 download