Riverpod Anti-Patterns & Diagnostic Remedies
🚫 Anti-Pattern Diagnostic Catalog
Identify, diagnose, and refactor 6 dangerous Riverpod anti-patterns using side-by-side code diffs and architectural explanations.
Anti-Pattern 1: Calling ref.read() inside Widget build()
WHY IT BREAKS: Replacing ref.watch() with ref.read() inside build() prevents the widget from subscribing to reactive updates. The screen will fail to rebuild when provider state changes.
- final user = ref.read(userProvider); // BAD: Non-reactive read breaks UI updates + final user = ref.watch(userProvider); // GOOD: Reactive subscription rebuilds UI
Anti-Pattern 2: Missing ref.mounted Guard
WHY IT BREAKS: If a user navigates away from a screen while an async network request is pending, the provider may be disposed. Mutating state on an unmounted notifier throws a fatal runtime state error.
Future<void> fetchProfile() async {
final res = await api.getProfile();
- state = AsyncValue.data(res); // BAD: Crashes if provider unmounted during await
+ if (!ref.mounted) return; // GOOD: Protective unmount guard
+ state = AsyncValue.data(res);
}
Anti-Pattern 3: Un-Overridden Family Parameters
WHY IT BREAKS: Passing a custom object to a Family provider without overriding operator == and hashCode relies on instance identity (`identical`). This creates duplicate provider instances on every rebuild, triggering infinite fetch loops.
class FilterArgs {
final String category;
final int minPrice;
FilterArgs({required this.category, required this.minPrice});
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is FilterArgs && category == other.category && minPrice == other.minPrice;
+
+ @override
+ int get hashCode => Object.hash(category, minPrice);
}
Anti-Pattern 4: Direct API Endpoint Calls inside Widgets
WHY IT BREAKS: Calling HTTP clients directly inside button click handlers tightly couples UI widgets to backend API contracts and prevents unit testing.
- onPressed: () async { await http.post('/login', body: ...); } // BAD: Direct API call
+ onPressed: () => ref.read(loginControllerProvider.notifier).login(email, password), // GOOD
🎉 Master Reference Complete
You have completed the entire Riverpod Master Reference! You now hold a complete mental model, decision trees, cheat sheets, and audit protocols. You are 100% prepared to begin the real Transwave production codebase deep dives in Milestone 3!