How to access Riverpod ref from non-widget classes, before Riverpod 1, we could use context.read, where we had access to global navigation context. How can we achieve something similar in Riverpod 1?
I've tried using a ProviderContainer and it's working, but need to wrap the MaterialApp with UncontrolledProviderScope to be able to access the container, this in turn prevents logging changes of StateNotifierProviders, which is very handy while debugging.
Could anyone help with this?
You probably don't want to use ProviderContainer directly unless you're mocking dependencies for your non-widget class when testing. Pass your non-widget class a Reader tear-off from a widget, e.g.
class YourObject {
YourObject(this.reader);
Reader? reader;
void someFunc() {
final dependency = reader?.call(someProvider);
}
}
// Inside a consumer widget
final myObject = YourObject(ref.watch)
Related
I am trying to create initialization function placed inside Cubit class (something that works alike initState() {} from State<>) to divide my UI classes from business logic classes. Is there a way to call internal function in Cubit extending class from super() part of constructor to initialize from DB or I can only use State extending class to create needed Cubit in BlocProvider?
The common good practice is to run some initiative function when providing the cubit to a widget using the .. operator. So in the create parameter to the BlocProvider you do something like this:
create: (_) => YourCubit()..initializingMethod(),
I have a very big, complex app with lots of pages with input forms with lots of fields (texts, date-selection, combo-boxes,...), so the state is rather big.
I'm switching from Provider to Riverpod, and the proposed way for state management seems to be primarily StateNotifierProvider. But that state is immutable, meaning you have to clone the complete state on every change.
What I would need is Providers ChangeNotifierProvider (that Riverpod has too), in order to be able to have mutable state and more control over when rebuilds on the listeners are triggered.
But Riverpod discourages the use of ChangeNotifierProvider, what makes me a little nervous. Maybe I didn't really understand the StateNotifierProvider concept.
So the question is, how can I use StateNotifierProvider to handle complex state? Is it really necessary to always clone the state?
Edit: With complex I mean a larger number of fields (e.g. 50) and also lists and object-structures. You could clone that, but it's expensive and it doesn't feel natural/smart to do this on every little change. Is this really the intended way? Is there a way around cloning while still using StateNotifierProvider?
First of all i wouldn't recommend using ChangeNotifier(mutable state) over StateNotifier(immutable state), as best practical way of state management in flutter is to use immutable state.
You can use freezed and freezed_annotation to help you copy the state each time you want to emit new one, you just copy the old state and change what you need.
Something like this
state = state.copyWith(var1: "new var");
here is an example of complex state using freezed
add freezed_annotation to your dependencies
add freezed and build_runner to your dev_dependencies
create your state complex_state.dart something like this, change the factory constructor to match your needs
import 'package:freezed_annotation/freezed_annotation.dart';
part 'complex_state.freezed.dart';
#freezed
class ComplexState with _$ComplexState {
const ComplexState._();
const factory ComplexState({
required String var1,
required double var2,
required bool var3,
required int var4,
}) = _ComplexState;
}
run this command in terminal
flutter pub run build_runner build --delete-conflicting-outputs
freezed will generate your ComplexState as well as some other useful methods like copyWith() which now you can use in your StateNotifier
Here is an example of using StateNotifier with Riverpod
final complexStateNotifierProvider = StateNotifierProvider(
(ref) {
const initialState = ComplexState(
var1: "var1",
var2: 90.0,
var3: true,
var4: 90,
);
return ComplexStateNotifier(initialState);
},
);
abstract class ComplexStateEvent {}
class ComplexStateEventDoSomething implements ComplexStateEvent {}
class ComplexStateNotifier extends StateNotifier<ComplexState> {
ComplexStateNotifier(ComplexState state) : super(state);
void onEvent(ComplexStateEvent event) {
if (event is ComplexStateEventDoSomething) {
state = state.copyWith(var1: "new value");
}
}
}
Note: freezed also support nullable and default constructor parameters which you may use for the initial state, read more on its pub.dev page
Here is my architecture, I have two statfull widgets: ProfilePicture, RoundedProfilePicture. ProfilePicture is a statefull widget and it has complicated custom code which is very prone to error so I dont want to copy paste it, it utilizes variables from Picture abstract class. (ie. fetches server data and stores them inside variables of Picture). What I want to do is I want to extend from this widgets state so that I can create a new widget RoundedProfilePicture. This widgets state will basically utilize the complicated code from ProfilePicture's state with inheritance and it will add small extra logic to it. So I thought inheritance is the best choice here. Here is what I tried so far
class ProfilePictureState extends State<ProfilePicture> implements Picture{
// SOME LONG COMPLICATED 500 LINE OF CODE
}
class RoundedProfilePictureState extends ProfilePictureState {
#override
void initState() {
super.initState(); // this calls ProfilePictureState.initState() call. I want to call State<ProfilePicture>.initState()
}
}
My problem is void initState() in RoundedProfilePictureState requires me to make a super.initState() call. This call makes a ProfilePictureState.initState() call. I want to call State<ProfilePicture>.initState() because I want to add a different logic in my init state call. So the structure is:
----abstract State class
---ProfilePictureState
--RoundedProfilePictureState
How can I make a call to abstract State class's initState method from RoundedProfilePictureState? Is this possible?
Since no one answered, here is how I solved this. I think this is impossible to achieve. What I did is I moved the code and all the variables to a mixin. Took like an hour or two to do so. I achieved my needs tho.
I am learning flutter and it would be easy to understand if i get an example of listening to changes in dart only package aka riverpod.
I was using Provider in class where there is no stateless or stateful widget(basically no Buildcontext).
So i was passing context from other class to this class like this although it works but i feel its not right way.
class SaveToLocal {
final BuildContext context;
SaveToLocal(this.context);
foo(){
//used context here
var model = Provider.of<MyList>(context, listen: false);
}
}
then i came across riverpod where dart only package doesn't require context but i was confused as i was not able to find things like ProviderScope.I searched also examples but most of them are on flutter_riverpod not on riverpod.
This example on pub.dev is using FutureProvider what if i wanna just fetch from model
By model i mean
class MyList extends ChangeNotifier {
List<String> _myList = [];
List<String> get myList => _myList;
foo(){
//some task here
notifyListeners();
}
So here are questions
1.Is this appropriate way of using context
2.Is there any other way that i can achieve this
3.Can i get a simple example like CounterApp where read and watch is shown?
Sorry i got confused i thought riverpod is only for dart only classes but it was for non-flutter apps(saying after reading reso coder's article), as i tried further with flutter_riverpod and used without context (as said by Mr Random)and worked fine.
class SaveToLocal {
final container = ProviderContainer();
foo(){
var model = container.read(listProvider);
}
For the first question the answer is no: context shouldn't be saved in some external class since it might lead to issues.
I link you these example of how to manage state properly across the application:
https://medium.com/codechai/provider-state-management-in-flutter-d453e73537c5
In my Flutter app I'm using get_it to retrieve non-widget related dependencies (e.g. an http service).
Most of the Flutter Widget examples that I see don't save any variables in the widget class, but rather they retrieve them in the #build method. This makes sense for dependencies that rely on the context, but for other dependencies that don't rely on context is it ok to save them as instance variables when the constructor is called?
I'm not very familiar with the Widget lifecycle so I'm not sure if this could lead to memory leaks, etc if widget instances are discarded. I want to save them as variables because it more clearly documents the dependencies.
Fetching in #build
class MyWidget extends StatelessWidget {
#override
Widget build() {
var myService = GetIt.instance.get<MyService>();
}
}
Storing in instance variable
class MyWidget extends StatelessWidget {
final MyService myService;
MyWidget(): myService = GetIt.instance.get();
#override
Widget build() {
}
}
I see no reason why you couldn't store them as an instance variable.
You obviously cannot dispose of instance variables in a manual/clean way and must rely on the garbage collector to do what it should. That could be seen as a drawback. In your case I assume that isn't an issue since the service might live throughout the lifecycle of the app.
If you are very nervous about memory leaks, use a stateful widget and dispose the things needing disposal.
If you very clearly want to document the dependencies you could go further and require it as a parameter to the widget. It would also make the widget more easily testable.