Sparround

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.watchWidgetRef.watch
  • BuildContext.readWidgetRef.read
  • BuildContext.selectWidgetRef.watch(myProvider.select(...))
  • ProxyProvider → provider daxilində ref.watch()
  • ChangeNotifierProxyProviderref.listen()
  • MultiProvider → kökdə ProviderScope, provider-lər isə qlobal final dəyişənlər
  • ChangeNotifierNotifier / 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.

dart
// ---------- Ə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).

ProviderRiverpodDiqqə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