Riverpod — суштина, компилација зависности у Flutter-у

Аутор: IT Sectr Објављено: 2026-02-19 Време читања: 7 мин

Riverpod — компилабилни менаџер стања и зависности за Flutter, креиран од страна Rémi Roussel-а 2021. године као наследник Provider-а. Riverpod решава фундаменталне проблеме Provider-а: недостатак компилационе провере, зависност од BuildContext-а и тешкоће са ProviderNotFoundException. Према подацима pub.dev, пакет је прикупио преко 5 хиљада лајкова и активно замењује Provider у новим пројектима.

Главно

  • ProviderRef — објекат за приступ другим провајдерима унутар провајдера
  • AsyncValue — омотач за асинхроне податке са стањима loading/error/data
  • Notifier — класа за мутабилно стање са методама измене
  • ProviderScope — коренски виџет који управља свим провајдерима
  • Code Generation — анотације @riverpod за аутоматско генерисање провајдера

Шта је Riverpod?

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).

Dart
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 и рад са асинхроношћу

AsyncValue — sealed класа Riverpod-а за представљање асинхроног стања. AsyncValue има три варијанте: AsyncData (успешни подаци), AsyncError (грешка), AsyncLoading (учитавање). Уместо ручног пребацивања између loading/error/data, сваки FutureProvider или StreamProvider аутоматски враћа AsyncValue, а виџет обрађује сва три стања кроз ref.watch.

Dart
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 — заставица која спречава уништавање кеша провајдера при изласку из видокруга.

Code Generation и @riverpod

Кодогенерација — кључна карактеристика Riverpod 2.0+. Анотација @riverpod изнад функције аутоматски генерише провајдер са исправним типом, подршком за рефакторисање и аутоматским довршавањем. Кодогенерација користи riverpod_generator и build_runner. Програмер пише чисту функцију, а све остало — типови, класе, фабрички конструктори — генеришу се аутоматски.

Dart
@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 vs Provider

Главне разлике Riverpod-а од Provider-а: независност од BuildContext-а, компилациона безбедност, уграђени рад са асинхроношћу, аутоматско кеширање и тестирање кроз override. Provider захтева BuildContext за приступ стању (context.watch, context.read), Riverpod користи WidgetRef и глобално декларисане провајдере.

КарактеристикаProviderRiverpod
Зависност од BuildContext-аДаНе
Компилациона провераНеДа (кроз @riverpod)
ProviderNotFoundExceptionRuntimeНемогућ
АсинхроностРучнаAsyncValue (уграђен)
ТестирањеОмотач у Provider-уProviderScope.overrideWith
КеширањеНеАутоматско + keepAlive

Миграција са Provider-а: Riverpod подржава ChangeNotifierProvider.adaptive за коришћење постојећих ChangeNotifier-а без преписивања. Постепена миграција: прво се нове функције пишу на Riverpod-у, затим се стари Provider-и замењују Riverpod провајдерима кроз адаптер. Оба пакета могу коегзистирати у истом пројекту, што омогућава миграцију без заустављања развоја.

Тестирање Riverpod-а

Тестирање Riverpod-а се заснива на ProviderScope.overrideWith. Сваки провајдер се поништава унутар тестног ProviderScope-а без мокова и DI контејнера. ProviderContainer — изоловано окружење за тестове без Flutter-а (чисти Dart), које омогућава тестирање провајдера без приказивања виџета.

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 разликује од BLoC-а?

Riverpod — библиотека за управљање стањем са глобалним провајдерима, AsyncValue и кодогенерацијом. BLoC — архитектонски образац са Event → Stream → State. Riverpod је лакши за учење и има боље програмерско искуство кроз @riverpod анотације. BLoC пружа строгу изолацију пословне логике и праћење Event-а кроз BlocObserver. Избор зависи од парадигме пројекта: Riverpod је ближи Provider-у, BLoC — реактивним токовима.

Шта је autodispose у Riverpod-у?

Autodispose — механизам аутоматског уништавања провајдера када нико није претплаћен на њега. Подразумевано, сви Riverpod провајдери су autodispose: при изласку виџета из стабла, провајдер се уклања из меморије. keepAlive — заставица која искључује autodispose за провајдере који треба да живе увек (API клијенти, репозиторијуми, подешавања). Ово спречава цурење меморије — неискоришћени провајдери се аутоматски уништавају.

Како ради ref.invalidate?

ref.invalidate — метода која присилно ресетује кеш провајдера. Након invalidate, провајдер се поново креира при следећем читању: FutureProvider поново извршава async функцију, StreamProvider се поново претплаћује на ток. Користите invalidate за присилно освежавање података (pull-to-refresh, промена корисника). ref.refresh — комбинација invalidate + читање: ресетује и одмах чита нову вредност у једној операцији.

Може ли се Riverpod користити без кодогенерације?

Да. Riverpod 1.x ради само без кодогенерације — провајдери се креирају ручно кроз Provider(), StateNotifierProvider(), FutureProvider() итд. Riverpod 2.x подржава оба приступа. Без кодогенерације има више boilerplate-а, али нема зависности од build_runner-а и dart run build_runner build. За мале пројекте (до 30 провајдера) ручно креирање је оправдано, за велике — кодогенерација је обавезна.

Шта су Family провајдери?

Family — модификатор провајдера који прихвата спољни параметар. На пример, userProvider(123) — провајдер који учитава корисника са ID 123. Family провајдери кеширају резултат за сваки јединствени параметар посебно. Користите Family за листу елемената где се сваки елемент учитава по ID-у. Family модификатор је доступан за све типове провајдера: Provider.family, FutureProvider.family, StreamProvider.family.

Резиме

  • Riverpod — компилабилни менаџер стања, наследник Provider-а без ProviderNotFoundException
  • ProviderRef — замена за BuildContext за приступ провајдерима унутар других провајдера
  • AsyncValue — sealed класа са стањима loading/error/data за асинхроне податке
  • Кодогенерација @riverpod — аутоматско закључивање типова и фабрика провајдера
  • ProviderScope.overrideWith — изоловано тестирање без мокова и DI контејнера
  • Family — параметризовани провајдери са индивидуалним кеширањем
  • autodispose и keepAlive — аутоматско управљање животним циклусом провајдера

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође