- Status: Accepted
- Date: 2026-05-08
- Deciders: Flutter team
Most "stateful" widgets in this app need lightweight local concerns: a TextEditingController, a ScrollController, a useEffect to subscribe/unsubscribe, an animation controller. StatefulWidget answers all of these but with a verbose State<T> class, manual initState / dispose, and lifecycle bugs (forgot to dispose, late init order).
flutter_hooks collapses all four into composable hooks (useTextEditingController, useScrollController, useEffect, useAnimationController) with automatic disposal. Domain state already lives in MobX; widget-local concerns rarely justify a full State<T> class.
- Less ceremony for common patterns (controllers, effects).
- Automatic resource disposal — no leaked controllers.
- Composable extraction — custom hooks vs custom
State<T>subclass.
HookWidgetas default for any non-pure widget;StatelessWidgetfor pure presentation.StatefulWidgetalways; ban hooks.- Mix freely with no policy.
Chosen option: HookWidget default; StatelessWidget for pure presentation.
- Pure presentation (no controllers, no effects, no local mutable state) →
StatelessWidget. - Anything that needs a controller, an effect, a local
useState, or animation →HookWidget. StatefulWidgetis allowed only when interacting with APIs that genuinely need aState<T>lifecycle method not available as a hook (rare).
class _Content extends HookWidget {
@override
Widget build(BuildContext context) {
final controller = useTextEditingController();
useEffect(() {
final sub = stream.listen(_handle);
return sub.cancel;
}, const []);
return TextField(controller: controller);
}
}- Good: shorter widget files; no manual disposal bugs.
- Good: extractable hooks let multiple widgets share lifecycle logic without inheritance.
- Good: matches the codebase's existing widgets — see Quick Navigation in CLAUDE.md.
- Bad:
flutter_hooksadds a dependency and a small learning curve foruseEffectkeys / dependencies. - Bad: stack traces include hook-internal frames when a hook misuse triggers a runtime error.
- Good: terse, automatic disposal, composable.
- Bad: extra dependency; one more concept to learn.
- Good: zero added dependencies.
- Bad: verbose; manual
disposeis bug-prone; controller setup boilerplate everywhere.
- Bad: two patterns for the same thing in adjacent files; reviewers can't tell which to use.