avoid-misused-hooks

Path
lint/correctness/avoid-misused-hooks
Default
On (recommended)
Origin
Falcon
Rule
89 of 512
recommendedflutter_hooks

Flags confirmed Flutter hooks called directly in non-hook Flutter widget builds.

Widget ancestry must resolve. HookWidget/HookConsumerWidget builds and State builds for StatefulHookWidget/StatefulHookConsumerWidget are excluded, as is unknown ancestry. Custom useX functions must compose resolved flutter_hooks calls; spelling alone is insufficient. Nested callbacks belong to avoid-hooks-outside-build instead.

Invalid

example.dartdart
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_hooks/flutter_hooks.dart' as hooks;

int useFeature() => useState(1);
class Screen extends StatelessWidget {
  Widget build(BuildContext context) {
    useState<int>(0).toString();
    useMemoized(() => 1);
    useFeature();
    for (var i = 0; i < 2; i++) {
      useState(i);
    }
    return const SizedBox();
  }
}
abstract class BaseScreen extends StatelessWidget {}
class DerivedScreen extends BaseScreen {
  Widget build(BuildContext context) {
    hooks.useState(0);
    return const SizedBox();
  }
}
class StatefulScreen extends StatefulWidget {}
class ScreenState extends State<StatefulScreen> {
  Widget build(BuildContext context) {
    hooks.useEffect(() {});
    return const SizedBox();
  }
}
Hooks require a `HookWidget`, `HookConsumerWidget`, or `HookBuilder`; replace this non-hook widget.
7 Widget build(BuildContext context) {
8 useState<int>(0).toString();
Hooks require a `HookWidget`, `HookConsumerWidget`, or `HookBuilder`; replace this non-hook widget.
8 useState<int>(0).toString();
9 useMemoized(() => 1);
Hooks require a `HookWidget`, `HookConsumerWidget`, or `HookBuilder`; replace this non-hook widget.
9 useMemoized(() => 1);
10 useFeature();
Hooks require a `HookWidget`, `HookConsumerWidget`, or `HookBuilder`; replace this non-hook widget.
11 for (var i = 0; i < 2; i++) {
12 useState(i);
Hooks require a `HookWidget`, `HookConsumerWidget`, or `HookBuilder`; replace this non-hook widget.
19 Widget build(BuildContext context) {
20 hooks.useState(0);
Hooks require a `HookWidget`, `HookConsumerWidget`, or `HookBuilder`; replace this non-hook widget.
26 Widget build(BuildContext context) {
27 hooks.useEffect(() {});

Valid

example.dartdart
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

int useBusiness() => 2;
void ordinary() { useState(0); } // Outside-build rule owns this.
class Screen extends HookWidget {
  Widget build(BuildContext context) {
    useState<int>(0);
    return const SizedBox();
  }
}
class RiverScreen extends HookConsumerWidget {
  Widget build(BuildContext context, WidgetRef ref) {
    useMemoized(() => 1);
    return const SizedBox();
  }
}
class NonHookScreen extends StatelessWidget {
  Widget build(BuildContext context) {
    useBusiness(); // A custom useX name alone is not a hook.
    final callback = () { useState(0); }; // Outside-build owns callbacks.
    return HookBuilder(builder: (context) {
      useState(1);
      return const SizedBox();
    });
  }
}
class Service {
  int build() => useState(0); // Not a Flutter widget.
}
class ShadowedScreen extends StatelessWidget {
  final int Function(int) useState;
  ShadowedScreen(this.useState);
  Widget build(BuildContext context) {
    useState(0);
    return const SizedBox();
  }
}
class StatefulScreen extends StatefulWidget {}
class ScreenState extends State<StatefulScreen> {
  void event() { useState(0); } // Not a build method.
}

class StatefulHooks extends StatefulHookWidget {}
class HooksState extends State<StatefulHooks> {
  Widget build(BuildContext context) {
    useState(0);
    return const SizedBox();
  }
}
class StatefulRiverHooks extends StatefulHookConsumerWidget {}
class RiverHooksState extends ConsumerState<StatefulRiverHooks> {
  Widget build(BuildContext context) {
    useState(0);
    return HookConsumer(builder: (context, ref, child) {
      useState(1);
      return const SizedBox();
    });
  }
}
class UnknownScreen extends MissingHookBase {
  Widget build(BuildContext context) {
    useState(0);
    return const SizedBox();
  }
}
class UnknownState extends State<dynamic> {
  Widget build(BuildContext context) {
    useState(0);
    return const SizedBox();
  }
}

Configure avoid-misused-hooks

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "correctness": {
        "avoid-misused-hooks": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/correctness/avoid-misused-hooks: <reason>