Riverpod — компилабилни менаџер стања и зависности за Flutter, креиран од страна Rémi Roussel-а 2021. године као наследник Provider-а. Riverpod решава фундаменталне проблеме Provider-а: недостатак компилационе провере, зависност од BuildContext-а и тешкоће са ProviderNotFoundException. Према подацима pub.dev, пакет је прикупио преко 5 хиљада лајкова и активно замењује Provider у новим пројектима.
Главно
Riverpod — библиотека за управљање стањем и убризгавање зависности у Flutter-у, која компилира опис провајдера у безбедан Dart код. За разлику од Provider-а, Riverpod провајдери нису везани за BuildContext: креирају се глобално или у ProviderScope-у и доступни су са било ког места. Компилатор проверава типове, зависности и интегритет графа провајдера у фази изградње, елиминишући runtime грешке попут ProviderNotFoundException.
Riverpod користи модел override за тестирање: сваки провајдер може бити поништен путем ProviderScope.overrideWith без потребе за креирањем подкласа или моковања интерфејса. То чини тестирање изолованим: сваки тест добија своју копију графа зависности која је потпуно контролисана.
Према Flutter Community Survey 2025, Riverpod заузима треће место по популарности после Provider-а и BLoC-а. Истовремено, Riverpod је најбрже растући пакет: +120% инсталација у 2024. Главни разлози: компилациона безбедност, одсуство ProviderNotFoundException, уграђена подршка за асинхроност кроз AsyncValue.
Riverpod нуди 8 типова провајдера, сваки за одређени сценарио: Provider (константа/услуга), StateProvider (примитивно стање), StateNotifierProvider (сложена логика са StateNotifier), ChangeNotifierProvider (за миграцију са Provider-а), FutureProvider (асинхрони подаци, једном), StreamProvider (реактивни ток), NotifierProvider (нови API, Flutter 3.10+) и AsyncNotifierProvider (асинхрони Notifier).
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
}
class CounterScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('$count');
}
}ProviderRef — објекат који се прослеђује сваком провајдеру за приступ другим провајдерима. ref.watch — претплата на промене, ref.read — једнократно читање, ref.invalidate — ресетовање кеша. ProviderRef замењује BuildContext из Provider-а: било који провајдер може читати друге провајдере без приступа стаблу виџета. Ово омогућава изградњу графа зависности ван UI слоја.
ProviderScope — обавезни коренски виџет за рад Riverpod-а. ProviderScope чува све провајдере, управља њиховим животним циклусом и кешира вредности. Без ProviderScope-а апликација ће пасти са ProviderNotFoundException. ProviderScope може бити угнежђен — угнежђени опсег поништава провајдере родитеља, што се користи за тестирање и изолацију функција.
AsyncValue — sealed класа Riverpod-а за представљање асинхроног стања. AsyncValue има три варијанте: AsyncData (успешни подаци), AsyncError (грешка), AsyncLoading (учитавање). Уместо ручног пребацивања између loading/error/data, сваки FutureProvider или StreamProvider аутоматски враћа AsyncValue, а виџет обрађује сва три стања кроз ref.watch.
final userProvider = FutureProvider((ref) async {
final api = ref.watch(apiProvider);
return await api.fetchUser();
});
class UserScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final userAsync = ref.watch(userProvider);
return userAsync.when(
data: (user) => UserWidget(user),
error: (e, _) => ErrorWidget(e.toString()),
loading: () => CircularProgressIndicator(),
);
}
}AsyncValue.when — метода за поклапање образаца сва три стања. Компилатор проверава да су сва три случаја обрађена — ако се заборави loading или error, код се неће компилирати. AsyncValue.whenData — само за data (ако loading/error нису потребни). AsyncValue.guard — омотач try-catch за конвертовање изузетка у AsyncError. keepAlive — заставица која спречава уништавање кеша провајдера при изласку из видокруга.
Кодогенерација — кључна карактеристика Riverpod 2.0+. Анотација @riverpod изнад функције аутоматски генерише провајдер са исправним типом, подршком за рефакторисање и аутоматским довршавањем. Кодогенерација користи riverpod_generator и build_runner. Програмер пише чисту функцију, а све остало — типови, класе, фабрички конструктори — генеришу се аутоматски.
@riverpod
String helloWorld(HelloWorldRef ref) {
return 'Hello World';
}
// Генерисано: final helloWorldProvider = Provider((ref) => 'Hello World');
@riverpod
class Counter extends _$Counter {
int build() => 0;
void increment() => state++;
}Notifier — нови API за мутабилно стање са кодогенерацијом. Notifier је класа са методом build() и методама за промену стања. За разлику од StateNotifier-а, Notifier не захтева засебну класу стања и пружа директан приступ state-у кроз getter/setter. Riverpod аутоматски генерише NotifierProvider за сваку Notifier класу са анотацијом @riverpod.
build_runner: кодогенерација се покреће командом dart run build_runner build. Генерисане датотеке имају суфикс .g.dart и увозе се у изворни код. При промени анотација или типова провајдера потребно је поново покренути кодогенерацију. Riverpod 2.x препоручује кодогенерацију за све нове пројекте — ручно креирање провајдера застарева.
Главне разлике Riverpod-а од Provider-а: независност од BuildContext-а, компилациона безбедност, уграђени рад са асинхроношћу, аутоматско кеширање и тестирање кроз override. Provider захтева BuildContext за приступ стању (context.watch, context.read), Riverpod користи WidgetRef и глобално декларисане провајдере.
| Карактеристика | Provider | Riverpod |
|---|---|---|
| Зависност од BuildContext-а | Да | Не |
| Компилациона провера | Не | Да (кроз @riverpod) |
| ProviderNotFoundException | Runtime | Немогућ |
| Асинхроност | Ручна | AsyncValue (уграђен) |
| Тестирање | Омотач у Provider-у | ProviderScope.overrideWith |
| Кеширање | Не | Аутоматско + keepAlive |
Миграција са Provider-а: Riverpod подржава ChangeNotifierProvider.adaptive за коришћење постојећих ChangeNotifier-а без преписивања. Постепена миграција: прво се нове функције пишу на Riverpod-у, затим се стари Provider-и замењују Riverpod провајдерима кроз адаптер. Оба пакета могу коегзистирати у истом пројекту, што омогућава миграцију без заустављања развоја.
Тестирање Riverpod-а се заснива на ProviderScope.overrideWith. Сваки провајдер се поништава унутар тестног ProviderScope-а без мокова и DI контејнера. ProviderContainer — изоловано окружење за тестове без Flutter-а (чисти Dart), које омогућава тестирање провајдера без приказивања виџета.
import 'package:flutter_test/flutter_test.dart';
import 'package:riverpod/riverpod.dart';
void main() {
test('Counter increments correctly', () {
final container = ProviderContainer();
container.read(counterProvider.notifier).increment();
expect(container.read(counterProvider), 1);
});
testWidgets('UI updates on increment', (tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [counterProvider.overrideWithValue(5)],
child: CounterScreen(),
),
);
expect(find.text('5'), findsOneWidget);
});
}ProviderContainer — без Flutter-а. Користите ProviderContainer за јединичне тестове провајдера без виџета. overrideWithValue — замена провајдера одређеном вредношћу. overrideWith — замена фабриком провајдера (за моковање услуга). autodispose — у тестовима проверите да ли се провајдер уништава при изласку из видокруга, помоћу container.dispose().
Често постављана питања
Riverpod — библиотека за управљање стањем са глобалним провајдерима, AsyncValue и кодогенерацијом. BLoC — архитектонски образац са Event → Stream → State. Riverpod је лакши за учење и има боље програмерско искуство кроз @riverpod анотације. BLoC пружа строгу изолацију пословне логике и праћење Event-а кроз BlocObserver. Избор зависи од парадигме пројекта: Riverpod је ближи Provider-у, BLoC — реактивним токовима.
Autodispose — механизам аутоматског уништавања провајдера када нико није претплаћен на њега. Подразумевано, сви Riverpod провајдери су autodispose: при изласку виџета из стабла, провајдер се уклања из меморије. keepAlive — заставица која искључује autodispose за провајдере који треба да живе увек (API клијенти, репозиторијуми, подешавања). Ово спречава цурење меморије — неискоришћени провајдери се аутоматски уништавају.
ref.invalidate — метода која присилно ресетује кеш провајдера. Након invalidate, провајдер се поново креира при следећем читању: FutureProvider поново извршава async функцију, StreamProvider се поново претплаћује на ток. Користите invalidate за присилно освежавање података (pull-to-refresh, промена корисника). ref.refresh — комбинација invalidate + читање: ресетује и одмах чита нову вредност у једној операцији.
Да. Riverpod 1.x ради само без кодогенерације — провајдери се креирају ручно кроз Provider(), StateNotifierProvider(), FutureProvider() итд. Riverpod 2.x подржава оба приступа. Без кодогенерације има више boilerplate-а, али нема зависности од build_runner-а и dart run build_runner build. За мале пројекте (до 30 провајдера) ручно креирање је оправдано, за велике — кодогенерација је обавезна.
Family — модификатор провајдера који прихвата спољни параметар. На пример, userProvider(123) — провајдер који учитава корисника са ID 123. Family провајдери кеширају резултат за сваки јединствени параметар посебно. Користите Family за листу елемената где се сваки елемент учитава по ID-у. Family модификатор је доступан за све типове провајдера: Provider.family, FutureProvider.family, StreamProvider.family.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође