Ref 07

Riverpod Anti-Patterns & Diagnostic Remedies

Calculating reading time... Module 1.5: Master Reference

🚫 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.

Diff Anti-Pattern #1 Remedy
- 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.

Diff Anti-Pattern #2 Remedy
  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.

Diff Anti-Pattern #3 Remedy
  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.

Diff Anti-Pattern #4 Remedy
- 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!