Skip to main content

useListenableListener

Runs a callback when a Listenable notifies, without rebuilding the hook. This is the side-effect counterpart to useListenable: use it when a change should trigger an action (logging, navigation, a snackbar) rather than new UI.

void useScrollToTopOnReachingEnd(ScrollController controller) {
// A ScrollController is a plain Listenable - it carries no single value, so
// the callback reads what it needs from the controller itself. No rebuild.
useListenableListener(controller, () {
if (controller.offset >= controller.position.maxScrollExtent) {
controller.jumpTo(0); // <- Side effect only; the hook never rebuilds
}
});
}

Signature

void useListenableListener(Listenable? listenable, void Function() block);

The listenable is nullable - passing null subscribes to nothing. The callback takes no arguments, so read whatever you need from the listenable inside it. The subscription is re-created when the listenable reference changes.

Use cases

  • Reacting to a Listenable that carries no single value (a ScrollController, TabController, a custom ChangeNotifier).
  • Side effects on change - showing a snackbar, logging analytics, triggering navigation - where no rebuild is wanted.

Caveats

  • The callback does not cause a rebuild. If you need the new value on screen, use useListenable (or useValueListenable) instead.
  • Do not run the side effect during build - that is exactly what this hook avoids. Notifications arrive after the build, like an effect.

useValueListenableListener

The value-carrying variant for Flutter's ValueListenable<T>. The callback receives the current value, so you do not read it off the source yourself.

void useLogTabChanges(ValueListenable<int> selectedTab) {
// useValueListenableListener passes the current value to the callback.
useValueListenableListener(selectedTab, (index) {
debugPrint('Tab changed to $index'); // <- Runs on every change, no rebuild
});
}

void useLogNameChanges(ListenableValue<String> name) {
// useListenableValueListener is the same, for utopia's ListenableValue
// (e.g. a useState result).
useListenableValueListener(name, (value) {
debugPrint('Name is now $value');
});
}

Signature

void useValueListenableListener<T>(ValueListenable<T>? listenable, void Function(T) block);

Calls block with the listenable's current value on every notification. The source is nullable (null = no subscription); the subscription is re-created when the reference changes.

Use cases

  • Logging or analytics on each value change, with the value passed straight to the callback.
  • Driving a one-off effect (a dialog, navigation, a focus change) off a ValueNotifier<T> or any Flutter ValueListenable<T> without rendering the value itself.

Caveats

  • The callback never rebuilds the hook. When the value needs to appear on screen, reach for useValueListenable instead.
  • Reach for this when you already hold a Flutter ValueListenable<T>. For utopia's ListenableValue<T> (a useState result, a useNotifiableValue), use useListenableValueListener below instead of converting.

useListenableValueListener

The same as useValueListenableListener, but for utopia's ListenableValue<T> instead of Flutter's ValueListenable<T>. Because a useState(...) result is a ListenableValue, this is how you react to another hook's state without rebuilding on it.

Signature

void useListenableValueListener<T>(ListenableValue<T>? listenable, void Function(T) block);

Identical behavior to useValueListenableListener: block receives the current value, the source is nullable, and the subscription tracks the reference.

tip

Three sources, three hooks: useListenableListener for a bare Listenable, useValueListenableListener for a Flutter ValueListenable, useListenableValueListener for a utopia ListenableValue. Pick the one whose type you already hold rather than converting.

Use cases

  • Logging or analytics on each value change.
  • Driving a one-off effect (dialog, navigation, focus change) in response to a value, without the value itself being rendered.

Caveats

  • These never rebuild the hook. Reach for useValueListenable / useListenableValue when the value needs to appear in the UI.

See also

  • useListenable - subscribe and rebuild on change instead of running an effect
  • useNotifiable - the producing side: trigger notifications yourself
  • Common hooks - useEffect and the rest of the everyday hooks