---
description: "Hooks + Riverpod conventions for {{PROJECT_NAME}}"
alwaysApply: true
---

# Hooks + Riverpod Standards — {{PROJECT_NAME}}

## Widget base classes
- `HookConsumerWidget` — when you need BOTH hooks and Riverpod providers
- `HookWidget` — when you need ONLY hooks (no Riverpod)
- `ConsumerWidget` — when you need ONLY Riverpod (no hooks)

## Hook rules
```dart
class ProductSearchWidget extends HookConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // Hooks at the TOP of build() — never inside conditions or loops
    final searchQuery  = useState('');
    final searchCtrl   = useTextEditingController();
    final focusNode    = useFocusNode();
    final debounced    = useDebounced(searchQuery.value, const Duration(milliseconds: 300));

    // Riverpod below hooks
    final results = ref.watch(searchResultsProvider(debounced));

    return // ...
  }
}
```

## What goes in hooks vs providers
| Concern | Tool |
|---------|------|
| Local UI state (text controller, animation, focus) | `useState`, `useAnimationController` |
| Server/async state | Riverpod `AsyncNotifier` |
| Cross-widget/feature state | Riverpod providers |
| Lifecycle side effects | `useEffect` |

## Rules
- **NEVER** call hooks inside `if`, `for`, or callbacks
- `useEffect` cleanup MUST return a dispose function
- Custom hooks: prefix with `use`, live in `lib/core/hooks/`
- Do not use `flutter_riverpod` `ref.watch` inside `useEffect` — use `useRef` + `ref.listen`
