45 lines
1.5 KiB
Cheetah
45 lines
1.5 KiB
Cheetah
---
|
|
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`
|