Skip to content

avoid_conditional_hooks

v0.4.0 Warning Widget Best Practices

This rule flags hook calls (useState, useMemoized, useEffect, etc.) that appear inside if statements, ternary expressions, switch cases, or short-circuit operators (&&, ||). Hooks must be called in the exact same order on every build, and conditional execution breaks that guarantee.

The hooks framework tracks state by call order, not by name. If a hook is skipped on one build because a condition is false, every subsequent hook shifts position and reads the wrong state. This leads to bizarre bugs where values swap between hooks or the app crashes with an index-out-of-range error. This is the same “Rules of Hooks” constraint from React.

See also: flutter_hooks | React Rules of Hooks

class MyWidget extends HookWidget {
final bool condition;
@override
Widget build(BuildContext context) {
// Hook called conditionally — order changes between builds
if (condition) {
final value = useMemoized(() => 42);
}
return const Text('Hello');
}
}
class MyWidget extends HookWidget {
final bool condition;
@override
Widget build(BuildContext context) {
// Hook always called, conditional logic inside
final value = useMemoized(() {
if (condition) return 42;
return 0;
});
return Text('$value');
}
}

To disable this rule:

plugins:
many_lints:
diagnostics:
avoid_conditional_hooks: false