How can I dispose ChangeNotifierProvider<T>.value( ) in Flutter - flutter

how can I dispose ChangeNotifierProvider.value( ) in Flutter
so there will be no memory leaks
I made the object that is as a value to the ChangeNotifierProvider.value( ) as a singleton
cause I need more than page to share their state in one object

ChangeNotifierProvider automatically calls the dispose() method of the state when it is needed. You need not do anything about that. They talk about this in this video:Pragmatic State Management in Flutter (Google I/O'19)

You need to use the dispose() method of State or the default constructor of ChangeNotifierProvider. The latter automatically disposes of the object created in the create function.
I wonder why you are using ChangeNotifierProvider.value() instead of ChangeNotifierProvider(), but assuming you want to pass the value to a different page, you can combine both as follows.
ChangeNotifierProvider<Foo>(
create: (context) => Foo(), // this Foo object is disposed of automatically
)
Somewhere down the tree:
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context2) => Provider<Foo>.value(
value: Provider.of<Foo>(context, listen: false),
child: NextPage(),
),
),
)
Note: You won't have to do this if you provide the value from above MaterialApp.

Related

Do I have to specifically call dispose() when I use Provider<T> class?

I was reading the documentation and it states that ChangeNotifierProviders will automatically be disposed.
https://pub.dev/packages/provider#existing-providers
Provider
The most basic form of provider. It takes a value and exposes it, whatever the value is.
ChangeNotifierProvider
A specification of ListenableProvider for ChangeNotifier. It will automatically call ChangeNotifier.dispose when needed.
So, when I'm using a basic Provider class as the top of my widget tree, do I need to dispose() it in my app's lifecycle? Given it will not be automatically disposed, and the reason behind?
void main() {
runApp(
Provider(
create: (context) => MemberRepository(),
child: const MyApp(),
),
);
}

Flutter: accessing providers from other providers

For my flutter project, I am using the following multiple providers below:
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<FirstProvider>(
create: (context) => FirstProvider(),
),
ChangeNotifierProvider<SecondProvider>(
create: (context) => SecondProvider(),
),
ChangeNotifierProvider<ThirdProvider>(
create: (context) => ThirdProvider(),
),
ChangeNotifierProvider<FourthProvider>(
create: (context) => FourthProvider(),
),
],
child: const MainApp(),
);
}
Because sometimes I need to either get data or call functions from different providers from another provider, I am using it like this:
//First Provider
class FirstProvider with ChangeNotifier {
void callFunctionFromSecondProvider({
required BuildContext context,
}) {
//Access the SecondProvider
final secondProvider= Provider.of<SecondProvider>(
context,
listen: false,
);
secondProvider.myFunction();
}
}
//Second Provider
class SecondProvider with ChangeNotifier {
bool _currentValue = true;
void myFunction(){
//Do something
}
}
The callFunctionFromSecondProvider()of the FirstProvider is called from a widget and it will call myFunction() successfully, most of times.
Depending on the complexity of the function, I am sometimes experiencing that I can't access the SecondProvider, presumably due to context being null, when the widget state changes.
I am reading some documents online regarding provider, and they are suggesting changenotifierproxyprovider for what I understood as 1 to 1 provider relationship.
However, in my case, one provider needs to be accessed by multiple providers and vice versa.
Question:
Is there a more appropriate way that I can approach my case where one provider can be accessed by multiple providers?
EDIT:
Accessing provider should also be able to access different variable values without creating a new instance.
Instead of passing context to the callFunctionFromSecondProvider function add the second provider as the parameter. So the function looks like the below.
Not sure this is the correct way of doing that but my context null issue was fixed this way.
void callFunctionFromSecondProvider({
required SecondProvider secondProvider,
}) {
secondProvider.myFunction();
}
}
Alright.
So it looks like Riverpod by the same author is the way to go as it addresses alot of flaws such as Provider being dependent on the widget tree, in my case, where the underlying issue came from.
—--------
For the time being, I still need to use the provider and for a quick and dirty solution, I am providing the context of not only the current widget that I am trying to access the provider, but also passing the parent context of the widget directly, so that in case a modal (for example) is closed, then any subsequent provider call can still be executed using the parent context.
Hope this helps.

How can we use the same instance of widget (with no rebuild) in a Navigator in flutter?

How can we reuse same widget with the same state, just building it once, and rebuilding only on changes at app state model (obviously currently it is being rebuilt each time when I trigger the Navigator)?
Navigator.of(context).push( MaterialPageRoute(
builder: (context) => SchedulerView(), ), );
SchedulerView is the target widget.
Thank you.
I'm guessing that your issue is not the rebuild (which is a call of the build() function triggered by the framework) but the creation of another SchedulerView instance because the Navigator calls the constructor SchedulerView()...?
If so, can you achieve your objective by instantiating only once and then using it by reference?
var schedulerView = SchedulerView();
...
Navigator.of(context).push( MaterialPageRoute( builder: (_) => schedulerView));

Dispatch first bloc event or cubit method, on page start inside StatelessWidget

I have 10 buttons in main menu of my app and each of them contains BlocBuilder inside them.
So when I click on those buttons to open a new page, I want to dispatch the first event, but I don't know how. I can change all classes to stateful widget and then call bloc.dispatch(event) inside initialState() function, but I would like to discover another way, and not sure whether it's the best way
In order to trigger the first event/method call inside BlocBuilder I had to add bloc argument and give parameter provided my BlocProvider, only after this I managed to call my method.
BlocBuilder<MyCubit, MyState>(
bloc: BlocProvider.of<MyCubit>(context)..myFunction(),
builder: (BuildContext context, state) {
//Your code...
}
you can use .. operator to add event while declaring like
BlocProvider(
create: (context) => FirstBloc()..add(InitialiEvent()), // <-- first event,
child: BlocBuilder<FirstBloc, FirstState>(
builder: (BuildContext context, state) {
...
},
),
or you can do it inside the initState method as well

Provider not accesable when Navigate to new screen

have a problem that I'm sitting on couple of days now.
have an app where:
depending of AUTH state, 'LoginScreen' or 'MainScreen' is Shown.
in MainScreen I setUp bottomNavigation with screens (HomeScreen, ShoppingScreen,MyFavorites)
I set up there as well my StreamProviders(those depend on Auth) by using MultiProvider
on HomeScreen when I User Provider.of(context) it works like it should
but when I use :
`Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ProfileScreen(),
),
);
` and use Provider.of(context) there I get "Could not find correct Provider....above this...widget"
I read some issues on that and solution there was to decler providers above MaterailApp which in my case I can not do because I can set up thoese only after Auth is successfull.
Tryed passing context(from HomeScreen) to ProfileScreen(through constructor) and that work but when value changed of UserData it did not update the screen (guessing beacause of diffrent 'contexts')
What am I doing wrong in here,any Ideas?:S
Providers are "scoped".
This means that if they are placed inside a screen, they aren't accessible outside that screen.
Which means that if a provider is scoped but needs to be accessed outside of the route it was created in, we have two solutions:
un-scope the provider. This involves moving the provider to a common ancestor of both widgets that needs to obtain the value.
If those two widgets are on two different Routes, then it basically mean "move the provider above MaterialApp/CupertinoApp.
manually pass the provider to the new screen (needed when using Navigator.push)
The idea is, instead of having one provider, we have two of them, both using the same value as explained here See How to scope a ChangeNotifier to some routes using Provider? for a practical example.
For Navigator.push, this can look like:
final myModel = Provider.of<MyModel>(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
ChangeNotifierProvider.value(
value: myModel,
child: MyScreen(),
),
),
);
Please make sure that you application's root widget is Provider Widget, it should event be the parent of MaterialWidget. If this is already the case I will need your code to look into. Something like this
class AppState {
User loggedInUser;
bool get isLoggedIn {
return loggedInUser != null;
}
// Other states as per the requirements
// ...
}