Riverpod — ουσία, μεταγλώττιση εξαρτήσεων στο Flutter

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-02-19 Χρόνος ανάγνωσης: 7 λεπ

Riverpod — ένας μεταγλωττίσιμος διαχειριστής κατάστασης και εξαρτήσεων για Flutter, που δημιουργήθηκε από τον Rémi Roussel το 2021 ως διάδοχος του Provider. Το Riverpod λύνει τα θεμελιώδη προβλήματα του Provider: έλλειψη ελέγχου μεταγλώττισης, εξάρτηση από το BuildContext και δυσκολία με το ProviderNotFoundException. Σύμφωνα με τα δεδομένα του pub.dev, το πακέτο έχει συγκεντρώσει πάνω από 5 χιλιάδες likes και αντικαθιστά ενεργά το Provider σε νέα έργα.

Κύρια Σημεία

  • ProviderRef — αντικείμενο για πρόσβαση σε άλλους provider μέσα σε έναν provider
  • AsyncValue — περιτύλιγμα για ασύγχρονα δεδομένα με καταστάσεις loading/error/data
  • Notifier — κλάση για μεταβλητή κατάσταση με μεθόδους αλλαγής
  • ProviderScope — το ριζικό widget που διαχειρίζεται όλους τους provider
  • Code Generation — σχολιασμοί @riverpod για αυτόματη δημιουργία provider

Τι είναι το Riverpod;

Riverpod — μια βιβλιοθήκη για διαχείριση κατάστασης και έγχυση εξαρτήσεων στο Flutter που μεταγλωττίζει την περιγραφή των provider σε ασφαλή κώδικα Dart. Σε αντίθεση με το Provider, οι provider του Riverpod δεν είναι δεσμευμένοι στο BuildContext: δημιουργούνται καθολικά ή στο ProviderScope και είναι προσβάσιμοι από οπουδήποτε. Ο μεταγλωττιστής ελέγχει τους τύπους, τις εξαρτήσεις και την ακεραιότητα του γράφου provider στο στάδιο της δημιουργίας, εξαλείφοντας σφάλματα χρόνου εκτέλεσης όπως το ProviderNotFoundException.

Το Riverpod χρησιμοποιεί το μοντέλο override για δοκιμές: κάθε provider μπορεί να παρακαμφθεί μέσω του ProviderScope.overrideWith χωρίς να χρειάζεται δημιουργία υποκλάσεων ή mock διεπαφών. Αυτό καθιστά τη δοκιμή απομονωμένη: κάθε δοκιμή λαμβάνει το δικό της αντίγραφο του γράφου εξαρτήσεων που ελέγχεται πλήρως.

Σύμφωνα με το Flutter Community Survey 2025, το Riverpod κατατάσσεται τρίτο σε δημοτικότητα μετά το Provider και το BLoC. Ταυτόχρονα, το Riverpod είναι το ταχύτερα αναπτυσσόμενο πακέτο: +120% εγκαταστάσεις το 2024. Κύριοι λόγοι: ασφάλεια μεταγλώττισης, απουσία ProviderNotFoundException, ενσωματωμένη υποστήριξη ασύγχρονου προγραμματισμού μέσω του AsyncValue.

Τύποι Provider

Το Riverpod παρέχει 8 τύπους provider, ο καθένας για συγκεκριμένο σενάριο: 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 — το αντικείμενο που μεταβιβάζεται σε κάθε provider για πρόσβαση σε άλλους provider. ref.watch — εγγραφή σε αλλαγές, ref.read — μία ανάγνωση, ref.invalidate — επαναφορά προσωρινής μνήμης. Το ProviderRef αντικαθιστά το BuildContext από το Provider: οποιοσδήποτε provider μπορεί να διαβάσει άλλους provider χωρίς πρόσβαση στο δέντρο widget. Αυτό επιτρέπει τη δημιουργία γράφου εξαρτήσεων εκτός του επιπέδου UI.

ProviderScope — το υποχρεωτικό ριζικό widget για τη λειτουργία του Riverpod. Το ProviderScope αποθηκεύει όλους τους provider, διαχειρίζεται τον κύκλο ζωής τους και αποθηκεύει τιμές στην προσωρινή μνήμη. Χωρίς ProviderScope, η εφαρμογή θα καταρρεύσει με ProviderNotFoundException. Το ProviderScope μπορεί να είναι ένθετο — το ένθετο scope παρακάμπτει τους provider του γονέα, το οποίο χρησιμοποιείται για δοκιμές και απομόνωση λειτουργιών.

AsyncValue και εργασία με ασύγχρονο προγραμματισμό

AsyncValue — μια sealed κλάση του Riverpod για την αναπαράσταση ασύγχρονης κατάστασης. Το AsyncValue έχει τρεις παραλλαγές: AsyncData (επιτυχημένα δεδομένα), AsyncError (σφάλμα), AsyncLoading (φόρτωση). Αντί για χειροκίνητη εναλλαγή μεταξύ loading/error/data, κάθε FutureProvider ή StreamProvider επιστρέφει αυτόματα AsyncValue, και το widget διαχειρίζεται και τις τρεις καταστάσεις μέσω του 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 — μια σημαία που αποτρέπει την καταστροφή της προσωρινής μνήμης του provider κατά την έξοδο από το πεδίο ορατότητας.

Code Generation και @riverpod

Δημιουργία κώδικα — ένα βασικό χαρακτηριστικό του Riverpod 2.0+. Ο σχολιασμός @riverpod πάνω από μια συνάρτηση δημιουργεί αυτόματα έναν provider με τον σωστό τύπο, υποστήριξη αναδόμησης και αυτόματη συμπλήρωση. Η δημιουργία κώδικα χρησιμοποιεί riverpod_generator και build_runner. Ο προγραμματιστής γράφει μια καθαρή συνάρτηση και όλα τα άλλα — τύποι, κλάσεις, factory constructors — δημιουργούνται αυτόματα.

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 και εισάγονται στον πηγαίο κώδικα. Όταν αλλάζουν οι σχολιασμοί ή οι τύποι provider, η δημιουργία κώδικα πρέπει να επανεκκινηθεί. Το Riverpod 2.x συνιστά τη δημιουργία κώδικα για όλα τα νέα έργα — η μη αυτόματη δημιουργία provider καθίσταται ξεπερασμένη.

Riverpod vs Provider

Κύριες διαφορές του Riverpod από το Provider: ανεξαρτησία από το BuildContext, ασφάλεια μεταγλώττισης, ενσωματωμένη εργασία με ασύγχρονο προγραμματισμό, αυτόματη προσωρινή αποθήκευση και δοκιμή μέσω override. Το Provider απαιτεί BuildContext για πρόσβαση στην κατάσταση (context.watch, context.read), το Riverpod χρησιμοποιεί WidgetRef και καθολικά δηλωμένους provider.

ΧαρακτηριστικόProviderRiverpod
Εξάρτηση από BuildContextΝαιΌχι
Έλεγχος μεταγλώττισηςΌχιΝαι (μέσω @riverpod)
ProviderNotFoundExceptionRuntimeΑδύνατο
Ασύγχρονος προγραμματισμόςΧειροκίνηταAsyncValue (ενσωματωμένο)
ΔοκιμήΠεριτύλιγμα σε ProviderProviderScope.overrideWith
Προσωρινή αποθήκευσηΌχιΑυτόματη + keepAlive

Μετεγκατάσταση από Provider: Το Riverpod υποστηρίζει ChangeNotifierProvider.adaptive για χρήση υπαρχόντων ChangeNotifier χωρίς επανεγγραφή. Σταδιακή μετεγκατάσταση: πρώτα οι νέες λειτουργίες γράφονται με Riverpod, στη συνέχεια οι παλιοί Provider αντικαθίστανται με provider του Riverpod μέσω προσαρμογέα. Και τα δύο πακέτα μπορούν να συνυπάρχουν στο ίδιο έργο, επιτρέποντας τη μετεγκατάσταση χωρίς πάγωμα της ανάπτυξης.

Δοκιμή του Riverpod

Η δοκιμή του Riverpod βασίζεται στο ProviderScope.overrideWith. Κάθε provider παρακάμπτεται μέσα στο ProviderScope δοκιμής χωρίς mock και δοχεία DI. ProviderContainer — ένα απομονωμένο περιβάλλον για δοκιμές χωρίς Flutter (καθαρό Dart), που επιτρέπει τη δοκιμή provider χωρίς απόδοση widget.

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 για μοναδιαίες δοκιμές provider χωρίς widget. overrideWithValue — αντικατάσταση provider με συγκεκριμένη τιμή. overrideWith — αντικατάσταση με εργοστάσιο provider (για mock υπηρεσιών). autodispose — στις δοκιμές ελέγξτε αν ο provider καταστρέφεται κατά την έξοδο από το πεδίο ορατότητας, με container.dispose().

Συχνές Ερωτήσεις

Σε τι διαφέρει το Riverpod από το BLoC;

Riverpod — βιβλιοθήκη διαχείρισης κατάστασης με καθολικούς provider, AsyncValue και δημιουργία κώδικα. BLoC — αρχιτεκτονικό μοτίβο με Event → Stream → State. Το Riverpod είναι ευκολότερο στην εκμάθηση και έχει καλύτερη εμπειρία προγραμματιστή μέσω των σχολιασμών @riverpod. Το BLoC παρέχει αυστηρή απομόνωση επιχειρηματικής λογικής και παρακολούθηση Event μέσω του BlocObserver. Η επιλογή εξαρτάται από το παράδειγμα του έργου: το Riverpod είναι πιο κοντά στο Provider, το BLoC — στα αντιδραστικά ρεύματα.

Τι είναι το autodispose στο Riverpod;

Autodispose — ο μηχανισμός αυτόματης καταστροφής του provider όταν κανείς δεν είναι εγγεγραμμένος σε αυτόν. Από προεπιλογή, όλοι οι provider του Riverpod είναι autodispose: όταν το widget εγκαταλείπει το δέντρο, ο provider αφαιρείται από τη μνήμη. keepAlive — μια σημαία που απενεργοποιεί το autodispose για provider που πρέπει να ζουν πάντα (πελάτες API, αποθετήρια, ρυθμίσεις). Αυτό αποτρέπει διαρροές μνήμης — οι μη χρησιμοποιούμενοι provider καταστρέφονται αυτόματα.

Πώς λειτουργεί το ref.invalidate;

ref.invalidate — μια μέθοδος που αναγκάζει την επαναφορά της προσωρινής μνήμης του provider. Μετά το invalidate, ο provider αναδημιουργείται στην επόμενη ανάγνωση: το FutureProvider εκτελεί ξανά την async συνάρτηση, το StreamProvider εγγράφεται ξανά στη ροή. Χρησιμοποιήστε το invalidate για αναγκαστική ανανέωση δεδομένων (pull-to-refresh, αλλαγή χρήστη). ref.refresh — ένας συνδυασμός invalidate + ανάγνωση: επαναφέρει και διαβάζει αμέσως τη νέα τιμή σε μία λειτουργία.

Μπορεί το Riverpod να χρησιμοποιηθεί χωρίς δημιουργία κώδικα;

Ναι. Το Riverpod 1.x λειτουργεί μόνο χωρίς δημιουργία κώδικα — οι provider δημιουργούνται χειροκίνητα μέσω Provider(), StateNotifierProvider(), FutureProvider() κ.λπ. Το Riverpod 2.x υποστηρίζει και τις δύο προσεγγίσεις. Χωρίς δημιουργία κώδικα υπάρχει περισσότερος boilerplate, αλλά καμία εξάρτηση από build_runner και dart run build_runner build. Για μικρά έργα (έως 30 provider) η χειροκίνητη δημιουργία είναι δικαιολογημένη, για μεγάλα έργα η δημιουργία κώδικα είναι υποχρεωτική.

Τι είναι οι Family provider;

Family — ένας τροποποιητής provider που δέχεται μια εξωτερική παράμετρο. Για παράδειγμα, userProvider(123) — ένας provider που φορτώνει τον χρήστη με ID 123. Οι Family provider αποθηκεύουν το αποτέλεσμα για κάθε μοναδική παράμετρο ξεχωριστά. Χρησιμοποιήστε το Family για μια λίστα στοιχείων όπου κάθε στοιχείο φορτώνεται με ID. Ο τροποποιητής Family είναι διαθέσιμος για όλους τους τύπους provider: Provider.family, FutureProvider.family, StreamProvider.family.

Περίληψη

  • Riverpod — μεταγλωττίσιμος διαχειριστής κατάστασης, διάδοχος του Provider χωρίς ProviderNotFoundException
  • ProviderRef — αντικαταστάτης του BuildContext για πρόσβαση σε provider μέσα σε άλλους provider
  • AsyncValue — sealed κλάση με καταστάσεις loading/error/data για ασύγχρονα δεδομένα
  • Δημιουργία κώδικα @riverpod — αυτόματη εξαγωγή τύπων και εργοστασίων provider
  • ProviderScope.overrideWith — απομονωμένη δοκιμή χωρίς mock και δοχεία DI
  • Family — παραμετροποιημένοι provider με ατομική προσωρινή αποθήκευση
  • autodispose και keepAlive — αυτόματη διαχείριση του κύκλου ζωής των provider

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης