Provider-dən Riverpod-a migrasiya
Riverpod sənədində Provider-dən keçid üçün ayrı bölmə var: motivasiya, qısa başlanğıc və API uyğunluq cədvəli. Əsas uyğunluqlar:
BuildContext.watch→WidgetRef.watchBuildContext.read→WidgetRef.readBuildContext.select→WidgetRef.watch(myProvider.select(...))ProxyProvider→ provider daxilindəref.watch()ChangeNotifierProxyProvider→ref.listen()MultiProvider→ kökdəProviderScope, provider-lər isə qlobalfinaldəyişənlərChangeNotifier→Notifier/AsyncNotifier
Sənəd ən vacib dəyişikliyi belə ifadə edir: Notifier/AsyncNotifier immutable state ilə birlikdə daha yaxşı dizayn qərarlarına və daha az səhvə aparır. AsyncNotifier-də isLoading, hasError bayraqları yazmaq lazım gəlmir — build-in qaytardığı future avtomatik AsyncValue-ya çevrilir.
// ---------- ƏVVƏL: ChangeNotifier ----------
class TodoNotifier extends ChangeNotifier {
TodoNotifier(this._repository) {
load(); // konstruktorda yükləmə
}
final TodoRepository _repository;
List<Todo> todos = [];
bool isLoading = true; // əl ilə saxlanan bayraq
bool hasError = false; // əl ilə saxlanan bayraq
Future<void> load() async {
isLoading = true;
hasError = false;
notifyListeners();
try {
todos = await _repository.fetchAll();
} catch (_) {
hasError = true;
} finally {
isLoading = false;
notifyListeners();
}
}
}
final todoProvider = ChangeNotifierProvider<TodoNotifier>((ref) {
return TodoNotifier(ref.watch(todoRepositoryProvider));
});
// ---------- SONRA: AsyncNotifier ----------
class Todos extends AsyncNotifier<List<Todo>> {
@override
Future<List<Todo>> build() {
// Yükləmə məntiqi build-də: future qaytarmaq kifayətdir.
return ref.watch(todoRepositoryProvider).fetchAll();
}
Future<void> reload() async {
state = const AsyncLoading();
state = await AsyncValue.guard(
() => ref.read(todoRepositoryProvider).fetchAll(),
);
}
}
final todosProvider =
AsyncNotifierProvider<Todos, List<Todo>>(Todos.new);
// UI: isLoading/hasError yerinə pattern matching.
// switch (ref.watch(todosProvider)) { ... }Əvvəl / sonra: ChangeNotifier → AsyncNotifier (rəsmi migrasiya sənədinin strukturu).
| Provider | Riverpod | Diqqət |
|---|---|---|
| `MultiProvider` kökdə | `ProviderScope` kökdə | Provider-lər ağacdan qlobal `final` dəyişənlərə köçür |
| `ChangeNotifierProvider` | `NotifierProvider` / `AsyncNotifierProvider` | State immutable olur; `notifyListeners()` aradan qalxır |
| `ProxyProvider` | Provider daxilində `ref.watch()` | Asılılıq qrafı kodun içində görünür |
| `context.select` | `ref.watch(p.select(...))` | Seçilən dəyər immutable olmalıdır |
| Widget testində ağacı əl ilə qurmaq | `ProviderScope(overrides: [...])` | Test kodu qısalır |
Praktik migrasiya strategiyası: iki paket bir layihədə yan-yana yaşaya bilər. ProviderScope-u MultiProvider-in içinə (ya da əksinə) yerləşdirib feature-ləri bir-bir köçürmək mümkündür. Bu, "hər şeyi bir sprintdə köçürək" planından qat-qat təhlükəsizdir — hər köçürülən feature ayrı PR və ayrı test dəsti ilə gəlir. Riverpod 3-də ChangeNotifierProvider hələ də mövcuddur (legacy import-da), ona görə keçid mərhələsində ChangeNotifier-lər bir müddət saxlanıla bilər.
📚 Mənbələr və sənədlər
- Provider vs Riverpodrəsmiriverpod.dev
API uyğunluq cədvəlinin rəsmi mənbəyi.
- Provider-dən keçid: motivasiyarəsmiriverpod.dev
- Provider-dən keçid: quickstartrəsmiriverpod.dev
- ChangeNotifier-dən migrasiyarəsmiriverpod.dev
isLoading/hasError bayraqlarının AsyncValue ilə əvəzlənməsi — əvvəl/sonra kodu.