Hook<R> class
abstract
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 a 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 restrictions is that HookState are obtained based on their index. So the index must never ever change, or it will lead to undesired behavior.
Usage
Hook is a powerful tool which enables the reuse of 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 in bigger widgets.
Hooks are designed so that they get the benefits of mixins, but are totally independent from each other. This means that hooks can store and expose values without needing to check if 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 functionally 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 a 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
Properties
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.
inherited
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent 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