Riverpod modeli: ProviderScope və Consumer-lər
Riverpod-un əsas struktur qərarı: provider-lər widget ağacında yaşamır. Rəsmi müqayisə səhifəsi onları plain Dart objects — top-level final dəyişənlər kimi təsvir edir. Ağacda yalnız bir widget qalır: tətbiqin kökündəki ProviderScope, provider-lərin state-ini saxlayan qab.
Bundan iki nəticə çıxır:
- Oxunuş tipə görə deyil, obyektə görədir:
ref.watch(counterProvider). Deməli "ağacda bu tipdən provider yoxdur" runtime xətası aradan qalxır — provider-i import etmirsinizsə, kod kompilyasiya olunmur. - Eyni tipdən istənilən sayda provider ola bilər:
nameProvidervətitleProviderhər ikisiStringqaytarsa da, qarışmır.
UI tərəfində üç giriş nöqtəsi var: ConsumerWidget (build(context, ref)), ConsumerStatefulWidget + ConsumerState (orada ref sinif səviyyəsində mövcuddur) və mövcud widget-in içində kiçik bir sahə üçün Consumer widget-i.
void main() {
// ProviderScope provider-lərin state-ini saxlayan qabdır.
runApp(const ProviderScope(child: MyApp()));
}
// Provider top-level final dəyişəndir — ağacda deyil.
final counterProvider = NotifierProvider<Counter, int>(Counter.new);
class Counter extends Notifier<int> {
@override
int build() => 0; // ilkin state
void increment() => state++;
}
// 1) ConsumerWidget: build metodunda ref var.
class CounterText extends ConsumerWidget {
const CounterText({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('$count');
}
}
// 2) Consumer: mövcud widget-in içində yalnız kiçik sahə.
Column(
children: [
const ExpensiveHeader(),
Consumer(
builder: (context, ref, child) => Text('${ref.watch(counterProvider)}'),
),
],
);
// 3) ConsumerStatefulWidget: initState/dispose lazım olduqda.
class ChartPage extends ConsumerStatefulWidget {
const ChartPage({super.key});
@override
ConsumerState<ChartPage> createState() => _ChartPageState();
}
class _ChartPageState extends ConsumerState<ChartPage> {
@override
void initState() {
super.initState();
// ConsumerState daxilində ref sinif səviyyəsindədir.
ref.read(counterProvider.notifier).increment();
}
@override
Widget build(BuildContext context) => Text('${ref.watch(counterProvider)}');
}Kök, provider və üç oxunuş forması.
| Provider (paket) | Riverpod |
|---|---|
| `MultiProvider` ağacın içində | Kökdə bir `ProviderScope`, provider-lər qlobal `final` dəyişən |
| `BuildContext.watch<T>()` | `WidgetRef.watch(provider)` |
| `BuildContext.read<T>()` | `WidgetRef.read(provider)` |
| `BuildContext.select<T, R>()` | `WidgetRef.watch(provider.select(...))` |
| `ProxyProvider` | Provider daxilində `ref.watch()` |
| `ChangeNotifierProxyProvider` | `ref.listen()` |
Rəsmi DO/DON'T səhifəsinin ən sərt qaydası: provider-lər yalnız top-level `final` dəyişən kimi yaradılmalıdır. Onları sinifin içində və ya dinamik şəkildə yaratmaq — sənədin ifadəsi ilə — yaddaş sızmasına və gözlənilməz davranışa aparır. Parametr lazımdırsa, family istifadə olunur; dinamik provider yaratmaq yolu deyil.
📚 Mənbələr və sənədlər
- Riverpod: başlanğıcrəsmiriverpod.dev
ProviderScope, quraşdırma və ilk provider — 3.x sənədi.
- Consumer-lərrəsmiriverpod.dev
ConsumerWidget, ConsumerStatefulWidget və Consumer arasındaki fərq.
- Provider-dən Riverpod-a: müqayisərəsmiriverpod.dev
API uyğunluq cədvəlinin mənbəyi.
- DO / DON'T qaydalarırəsmiriverpod.dev
Provider-lərin qlobal final olması və dinamik yaradılmanın qadağası burada.