Riverpod topic
Refena for Riverpod developers
Introduction
You might have noticed that Refena is similar to Riverpod.
However, there are still some things that need your attention when you want to migrate from Riverpod to Refena.
Providers
Riverpod | Refena |
---|---|
Provider |
Provider for immutable values, ViewProvider for reactive values |
StateProvider |
StateProvider (no change) |
FutureProvider |
FutureProvider (no change) |
StreamProvider |
StreamProvider (no change) |
ChangeNotifierProvider |
ChangeNotifierProvider (no change) |
StateNotifierProvider |
NotifierProvider (drop the State part) |
NotifierProvider |
NotifierProvider (no change) |
AsyncNotifierProvider |
AsyncNotifierProvider (no change) |
Family providers:
Riverpod | Refena |
---|---|
Provider.family |
ViewProvider.family (only reactive version available) |
FutureProvider.family |
FutureProvider.family (no change) |
StreamProvider.family |
StreamProvider.family (no change) |
Other | Not supported |
New providers:
Refena | Description |
---|---|
ViewProvider |
The only provider that can watch other providers |
ReduxProvider |
A provider where you dispatch actions to get the new state |
No ConsumerWidget or ConsumerStatefulWidget
In Refena, there is no ConsumerWidget
or ConsumerStatefulWidget
.
You can access the providers directly in the build
method:
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = context.watch(counterProvider);
return Text('$counter');
}
}
AutoDispose
In Refena, providers never dispose themselves automatically.
Instead, you need to call ref.dispose(provider)
manually (usually in a StatefulWidget
's dispose
method):
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
@override
void dispose() {
context.dispose(counterProvider);
super.dispose();
}
@override
Widget build(BuildContext context) {
final counter = context.watch(counterProvider);
return Text('$counter');
}
}
Use ViewModelBuilder
or FamilyViewModelBuilder
widgets to dispose providers automatically.
Watching
Do not execute ref.watch
of the same provider multiple times
as only the last one will be used for the rebuild condition.
Instead, you should use Records to combine multiple values.
Widget build(BuildContext context) {
final (themeMode, locale) = context.watch(settingsProvider.select((settings) {
return (settings.theme, settings.locale);
}));
}
Access notifiers
A slight difference between Riverpod and Refena is how you access the notifiers.
Riverpod | Refena |
---|---|
ref.read(provider.notifier) |
ref.notifier(provider) |
Listener
In Riverpod, you can listen to a provider by calling ref.listen(provider, (prev, next) {})
.
In Refena, there are two ways to listen to a provider:
Outside the build
method:
final subscription = ref.stream(provider).listen((prev, next) {
// This callback is called whenever the provider changes
});
// Cancel the subscription when you don't need it anymore
subscription.cancel();
Inside the build
method:
final state = ref.watch(provider, listener: (prev, next) {
// This callback is called whenever the provider changes
});
Lifespan of Ref
Compared to Riverpod,
the ref
object in Refena is always available because it's just a wrapper around RefenaContainer
.
You will never need to worry about the lifespan of ref
.
NotifierProvider
In Refena, the NotifierProvider
looks like this:
final counterProvider = NotifierProvider<Counter, int>((ref) {
return Counter();
});
class Counter extends Notifier<int> {
@override
int init() => 0;
void increment() => state++;
}
Notice that there is a ref
parameter in the NotifierProvider
's builder function.
All providers have this parameter to allow you to use the Dependency Injection pattern.
Also, the Notifier
class has the init
method that returns the initial state.
This is similar to the build
method in Riverpod.
StateProvider
To change the state of a StateProvider
, there is a slight difference between Riverpod and Refena.
Riverpod | Refena |
---|---|
ref.read(provider.notifier).state++ |
ref.notifier(provider).setState((old) => old + 1) |
You need to access the notifier first to keep the code consistent with other providers.
Widgets
➤ ConsumerWidget
Before:
class MyPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final counter = ref.watch(counterProvider);
return Text('$counter');
}
}
After:
class MyPage extends StatelessWidget { // change to native widget
@override
Widget build(BuildContext context) {
final ref = context.ref; // get ref from context
final counter = ref.watch(counterProvider);
return Text('$counter');
}
}
➤ ConsumerStatefulWidget
Before:
class MyPage extends ConsumerStatefulWidget {
@override
ConsumerState<MyPage> createState() => _MyPageState();
}
class _MyPageState extends ConsumerState<MyPage> {
@override
Widget build(BuildContext context) {
final counter = ref.watch(counterProvider);
return Text('$counter');
}
}
After:
class MyPage extends StatefulWidget { // change to native widget
@override
State<MyPage> createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> with Refena { // add mixin
@override
Widget build(BuildContext context) {
// ref is available from the mixin
final counter = ref.watch(counterProvider);
return Text('$counter');
}
}
Ref.listenSelf
Before:
final provider = StateProvider<int>((ref) {
ref.listenSelf((int? prev, int next) {
print('state changed from $prev to $next');
});
return 0;
});
After:
final provider = StateProvider<int>((ref) {
return 0;
}, onChanged: (prev, next, ref) {
print('state changed from $prev to $next');
});
Ref.refresh
Instead of ref.refresh(provider)
, you should use ref.rebuild(provider)
instead.
Riverpod | Refena |
---|---|
ref.refresh(provider) |
ref.rebuild(provider) |
In contrast to Riverpod, ref.dispose
and ref.read
are not the same as ref.rebuild
.
ref.rebuild
will trigger rebuilds but ref.dispose
and ref.read
will not.
➤ Why the term "rebuild"?
Because in Refena there is a strict separation between rebuildable and non-rebuildable providers.
Rebuildable providers can use ref.watch
in their provider lambda and will be rebuilt when their dependencies change.
Those providers are ViewProvider
, FutureProvider
, and StreamProvider
.
Non-rebuildable providers cannot use ref.watch
and are mostly associated with notifier-focused providers
like StateProvider
, NotifierProvider
, and ReduxProvider
.
In context of ref.rebuild
, it means that only rebuildable providers can be rebuilt.
Classes
- RefenaContainer Introduction Riverpod Initialization
- The RefenaContainer holds the state of all providers. Every provider is initialized lazily.