Hook<R> class Null safety

Hook is similar to a StatelessWidget, but is not associated to an Element.

A Hook is typically the equivalent of State for StatefulWidget, with the notable difference that a HookWidget can have more than one Hook. A Hook is created within the HookState.build method of HookWidget and the creation must be made unconditionally, always in the same order.

Good:

class Good extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final name = useState("");
    // ...
  }
}

Bad:

class Bad extends HookWidget {
  @override
  Widget build(BuildContext context) {
    if (condition) {
      final name = useState("");
      // ...
    }
  }
}

The reason for such restriction is that HookState are obtained based on their index. So the index must never ever change, or it will lead to undesired behavior.

The usage

Hook is powerful tool to reuse State logic between multiple Widget. They are used to extract logic that depends on a Widget life-cycle (such as HookState.dispose).

While mixins are a good candidate too, they do not allow sharing values. A mixin cannot reasonably define a variable, as this can lead to variable conflicts on bigger widgets.

Hooks are designed so that they get the benefits of mixins, but are totally independent from each others. This means that hooks can store and expose values without fearing that the name is already taken by another mixin.

Example

A common use-case is to handle disposable objects such as AnimationController.

With the usual StatefulWidget, we would typically have the following:

class Usual extends StatefulWidget {
  @override
  _UsualState createState() => _UsualState();
}

class _UsualState extends State<Usual>
    with SingleTickerProviderStateMixin {
  late final _controller = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 1),
  );

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

This is undesired because every single widget that wants to use an AnimationController will have to rewrite this exact piece of code.

With hooks it is possible to extract that exact piece of code into a reusable one.

This means that with HookWidget the following code is equivalent to the previous example:

class Usual extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final animationController = useAnimationController(duration: const Duration(seconds: 1));
    return Container();
  }
}

This is visibly less code then before. But in this example, the animationController is still guaranteed to be disposed when the widget is removed from the tree.

In fact this has secondary bonus: duration is kept updated with the latest value. If we were to pass a variable as duration instead of a constant, then on value change the AnimationController will be updated.

Mixed in types
Annotations

Constructors

Hook({List<Object?>? keys})
Allows subclasses to have a const constructor
const

Properties

hashCode int
The hash code for this object. [...]
read-only, inherited
keys List<Object?>?
A list of objects that specify if a HookState should be reused or a new one should be created. [...]
final
runtimeType Type
A representation of the runtime type of the object.
read-only, inherited

Methods

createState() HookState<R, Hook<R>>
Creates the mutable state for this hook linked to its widget creator. [...]
debugFillProperties(DiagnosticPropertiesBuilder properties) → void
Add additional properties associated with the node. [...]
@mustCallSuper, @protected, inherited
noSuchMethod(Invocation invocation) → dynamic
Invoked when a non-existent method or property is accessed. [...]
inherited
toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) DiagnosticsNode
Returns a debug representation of the object that is used by debugging tools and by DiagnosticsNode.toStringDeep. [...]
inherited
toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) String
A string representation of this object. [...]
inherited
toStringShort() String
A brief description of this object, usually just the runtimeType and the hashCode. [...]
inherited

Operators

operator ==(Object other) bool
The equality operator. [...]
inherited

Static Methods

shouldPreserveState(Hook hook1, Hook hook2) bool
The algorithm to determine if a HookState should be reused or disposed. [...]
use<R>(Hook<R> hook) → R
Register a Hook and returns its value [...]
@Deprecated('Use `use` instead of `Hook.use`')