Migrating from Riverpod to Creator
Closed this issue · 4 comments
Hi, thanks for the awesome package, it's so much easier to understand than Riverpod and it has really cleaned up the app I'm working on, especially when working with async data.
I found it easy to migrate. With Riverpod I was using ConsumerWidget
and ConsumerStatefulWidget
, which gave me access to a ref
in the build(BuildContext context, ref)
method. I saw the docs about the difference between Watcher's ref and context.ref
but I don't really understand what that means in practical terms. To make the compiler errors go away, I was able to do final ref = context.ref
at the top of my build method which made migrating easy, but I guess that's not a good thing to do?
Should I refactor all the Riverpod code that previously used ref in the build method to instead wrap a Watcher
at the root of the widget and do all my setup code in there? For example accessing services.
Thanks!
As I refactor more of the app, I got rid of my StateNotifier/StateNotifierProviders and I find myself not needing to use a ref at the top of the build method as much, instead I'm using Watcher where necessary and exposing state like in the counter example
However I noticed this example doesn't play well when you have classes that act as services which need to read this state, because in the example you expose state apis using ref.watch
, but now your service classes can't use the same helper methods. For example if I have a List<MyThing> downloadQueue
creator, and I want to expose a method bool isQueuedForDownload(Ref ref, MyThing thing)
, if I use ref.watch here, then this helper method can only be used in my widgets and not in the DownloadService class.
As you have notices, for ConsumerWidget and ConsumerStatefulWidget, you will likely want to replace them with a Watcher, and use ref from the Watcher. This way, the creator is disposed when the Watcher widget is disposed. context.ref
should be only used if you do not want to dispose the creator, or dispose the creator manually. For example:
import 'package:creator/creator.dart';
import 'package:flutter/material.dart';
/// Add a creator to stateful widget that got disposed automatically when the
/// state is disposed.
mixin CreatorMixin<T extends StatefulWidget> on State<T> {
// A creator that is typically set in initState and updated in didChangeWidget
// as needed.
Creator? _creator;
set creator(Creator value) {
if (_creator == null) {
_creator = value;
} else {
ref.dispose(_creator!);
_creator = value;
ref.watch(_creator!);
}
}
late Ref ref;
@override
void didChangeDependencies() {
ref = context.ref;
if (_creator != null) {
ref.watch(_creator!);
}
super.didChangeDependencies();
}
@override
void dispose() {
if (_creator != null) {
ref.dispose(_creator!);
}
super.dispose();
}
}
For your second question, what I typically do are:
-
Try to put logic heavy stuff in service/domain layer, without using creator. Creator could be the glue layer that does two things:
a. Glue service layer and UI to provide reactiveness.
b. Locate the service layer to make state change. -
Use derived creators.
bool isQueuedForDownload(Ref ref, MyThing thing)
sounds like a simple logic, that can be written in:
Creator<bool> isQueuedForDownload(MyThing thing) {
return downloadQueue.map((q) => q.contains(thing), args: ['isQueuedForDownload', thing]);
}
Hi, I hope you get what you need. Closing this issue but feel free to reopen if needed.