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 auseState. - Exposing a
Listenablefrom a hook so child widgets can subscribe to coarse "something changed" events.
Caveats
-
Reach for
useStatefirst. It stores the value, rebuilds on assignment, and skips rebuilds when the value is unchanged.useNotifiableis 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 (anawaited result, a timer), usenotifyIfMounted()instead.final notifiable = useNotifiable();// DON'TFuture<void> onLoaded() async {await service.load();notifiable.notify(); // <- Throws if the hook was disposed during the await}// DOFuture<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
useStaterebuilds do not fit because the object mutates internally.
Caveats
-
.valueis 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, useuseState. -
The same unmount rule applies: prefer
notifyIfMounted()in callbacks that can outlive the hook.
See also
- useListenable - the consuming side; subscribe to the
ListenableValuethis produces - useListenableListener - react to changes with a side effect instead of a rebuild
- Local state - when to use
useStateversus a hand-managed notifier