Skip to main content

useNotifiable

Creates a rebuild trigger you fire by hand. Where useListenable subscribes to something external, useNotifiable is the producing side: it owns a notifier and rebuilds the hook when you call notify(). It stores no value - use it when the data lives in a mutable object you read directly and you just need to signal "this changed, rebuild".

class StopwatchState {
final Duration elapsed;
final void Function() onLap;

const StopwatchState({required this.elapsed, required this.onLap});
}

StopwatchState useStopwatchState() {
final stopwatch = useMemoized(Stopwatch.new, []);
final notifiable = useNotifiable(); // <- A rebuild trigger with no stored value

return StopwatchState(
elapsed: stopwatch.elapsed, // read directly from the mutable object
onLap: () {
stopwatch.start();
notifiable.notify(); // <- Rebuild so the new elapsed value is shown
},
);
}

Signature

NotifiableHookState useNotifiable({bool listen = true});

Returns a NotifiableHookState - both a Notifiable (call notify() to rebuild) and a Listenable (children can subscribe to it). With listen: false the host hook is not rebuilt on notify(), but external listeners are still notified - useful when only a child should react.

Use cases

  • Signalling that a mutable object read elsewhere has changed (a Stopwatch, a manually managed buffer) without copying its state into a useState.
  • Exposing a Listenable from a hook so child widgets can subscribe to coarse "something changed" events.

Caveats

  • Reach for useState first. It stores the value, rebuilds on assignment, and skips rebuilds when the value is unchanged. useNotifiable is for the cases where the state genuinely lives in a mutable object you would rather not duplicate.

  • Calling notify() after the hook has been unmounted throws in debug mode. In a callback that may run after dispose (an awaited result, a timer), use notifyIfMounted() instead.

    final notifiable = useNotifiable();

    // DON'T
    Future<void> onLoaded() async {
    await service.load();
    notifiable.notify(); // <- Throws if the hook was disposed during the await
    }

    // DO
    Future<void> onLoaded() async {
    await service.load();
    notifiable.notifyIfMounted(); // <- No-op if already unmounted
    }

useNotifiableValue

Like useNotifiable, but it also holds a value built once on first build. The returned state is a ListenableValue<T>, so it can be read locally and handed to a child via useListenableValue. You mutate the held object in place, then call notify() (or notifyIfMounted()) to rebuild.

class CartState {
final int itemCount;
final void Function(String) onAdd;

const CartState({required this.itemCount, required this.onAdd});
}

CartState useCartState() {
// create() runs once; `state` holds a mutable Set and is itself a Listenable
// value you could hand to a child via useListenableValue.
final state = useNotifiableValue(() => <String>{});

return CartState(
itemCount: state.value.length,
onAdd: (id) {
state.value.add(id); // mutate the held object in place
state.notifyIfMounted(); // <- Rebuild; safe if the hook is already disposed
},
);
}

Signature

NotifiableValueHookState<T> useNotifiableValue<T>(T Function() create, {bool listen = true});

create runs once on the first build to produce the held value; it is not re-run on later builds. listen behaves as in useNotifiable. Read the value through .value.

Use cases

  • Owning a long-lived mutable object (a collection, a builder, a domain aggregate) that you mutate in place and want to expose as a ListenableValue.
  • Producing a value other hooks or child widgets subscribe to, where assignment-based useState rebuilds do not fit because the object mutates internally.

Caveats

  • .value is created once and the reference never changes - mutate the object it points to, do not expect reassigning a field to swap it. If you want assignment-driven, equality-checked rebuilds, use useState.

  • The same unmount rule applies: prefer notifyIfMounted() in callbacks that can outlive the hook.

See also

  • useListenable - the consuming side; subscribe to the ListenableValue this produces
  • useListenableListener - react to changes with a side effect instead of a rebuild
  • Local state - when to use useState versus a hand-managed notifier