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.
Why use this rule
Section titled “Why use this rule”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'); }}Configuration
Section titled “Configuration”This rule is in the core preset, so it is on with preset: core,
preset: recommended or preset: opinionated.
To turn it off:
rules: avoid_conditional_hooks: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_unnecessary_hook_widgets— Don’t extend HookWidget if you never call any hooks.avoid_hooks_outside_build— Only call hooks from a hook context.avoid_misused_hooks— Don’t call hooks inside loops.always_pass_global_key— Don’t create a GlobalKey inside build.