Skip to main content

useWithSelf

Builds a value that needs a stable reference to itself. The block receives a Value holding the very object it returns, so callbacks inside it can reach the finished result without a chicken-and-egg problem.

class StepperView extends HookWidget {
const StepperView({super.key});


Widget build(BuildContext context) {
final count = useState(0);

// The controller needs to call back into itself to reset.
final controller = useWithSelf<StepController>(
(self) => StepController(
increment: () => count.value++,
reset: () => self.value.increment(), // <- self.value is this StepController
),
);

return Column(
children: [
Text('${count.value}'),
TextButton(onPressed: controller.increment, child: const Text('+1')),
],
);
}
}

class StepController {
final void Function() increment;
final void Function() reset;

StepController({required this.increment, required this.reset});
}

Signature

T useWithSelf<T extends Object>(T Function(Value<T> self) block);
T useWithSelfOrNull<T>(T Function(Value<T?> self) block);
  • block - builds and returns the value. The self argument is a Value whose .value resolves to the object block returns once construction finishes.
  • The block runs on every build, and self.value is reassigned to the latest result each time, so callbacks always see the current instance.

useWithSelf requires a non-nullable T and provides a Value<T>. useWithSelfOrNull allows a nullable result and provides a Value<T?> initialized to null - use it when the type isn't Object or when a null self is meaningful.

Use cases

  • Building an object whose own methods or callbacks need to refer back to it (e.g. a controller that resets or re-emits itself)
  • Wiring a callback that, by the time it runs, should operate on the fully-constructed value rather than a half-built one

Caveats

  • Do not read self.value during construction (synchronously inside block) - it is only assigned after block returns. Reading it eagerly with useWithSelf throws (the holder is late-initialized); with useWithSelfOrNull it yields null. Read it only from callbacks that run later.

  • useWithSelf constrains T extends Object because its holder is a late, non-nullable Value. If T can be null, use useWithSelfOrNull.

  • The block runs on every build. Keep it cheap, and move any expensive one-off construction into a useMemoized inside the block.

See also

  • useMemoized - the caching primitive behind this hook; use it for one-time construction
  • useState - when the object does not need a self-reference
  • useValueWrapper - the related pattern of a stable Value holder refreshed each build