prefer_use_callback
v0.4.0 Warning Fix Hook Rules
This rule flags useMemoized(() => someFunction) — a useMemoized whose factory returns a function. useCallback exists for exactly that, and the quick fix swaps it in.
Why use this rule
Section titled “Why use this rule”useMemoized(() => () { ... }) has two layers of closure and only one of them means anything: the outer one is ceremony the hook demands, the inner one is the callback you wanted. useCallback takes the callback directly, so what you read is what gets memoised.
See also: flutter_hooks — useCallback
import 'package:flutter/material.dart';import 'package:flutter_hooks/flutter_hooks.dart';
class SubmitButton extends HookWidget { const SubmitButton({super.key});
@override Widget build(BuildContext context) { // A closure wrapped in a closure final onPressed = useMemoized( // LINT () => () { debugPrint('submitted'); }, );
return ElevatedButton(onPressed: onPressed, child: const Text('Submit')); }}class SubmitButton extends HookWidget { const SubmitButton({super.key});
@override Widget build(BuildContext context) { final onPressed = useCallback(() { debugPrint('submitted'); }, const []);
return ElevatedButton(onPressed: onPressed, child: const Text('Submit')); }}A returned tear-off counts too
Section titled “A returned tear-off counts too”// Don'tfinal onTap = useMemoized(() => _handleTap); // LINT
// Dofinal onTap = useCallback(_handleTap, const []);useMemoized is right for non-function values
Section titled “useMemoized is right for non-function values”Nothing is reported when the factory returns data — that is what the hook is for:
// Not reportedfinal rows = useMemoized( () => items.map((i) => i.toUpperCase()).toList(), [items],);
final total = useMemoized(() => items.length * 2, [items]);Known limitations
Section titled “Known limitations”Only a single-expression or single-return factory is checked. A factory that does other work before returning the closure is left alone, even though useCallback may still fit:
// Not reported — the block has more than one statementfinal onTap = useMemoized(() { final label = title.toUpperCase(); return () => debugPrint(label);});The hook is matched by name. useMemoized and _useMemoized are recognised wherever they are declared; a memoisation helper under another name is not.
The factory must be written inline. useMemoized(buildCallback), passing an existing function rather than a literal closure, is not inspected.
Configuration
Section titled “Configuration”This rule is in the opinionated preset, so it is on with
preset: opinionated, or by name:
rules: prefer_use_callback: trueTo turn it off again:
rules: prefer_use_callback: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”prefer_use_prefix— Custom hooks should start with the ‘use’ prefix.avoid_hooks_outside_build— Only call hooks from a hook context.avoid_misused_hooks— Don’t call hooks inside loops.avoid_throw_in_fp_callback— A throw inside an fpdart callback escapes the error channel the pipeline is built to carry.