25 β Riverpod (3.x)¶
β¬ οΈ Oldingi: 24 β Provider va InheritedWidget Β· π README Β· Keyingi: 26 β Bloc va Cubit β‘οΈ
Bu bobda: 24-bobda
Providerpaketi bilan holatni vidjet daraxti orqali ulashishni o'rgandingiz. U yaxshi, lekin o'sib borgan ilovada bir nechta nozik kamchiligi bor: ba'zi xatolar faqat ishga tushganda (runtime'da) sezilib,BuildContext'ga bog'liqligi esa holatni sinash va birlashtirishni qiyinlashtiradi. Endi xuddi shu muallif yaratgan Riverpod paketi bilan tanishamiz β bu "tuzatilgan Provider". Siz nega Riverpod (kompilyatsiya-xavfsizlik,BuildContext'dan ozodlik, oson birlashtirish, ichki async), uni o'rnatish (ProviderScope), vidjetda provayderni o'qish (ConsumerWidget,ref.watch/ref.read/ref.listen), provayder turlari (Provider,StateProvider, zamonaviyNotifierProvider, async uchunAsyncNotifierProvider/FutureProvider), tavsiya etilgan kod-generatsiya (@riverpod+build_runnerβ makros emas!), provayderlarni birlashtirish/hosila qilish (.family,.autoDispose), yon ta'sir va yangilash (ref.invalidate,ref.listen) va sinash (test) ni o'rganasiz. Oxirida hisoblagich, async foydalanuvchilar ro'yxati va filtrlangan hosila ro'yxat quramiz.
Nega Riverpod? (Provider'ning nozik kamchiliklari)¶
24-bobda Provider paketi holatni vidjet daraxtidan yuqoriga qo'yib, pastdagi vidjetlar uni context.watch/Consumer bilan o'qishini ko'rdik. Bu ishlaydi β lekin uch xil og'riq bor:
- Xato faqat ishga tushganda sezildi. Agar vidjet daraxtida tegishli
Providertopilmasa, ilova ishlab turgandaProviderNotFoundExceptionbilan qulaydi. Kompilyator bu xatoni oldindan ushlamaydi β siz uni faqat o'sha ekranni ochganingizda ko'rasiz. BuildContext'ga bog'liqlik. Provider holatnicontext(vidjet daraxtidagi joylashuv) orqali topadi. Demak holatni o'qish uchun sizda doimBuildContextbo'lishi shart β bu test yozishni va daraxtdan tashqarida holatga murojaat qilishni noqulay qiladi.- Birlashtirish noqulay. "A holatdan B holatni hisoblab chiqarish" yoki "ikki holatni birlashtirish" β Provider'da bir oz mashaqqatli.
Riverpod β Provider'ning muallifi (Remi Rousselet) tomonidan yozilgan, ana shu uch og'riqni davolovchi paket. Nomi ham bejiz emas: "Riverpod" β "Provider" so'zining harflarini qayta tartiblash (anagramma). G'oyasi: provayderlarni vidjet daraxtiga emas, balki global, lekin nazoratli (scoped) obyektlarga aylantirish.
Riverpod nimani yaxshilaydi:
- Kompilyatsiya-xavfsizlik. Provayderni noto'g'ri ishlatsangiz, kod kompilyatsiya bo'lmaydi β xato ekranni ochishdan oldin, yozish paytida bilinadi.
ProviderNotFoundExceptionkabi runtime ajablanish yo'q. BuildContext'dan ozod. Holatni o'qish uchuncontextshart emas βreforqali o'qiladi. Shuning uchun holatga vidjetdan tashqarida ham (masalan boshqa provayder ichida) murojaat qilsa bo'ladi.- Oson birlashtirish. Bir provayder boshqasini
ref.watchqilib, undan hosila (derived) holat chiqaradi. Manba o'zgarsa, hosila avtomatik yangilanadi. - Ichki async qo'llab-quvvatlash. Async ma'lumot uchun maxsus
AsyncValueturi bor β yuklanmoqda/xato/tayyor holatlarini bitta joyda, toza tarzda boshqaradi. - Sinaluvchanlik (testability). Provayderlarni testda soxta (fake) versiyalar bilan almashtirish (override) oson β bu Riverpod'ning katta yutug'i.
- Avtomatik tozalash (auto-dispose). Provayder ishlatilmay qolsa, xotirani o'zi bo'shatadi.
π‘ Qisqasi: Riverpod = "Provider, tuzatilgan". Agar siz Provider'ni o'rgangan bo'lsangiz, g'oyalar tanish: "holatni bir joyga qo'yib, vidjetlar uni kuzatadi". Farqi β Riverpod buni daraxtga emas,
ref'ga bog'laydi, shuning uchun xavfsizroq va moslashuvchanroq.
O'rnatish (setup)¶
Avval paketni qo'shamiz. Terminalda loyiha papkasida:
Bu pubspec.yaml ga eng so'nggi versiyani qo'shadi:
So'ng β bu yagona majburiy qadam β butun ilovani ProviderScope ga o'rab qo'yamiz. ProviderScope β barcha provayderlar holatini saqlaydigan idish (konteyner); usiz Riverpod ishlamaydi:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
const ProviderScope( // <-- butun ilovani shunga o'raymiz
child: MyApp(),
),
);
}
π‘
^3.3.0dagi^(karet) belgisi "3.3.0 va undan yuqori, lekin 4.0.0 dan past" degani β kichik yangilanishlarni avtomatik oladi, buzuvchi katta o'zgarishlardan himoya qiladi. Riverpod'ning hozirgi (2026) versiyasi β 3.x.
Provayderni vidjetda o'qish¶
Provayderni o'qish uchun vidjetga ref (reference β "murojaat") obyekti kerak. Eng oson yo'l β vidjetni StatelessWidget o'rniga ConsumerWidget qilish: u build metodiga qo'shimcha ref argumentini beradi:
class CounterText extends ConsumerWidget {
const CounterText({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) { // <-- ref qo'shildi
final count = ref.watch(counterProvider); // obuna bo'lib o'qish
return Text('Hisob: $count');
}
}
ref orqali holatni o'qishning uch usuli bor β ularni farqlash juda muhim:
| Metod | Nima qiladi | Qayerda ishlatiladi |
|---|---|---|
ref.watch(p) |
Provayderga obuna bo'ladi: qiymat o'zgarsa, vidjet qayta quriladi. | build ichida β UI ko'rsatadigan qiymatni o'qishda. |
ref.read(p) |
Qiymatni bir martalik o'qiydi, obuna bo'lmaydi. | Callback ichida (tugma bosilganda) β metod chaqirishda. |
ref.listen(p, cb) |
O'zgarishni "tinglaydi" va yon ta'sir bajaradi (qayta qurmaydi). | build ichida β SnackBar ko'rsatish, navigatsiya kabi. |
Eng keng tarqalgan xato β build ichida ref.read ishlatish yoki callback ichida ref.watch ishlatish. Oddiy qoida:
π‘ Qoida: UI'da ko'rsatish uchun o'qiyapsizmi β
ref.watch(build ichida). Tugma bosilganda biror amal qilyapsizmi βref.read(callback ichida). "Voqea sodir bo'lganda bir marta nimadir qil" (SnackBar/navigatsiya) βref.listen.
Agar butun vidjetni emas, balki uning kichik bir qismini kuzatishni xohlasangiz, Consumer vidjetidan foydalaning β faqat shu qism qayta quriladi:
Consumer(
builder: (context, ref, child) {
final count = ref.watch(counterProvider);
return Text('Hisob: $count');
},
)
StatefulWidget kerak bo'lsa β ConsumerStatefulWidget (juft ConsumerState) bor; unda ref State ichida to'g'ridan-to'g'ri ishlatiladi.
Provayder turlari β soddadan zamonaviygacha¶
Riverpod'da bir nechta provayder turi bor. Ularni soddadan murakkabga qarab quramiz.
Provider β o'qish uchun (hosila, DI)¶
Eng sodda tur. O'zgarmas yoki hisoblangan qiymatni beradi β masalan boshqa obyektga bog'liqlik in'ektsiyasi (dependency injection) yoki hosila qiymat:
ref β bu provayder ichida boshqa provayderlarni o'qish uchun beriladi (pastda hosila holatda ko'ramiz).
StateProvider β oddiy o'zgaruvchan qiymat¶
Bitta oddiy o'zgaruvchan qiymat (son, matn, bool) uchun. Eng tez yo'l, lekin u eski (legacy) uslub hisoblanadi β jiddiy holat uchun pastdagi NotifierProvider tavsiya etiladi:
// DIQQAT: Riverpod 3'da StateProvider (va boshqa legacy provayderlar) asosiy
// paketdan emas, alohida 'legacy.dart' faylidan keladi β uni ham import qiling:
import 'package:flutter_riverpod/legacy.dart';
final filterProvider = StateProvider<String>((ref) => '');
// O'zgartirish: ref.read(filterProvider.notifier).state = 'al';
β οΈ
StateProviderfaqat eng oddiy qiymatlar uchun qulay. Mantiqi bor holat (metodlar, validatsiya, bir nechta maydon) kerak bo'lsa βNotifierProviderishlating. Yangi koddaStateProvider'ga ko'p suyanmang.π Riverpod 3 muhim o'zgarishi.
StateProvider,StateNotifierProvidervaStateNotifierkabi eski provayderlar Riverpod 3'da asosiyflutter_riverpod.dartbarrelidan olib tashlandi β ular endiimport 'package:flutter_riverpod/legacy.dart';orqali keladi. AgarStateProvider isn't definedxatosini ko'rsangiz β aynan shu importni qo'shing. ZamonaviyNotifier/AsyncNotifieresa asosiy importning o'zida bor.
NotifierProvider + Notifier<T> β zamonaviy asosiy yo'l¶
Bu β o'zgaruvchan holatni boshqarishning tavsiya etilgan usuli. Siz Notifier<T> dan meros oluvchi klass yozasiz: build() boshlang'ich holatni qaytaradi, metodlar esa state ni o'zgartiradi. state ga yangi qiymat berganingizda β kuzatuvchi vidjetlar avtomatik qayta quriladi:
class Counter extends Notifier<int> {
@override
int build() => 0; // boshlang'ich holat
void increment() => state++; // state o'zgardi -> watcherlar qayta quriladi
void decrement() => state--;
void reset() => state = 0;
}
final counterProvider = NotifierProvider<Counter, int>(Counter.new);
E'tibor bering: holatni state orqali o'zgartirasiz, setState emas. Oqim bir tomonlama va toza:
Rasmda ko'rganingizdek: (1) vidjet ref.watch bilan obuna bo'ladi β (2) foydalanuvchi tugmani bosadi β (3) callback ref.read(counterProvider.notifier).increment() ni chaqiradi β (4) state++ ishlaydi va barcha kuzatuvchilar qayta quriladi. Ma'lumot faqat bir yo'nalishda aylanadi β bu xulqni oldindan aytishni osonlashtiradi.
Vidjetda ishlatish:
class CounterPage extends ConsumerWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider); // qiymatni o'qish (obuna)
return Scaffold(
appBar: AppBar(title: const Text('Hisoblagich')),
body: Center(child: Text('$count', style: const TextStyle(fontSize: 48))),
floatingActionButton: FloatingActionButton(
// .notifier orqali metodga kiramiz; read β bir martalik
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Icon(Icons.add),
),
);
}
}
π‘
providervaprovider.notifierfarqi.ref.watch(counterProvider)β qiymat ni (int) qaytaradi.ref.read(counterProvider.notifier)β Notifier obyekti ni (metodlar bor:increment,reset) qaytaradi. Qiymatni o'qigandaprovider, metod chaqirgandaprovider.notifier.
Async holat: AsyncNotifierProvider, FutureProvider/StreamProvider¶
21-bobda serverdan ma'lumot olishni FutureBuilder bilan ko'rgan edik: u Future'ning yuklanmoqda / xato / tayyor uchta holatini boshqaradi. Riverpod'da bu vazifani AsyncValue<T> turi bajaradi β async natija doim shu uch holatdan birida bo'ladi.
Eng sodda yo'l β FutureProvider: async funksiya yozasiz, u Future qaytaradi, Riverpod uni AsyncValue ga o'raydi:
final usersProvider = FutureProvider<List<User>>((ref) async {
final repo = ref.watch(repositoryProvider); // boshqa provayderga bog'liqlik
return repo.fetchUsers(); // Future<List<User>> qaytaradi
});
Vidjetda AsyncValue ni .when bilan ochasiz β uch holat uchun uchta builder beradi:
class UserListView extends ConsumerWidget {
const UserListView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final usersAsync = ref.watch(usersProvider); // AsyncValue<List<User>>
return usersAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, st) => Center(child: Text('Xato: $e')),
data: (users) => ListView(
children: [for (final u in users) ListTile(title: Text(u.name))],
),
);
}
}
Rasmda ko'rinib turganidek, .when xuddi 21-bobdagi FutureBuilder kabi ishlaydi β lekin yanada toza: connectionState yoki snapshot.hasData ni qo'lda tekshirish, null xavfi yo'q. Har bir holat aniq nomlangan: loading, error, data.
Agar holatni vaqt o'tib o'zgartiradigan murakkab async mantiq kerak bo'lsa (masalan qayta yuklash metodlari bilan), AsyncNotifierProvider + AsyncNotifier<T> ishlatiladi β bu Notifierning async ekvivalenti:
class UsersNotifier extends AsyncNotifier<List<User>> {
@override
Future<List<User>> build() async {
final repo = ref.watch(repositoryProvider);
return repo.fetchUsers();
}
Future<void> refresh() async {
state = const AsyncValue.loading(); // qayta yuklanmoqda
state = await AsyncValue.guard(() => ref.read(repositoryProvider).fetchUsers());
}
}
final usersProvider2 = AsyncNotifierProvider<UsersNotifier, List<User>>(UsersNotifier.new);
Uzluksiz oqim (real vaqtli yangilanish, WebSocket) uchun esa StreamProvider bor β u Stream qaytaradi (9-bobdagi Stream'ni eslang) va xuddi shunday AsyncValue beradi.
Kod-generatsiya β Riverpod 3'ning tavsiya etilgan yo'li¶
Yuqorida provayderlarni qo'lda e'lon qildik (NotifierProvider<Counter, int>(Counter.new)). Riverpod 3'da tavsiya etilgan zamonaviyroq usul bor: @riverpod annotatsiyasi + kod-generatsiya. Siz funksiya yoki klass yozib, ustiga @riverpod qo'yasiz β provayderni kod-generator siz uchun yaratadi.
β οΈ MUHIM β bu makros emas! Riverpod kod-generatsiyasi
build_runnerasbobi orqali ishlaydi: u.g.dartfaylga kod yozadi. Dart "makros" (macros) tajribasi bekor qilingan, shuning uchun u bilan adashtirmang β bu oddiy, barqarorbuild_runnercodegen.
Avval kerakli paketlarni qo'shamiz (codegen uchun uchta qo'shimcha):
dependencies:
flutter_riverpod: ^3.3.0
riverpod_annotation: ^3.0.0
dev_dependencies:
build_runner: ^2.4.0
riverpod_generator: ^3.0.0
Endi hisoblagichni annotatsiya bilan yozamiz. part 'fayl.g.dart'; qatori generator yozadigan faylni ulaydi, klass esa _$Counter (generatsiya qilinadigan asos) dan meros oladi:
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'counter.g.dart'; // generator shu faylni yaratadi
@riverpod
class Counter extends _$Counter {
@override
int build() => 0; // boshlang'ich holat
void increment() => state++;
}
// `counterProvider` ni generator avtomatik yaratadi!
Async ma'lumot uchun β shunchaki async funksiyaga @riverpod qo'yamiz; u FutureProvider ga aylanadi:
@riverpod
Future<List<User>> users(Ref ref) async {
final repo = ref.watch(repositoryProvider);
return repo.fetchUsers();
}
// `usersProvider` (FutureProvider) avtomatik yaratiladi.
Generatorni ishga tushirish β u kodingizni kuzatib, har o'zgarishda .g.dart ni yangilab turadi:
Codegen'ning afzalligi: provayder turini (NotifierProvider<Counter, int>) qo'lda yozmaysiz, xato qilish ehtimoli kamayadi, va .autoDispose avtomatik yoqiladi (pastda).
π‘ Boshlang'ich uchun: qo'lda e'lon qilish (yuqoridagi
NotifierProvider(...)) tushunish uchun aniqroq. Kattaroq loyihada esa codegen tavsiya etiladi β kamroq qo'l kodi, kamroq xato. Ikkalasi ham aynan bir xil ishlaydi.
Birlashtirish va hosila holat¶
Riverpod'ning eng kuchli tomoni β provayderlar bir-birini ref.watch qila oladi. Shunda bir holatdan ikkinchisini hosila (derived) qilib chiqarasiz va manba o'zgarsa hosila o'zi yangilanadi.
Masalan: barcha foydalanuvchilar (usersProvider) va qidiruv matni (filterProvider) dan filtrlangan ro'yxat chiqaramiz:
final filterProvider = StateProvider<String>((ref) => '');
final filteredUsersProvider = Provider<List<User>>((ref) {
final filter = ref.watch(filterProvider); // matnni kuzatadi
final usersAsync = ref.watch(usersProvider); // ro'yxatni kuzatadi
final users = usersAsync.value ?? []; // hali yuklanmagan bo'lsa []
if (filter.isEmpty) return users;
return users.where((u) => u.name.toLowerCase().contains(filter.toLowerCase())).toList();
});
Endi filterProvider yoki usersProvider o'zgarsa β filteredUsersProvider avtomatik qayta hisoblanadi, uni kuzatayotgan vidjet esa qayta quriladi. Siz hech narsani qo'lda ulamaysiz:
Rasmda butun zanjir ko'rinadi: repositoryProvider (manba) β usersProvider (uni watch qiladi) β filteredUsersProvider (users va filter ni birga watch qiladi) β vidjet faqat oxirgisini watch qiladi. Bu yo'naltirilgan graf (DAG): har bir tugun yuqorisidagilarni kuzatadi, o'zgarish pastga oqadi.
.family β parametrli provayder¶
Ba'zan provayderga argument kerak: "shu id li foydalanuvchi". Buning uchun .family bor β provayderni funksiya kabi parametr bilan chaqirasiz:
final userProvider = FutureProvider.family<User, int>((ref, id) async {
final repo = ref.watch(repositoryProvider);
return repo.fetchUser(id);
});
// Vidjetda:
final userAsync = ref.watch(userProvider(42)); // id = 42
Codegen bilan bu yanada tabiiy β funksiyaga shunchaki qo'shimcha parametr qo'shasiz: Future<User> user(Ref ref, int id) async => ....
.autoDispose β xotirani bo'shatish¶
Standart holatda provayder bir marta yaratilsa, ilova davomida xotirada qoladi. .autoDispose esa provayderni hech kim kuzatmay qolgan zahoti tozalaydi β bu, ayniqsa, .family (har xil id uchun ko'p nusxa) va vaqtinchalik ekranlar uchun foydali:
final searchProvider = FutureProvider.autoDispose<List<User>>((ref) async {
// qidiruv ekrani yopilsa, bu provayder o'zini tozalaydi
return ref.watch(repositoryProvider).search('al');
});
π‘ Codegen bilan
.autoDisposeavtomatik β@riverpodqo'yilgan har bir provayder standart holatda auto-dispose bo'ladi. Doimiy saqlanishi kerak bo'lsa,@Riverpod(keepAlive: true)deysiz. Qo'lda e'londa esa.autoDisposeni o'zingiz qo'shasiz.
Yon ta'sir va yangilash¶
ref.invalidate(p) β provayderni "eskirgan" deb belgilaydi va uni qayta hisoblashga majbur qiladi. Bu async ma'lumotni qayta yuklash (refresh) uchun ajoyib:
// "Yangilash" tugmasi β usersProvider'ni qaytadan fetch qiladi:
onPressed: () => ref.invalidate(usersProvider),
ref.listen β holat o'zgarganda yon ta'sir bajarish uchun (UI qurish emas): xato kelganda SnackBar ko'rsatish, login bo'lganda boshqa ekranga o'tish kabi. Uni build ichida yozasiz, lekin u qayta qurmaydi:
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen(usersProvider, (previous, next) {
if (next is AsyncError) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Ma\'lumot yuklanmadi')),
);
}
});
// ... qolgan UI
}
π‘
watchvalistenfarqi.ref.watchβ qiymatni UI'da ko'rsatish uchun (o'zgarsa qayta quradi).ref.listenβ o'zgarishga reaksiya qilish (SnackBar/navigatsiya) uchun, qayta qurmaydi. SnackBar niwatchichida ko'rsatmang β har qayta qurishda takror chiqib ketadi.
Sinash (testing) β Riverpod'ning katta yutug'i¶
Provayderlar ref'ga bog'langani uchun, testda ularni soxta (fake) versiyalar bilan almashtirish juda oson. ProviderContainer β testdagi ProviderScopening kodli ekvivalenti; overrides orqali haqiqiy provayderni soxtasiga almashtirasiz:
test('filtrlangan ro\'yxat to\'g\'ri ishlaydi', () {
final container = ProviderContainer(
overrides: [
// haqiqiy repository o'rniga soxtasini in'ektsiya qilamiz
repositoryProvider.overrideWithValue(FakeRepository()),
],
);
addTearDown(container.dispose); // test tugagach tozalash
container.read(filterProvider.notifier).state = 'al';
final result = container.read(filteredUsersProvider);
expect(result.every((u) => u.name.toLowerCase().contains('al')), isTrue);
});
Mana shu β BuildContext'siz, hech qanday vidjet qurmasdan holat mantig'ini sinash imkoni. Provider'da bunga erishish ancha qiyinroq edi.
Riverpod yoki Bloc? (halol taqqoslash)¶
Keyingi 26-bobda yana bir mashhur holat boshqaruvi β Bloc/Cubit bilan tanishamiz. Ikkalasi ham yaxshi; tanlov β uslub masalasi:
- Riverpod β moslashuvchan, kompilyatsiya-xavfsiz, kam qoidali (boilerplate). Provayderlarni erkin birlashtirasiz. Ko'pchilik ilova uchun ajoyib, ayniqsa async ma'lumot ko'p bo'lsa.
- Bloc β yanada tuzilgan (structured) va voqea asosli (event-driven): UI voqea (event) yuboradi, Bloc uni holatga aylantiradi. Bu qat'iy oqim katta jamoalarda mashhur, chunki "kim nimani qachon o'zgartirdi" aniq ko'rinadi.
π‘ To'g'ri javob yo'q β ikkalasi ham real ilovalarda keng ishlatiladi. Riverpod tezroq boshlanadi va moslashuvchanroq; Bloc qat'iyroq tuzilma va voqealar izini (audit) beradi. Avval ikkalasini ko'ring, keyin loyihangizga mosini tanlang.
Keyingi qadam¶
Bu bobda Riverpod 3'ni o'rgandingiz: nega u kerak (kompilyatsiya-xavfsizlik, BuildContext'dan ozodlik, oson birlashtirish, ichki async), ProviderScope bilan o'rnatish, ConsumerWidget + ref.watch/read/listen bilan o'qish, provayder turlari (Provider, StateProvider, zamonaviy NotifierProvider, async uchun AsyncNotifierProvider/FutureProvider + AsyncValue.when), tavsiya etilgan codegen (@riverpod + build_runner β makros emas), birlashtirish (.family, .autoDispose), yangilash (ref.invalidate, ref.listen) va ProviderContainer bilan sinash.
Keyingi 26-bobda Bloc va Cubit bilan β voqea asosli, qat'iy tuzilgan holat boshqaruvi bilan tanishamiz va uni Riverpod bilan amalda solishtiramiz.
Mashqlar¶
Oson¶
- O'z so'zlaringiz bilan ayting: Riverpod Provider'ning qaysi uch og'rig'ini davolaydi? "Kompilyatsiya-xavfsizlik" nimani anglatadi?
- Har bir ilovada qaysi vidjet majburiy ravishda butun ilovani o'rashi kerak, va nega? Uni
mainda qanday yozasiz? ref.watch,ref.readvaref.listenβ har birini bir jumla bilan tushuntiring va qaysi biribuildichida, qaysi biri callback ichida ishlatilishini ayting.ref.watch(counterProvider)varef.read(counterProvider.notifier)β bu ikkisi nimani qaytaradi va farqi nimada?
O'rta¶
Notifier<int>dan meros oluvchiCounterklassini yozing: boshlang'ich qiymati 0,increment(),decrement()vareset()metodlari bilan. So'ng unga mosNotifierProviderni e'lon qiling.- Quyidagi
FutureProviderning qiymatini vidjetda.whenbilan ko'rsating β yuklanmoqda, xato va tayyor holatlari uchun mos UI yozing: @riverpodannotatsiyasi bilan ishlaydigan kod-generatsiya makrosmi? Aniq tushuntiring: u qaysi asbob bilan ishlaydi va kod qayerga yoziladi?
Qiyin¶
allTodosProvider(barcha vazifalar) vashowCompletedProvider(StateProvider<bool>) berilgan. Faqat shu sozlamaga qarab vazifalarni filtrlaydigan hosilavisibleTodosProviderni yozing. Manbalardan biri o'zgarsa nima bo'ladi va nega siz hech narsani qo'lda ulamaysiz?- Bir o'quvchi
buildmetodi ichida xato kelganda SnackBar ko'rsatish uchunref.watchishlatib, SnackBar bir necha marta takror chiqib ketishidan shikoyat qilyapti. Muammo nimada va to'g'ri yechim qaysirefmetodi bilan bo'ladi? .autoDisposenima qiladi va nega.familybilan birga, ayniqsa, foydali? Codegen (@riverpod) ishlatganda.autoDisposexulqi qanday bo'ladi?
Yechimlar
1. Riverpod uch og'riqni davolaydi: (1) runtime xato β Provider'da topilmagan provayder ilova ishlaganda ProviderNotFoundException bilan qulaydi; Riverpod'da bu kompilyatsiya xatosi bo'ladi. (2) BuildContext'ga bog'liqlik β Riverpod holatni ref orqali o'qiydi, context shart emas. (3) birlashtirish noqulayligi β Riverpod'da bir provayder boshqasini ref.watch qilib hosila holat chiqaradi. Kompilyatsiya-xavfsizlik = xato kodni yozish/kompilyatsiya paytida bilinadi, ilovani ishga tushirib o'sha ekranni ochishni kutish shart emas.
2. ProviderScope majburiy β u barcha provayderlar holatini saqlaydigan idish; usiz Riverpod ishlamaydi. main da butun ilovani shunga o'raysiz:
3. ref.watch(p) β provayderga obuna bo'ladi va qiymat o'zgarsa vidjetni qayta quradi; build ichida ishlatiladi. ref.read(p) β qiymatni bir martalik o'qiydi, obuna bo'lmaydi; callback (tugma bosilishi) ichida ishlatiladi. ref.listen(p, cb) β o'zgarishni tinglab yon ta'sir (SnackBar/navigatsiya) bajaradi, qayta qurmaydi; build ichida yoziladi.
4. ref.watch(counterProvider) β provayderning qiymatini (masalan int) qaytaradi (UI'da ko'rsatish uchun). ref.read(counterProvider.notifier) β Notifier obyektini qaytaradi, unda holatni o'zgartiruvchi metodlar (increment, reset) bor. Qisqasi: qiymat uchun provider, metod uchun provider.notifier.
5.
class Counter extends Notifier<int> {
@override
int build() => 0;
void increment() => state++;
void decrement() => state--;
void reset() => state = 0;
}
final counterProvider = NotifierProvider<Counter, int>(Counter.new);
6.
@override
Widget build(BuildContext context, WidgetRef ref) {
final productsAsync = ref.watch(productsProvider);
return productsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, st) => Center(child: Text('Xato: $e')),
data: (products) => ListView(
children: [for (final p in products) ListTile(title: Text(p))],
),
);
}
7. Yo'q, bu makros emas. Makros (Dart macros) tajribasi bekor qilingan. @riverpod kod-generatsiyasi build_runner asbobi orqali ishlaydi: u kodingizni o'qib, provayderni alohida .g.dart faylga yozadi (part 'fayl.g.dart'; bilan ulanadi). Generatorni dart run build_runner watch -d bilan ishga tushirasiz; u o'zgarishlarni kuzatib .g.dart ni yangilab turadi.
8.
final visibleTodosProvider = Provider<List<Todo>>((ref) {
final todos = ref.watch(allTodosProvider);
final showCompleted = ref.watch(showCompletedProvider);
if (showCompleted) return todos;
return todos.where((t) => !t.isCompleted).toList();
});
allTodosProvider yoki showCompletedProvider o'zgarsa β Riverpod visibleTodosProvider ni avtomatik qayta hisoblaydi, uni ref.watch qilayotgan vidjet esa qayta quriladi. Siz hech narsani qo'lda ulamaysiz, chunki ref.watch bog'liqlik grafini o'zi quradi: hosila provayder qaysi provayderlarni watch qilsa, o'shalarga avtomatik obuna bo'ladi va ular o'zgarganda yangilanadi.
9. Muammo: ref.watch build ichida har qayta qurishda qayta o'qiydi va SnackBar'ni har safar (har rebuild'da) takror ko'rsatadi β SnackBar UI qurish emas, balki yon ta'sir. To'g'ri yechim β ref.listen ishlatish: u faqat qiymat o'zgarganda bir marta chaqiriladi va vidjetni qayta qurmaydi:
ref.listen(dataProvider, (prev, next) {
if (next is AsyncError) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Xato yuz berdi')),
);
}
});
10. .autoDispose provayderni hech kim kuzatmay qolgan zahoti tozalaydi (xotiradan bo'shatadi), keyin yana kerak bo'lsa qaytadan yaratiladi. .family bilan, ayniqsa, foydali, chunki .family har xil argument (masalan har bir id) uchun alohida nusxa yaratadi β agar ular tozalanmasa, ishlatilmayotgan nusxalar xotirada to'planib qoladi; .autoDispose har bir nusxani kerakmay qolganda bo'shatadi. Codegen (@riverpod) ishlatganda .autoDispose standart yoqilgan β har bir generatsiya qilingan provayder avtomatik auto-dispose bo'ladi; doimiy saqlash kerak bo'lsa @Riverpod(keepAlive: true) deyiladi.
β¬ οΈ Oldingi: 24 β Provider va InheritedWidget Β· π README Β· Keyingi: 26 β Bloc va Cubit β‘οΈ