Flutter ChangeNotifierProxyProvider ChangeNotifier in create needs arguments - flutter

I am in the process of programming my app and need to use a ChangeNotifierProxyProvider. Unfortunately my ChangeNotifier (in this case Entries) needs 3 positional arguments. I already managed to specify the arguments in update, but how can I do it in create?
I would be very happy about an answer, because I can't find anything on the internet.
Hey!
I am in the process of programming my app and need to use a ChangeNotifierProxyProvider. Unfortunately my ChangeNotifier (in this case Entries) needs 3 positional arguments. I already managed to specify the arguments in update, but how can I do it in create?
I would be very happy about an answer, because I can't find anything on the internet.
Here is my code for the providers: []:
providers: [
ChangeNotifierProvider.value(value: Auth()),
ChangeNotifierProxyProvider<Auth, Entries>(
create: (_) => Entries(),
update: (_, auth, previousEntries) => Entries(
auth.token,
auth.userId,
previousEntries == null ? [] : previousEntries.items,
),
),
],

In this question ChangeNotifierProxyProvider not initiated on build the author did this:
return MultiProvider(
providers: [
ChangeNotifierProvider<WhatEver>(create: (context) => WhatEver()),
ChangeNotifierProxyProvider<AuthProvider, ProductList>(
create: (_) => ProductList(Provider.of<AuthProvider>(context, listen: false)),
update: (_, auth, productList) => productList..reloadList(auth)
),
],
i.e. he used Provider.of to get the AuthProvider required by his ProductList. You could use a similar approach, but I expect it will need to be created in a parent context.
Note that the answer to the other question pointed out the need to use lazy: false. So here is your example:
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => AuthService(),
builder: (context, _) => MultiProvider(
providers: [
ChangeNotifierProxyProvider<AuthService, Entries>(
create: (_) =>
Entries(Provider.of<AuthService>(context, listen: false).user),
lazy: false,
update: (_, auth, previousEntries) => Entries(
auth.user,
),
),
],
child: MaterialApp(
Obviously you no longer need to use MultiProvider, but it does not hurt.

Related

Some Providers not working in modal bottom sheet

Problem: Some of my providers cannot be found in the context above the modal bottom sheet. Example:
Error: Could not find the correct Provider above
this ModalEnterTask Widget
This happens because you used a BuildContext that does not include
the provider of your choice.
All Providers are definetly above the widget opening the modal sheet. One provider is actually working. That one is created above the material app. The ones not working are created in the build method of my tabs screen. I've been using them sucesfuly all throughout the app. Why can they not be found in the modal sheet?
My theory: The context used for the modal sheet is dependend on the Material app context; not on the context from where it is opened in the widget tree. Correct?
Solution: I don't know. I can't move the providers up to where the working Provider sits. They need context information (edit: MediaQuery information, not context), so they cannot be initialized before the material app.
Code:
MyApp State...this is where I initialize the provider that works:
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MainElementList(widget.mainElementList),
),
ChangeNotifierProvider(
create: (context) => ScrollControllerToggles(),
)
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Three Things',
initialRoute: '/',
routes: {
'/': (ctx) => TabsScreen(),
},
),
);
}
}
The TabsScreen(), here I initialize the Providers that do not work in the modal sheet:
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (ctx) => CustomColors(customColorScheme),
),
//
ChangeNotifierProvider(
create: (ctx) => DimensionsProvider(
appBarHeight: appBarHeight,
availableHeight: availableHeight,
availableWidth: availableWidth),
),
//
ChangeNotifierProvider(
create: (ctx) => CustomTextStyle(availableHeight, customTextTheme),
),
],
child: Scaffold(//body: function picking pages)
Calling the modal sheet:
return GestureDetector(
onTap: () => showModalBottomSheet(
context: context,
builder: (bctx) => ModalEnterTask(positionTask: positionTask),
),
//
child: Center(//container with an icon)
The widget called in the builder of the the modal sheet:
class ModalEnterTask extends StatelessWidget {
late String positionTask;
ModalEnterTask({required String this.positionTask, Key? key})
: super(key: key);
#override
Widget build(BuildContext context) {
//RESPONSIVENESS
final availableHeight =
Provider.of<DimensionsProvider>(context).availableHeight;
return Column(
children: [
SizedBox(
height: 10,
),
//
IconButton(
icon: Icon(Icons.plus_one),
onPressed: () {
Provider.of<MainElementList>(context, listen: false)
.changeSingleTaskPhase(0, positionTask);
Navigator.of(context).pop();
},
),
],
);
}
}
DimensionProvider > doesn't work
MainElementList > works
As I can see you are getting error because your provider is not in the tree which you are calling so its better to include all providers in the main and you will be able to resolve this issue. Here is how you do that
void main() async {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CustomColors()),
ChangeNotifierProvider(create: (context) => DimensionsProvider()),
ChangeNotifierProvider(create: (context) => MainElementList()),
ChangeNotifierProvider(create: (context) => ScrollControllerToggles()),
],
child: MyApp(),
),
);
}
I think this solution will work in your case... Have a try and let me know
Note: I can see that you are passing some parameters in providers so just change a structure little bit and assign values to providers where you are initializing them
I got same error when using method showModalBottomSheet.
I try to use BlocBuilder in the widget that was open as a modal bottom sheet, then i got
BlocProvider.of() called with a context that does not contain a MyBloc
My solutions is:
pass the value as a parameter of widget. In my case is:
showModalBottomSheet(
context: context,
builder: (BuildContext btsContext) {
return CartBottomSheetPage(
cartBloc: BlocProvider.of<MyBloc>(context),
);
},
);
NOTE: you must get the value from parent context like this:
BlocProvider.of<MyBloc>(context)
not like this:
BlocProvider.of<MyBloc>(btsContext)

Flutter adding ChangeNotifierProvider by list

I am trying to add a list of ChangeNotifierProviders. I receive no errors but looking at the Inspector is just shows ChangeNotifierProvider<ChangeNotifier> instead of the name of the ChangeNotifierProvider<nameofprovider> provided in the list. List<ChangeNotifier> changeNotifiers = [nameofprovider()]; The Net is constant and shows correctly with ChangeNotifierProvider<Net>.
home: MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Net()),
for (ChangeNotifier changeNotifier in changeNotifiers)
ChangeNotifierProvider(create: (_) => changeNotifier),
],
child: const MainPage(),
),

How to use more than one ChangeNotifierProvider in Flutter?

I recently started using provider for my state management and I know how to use one at a time.
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ChangeNotifierProvider<Reader>(
create: (context) => new Reader(),
child: _HomeBody(),
),
);
}
}
But now I have two different classes and I want to add both of them and be able to access in my widget tree.
How can I add more than one ChangeNotifierProvider in Flutter?
One option (not recommended) is to nest 2 Providers:
ChangeNotifierProvider<Reader>(
create: (_) => Reader(),
child: ChangeNotifierProvider<SomethingElse>(
create: (_) => SomethingElse(),
child: ChangeNotifierProvider<AnotherThing>(
create: (_) => AnotherThing(),
child: someWidget,
),
),
),
This is not recommended because, as the documentation states:
When injecting many values in big applications, Provider can rapidly become pretty nested:
But, another suggestion from the Provider package itself is to use the MultiProvider:
MultiProvider(
providers: [
ChangeNotifierProvider<Reader>(create: (_) => Reader()),
ChangeNotifierProvider<SomethingElse>(create: (_) => SomethingElse()),
ChangeNotifierProvider<AnotherThing>(create: (_) => AnotherThing()),
],
child: _HomeBody(),
)
Both approaches work the same but the second one is more readable. As the documentation words:
The behavior of both examples is strictly the same. MultiProvider only changes the appearance of the code.
Example adapted from the provider flutter package page and adapted to your case.
You could use MultiProvider
This guide helped me, hope it will help you too...
MultiProvider(
providers: [
ChangeNotifierProvider<Counter>(builder: (context) => Counter(0)),
ProxyProvider<Counter, ThemeSwitch>.custom(
builder: (context, counter, previous) {
final theme = previous ?? ThemeSwitch(ThemeState.light);
theme.themeState =
(counter.value > 5) ? ThemeState.dark : ThemeState.light;
return theme;
},
dispose: (context, value) => value.dispose(),
providerBuilder: (_, value, child) =>
ChangeNotifierProvider.value(notifier: value, child: child),
),
],
)

Why the constructor of class isn't executed when using provider in flutter?

void main() {
MainStream.init();
runApp(
MultiProvider(
providers: [
Provider(
create: (context) => Test(context),
),
],
child: MyApp()));
}
class Test {
Test(BuildContext context) {
print("Test");
}
}
In this test code I would expect that "Test" is printed out when my App starts but it doesn't. What I'm doing wrong? I saw examples which initializes the providers like this.
According to the provider documentation the create callback is lazy-loaded so this is expected behavior. If you pass "lazy: false" it should work as you expected:
Provider(
create: (context) => Test(context),
lazy: false
),

A Provider was used after being disposed - Multiprovider

After I added the dependency of ProfileLogic to LocationLogic I get the following error when the app starts:
I/flutter (14674): A LocationLogic was used after being disposed.
I/flutter (14674): Once you have called dispose() on a LocationLogic, it can no longer be used.
These are my providers:
providers: [
ChangeNotifierProvider(builder: (_) => ConnectivityLogic()),
ChangeNotifierProxyProvider<ConnectivityLogic, ProfileLogic>(
builder: (context, connectivity, previousMessages) =>
ProfileLogic(connectivity.isOnline),
initialBuilder: (BuildContext context) => ProfileLogic(false),
),
ChangeNotifierProxyProvider<ProfileLogic, LocationLogic>(
builder: (context, profileLogic, previousMessages) =>
LocationLogic(profileLogic.profile),
initialBuilder: (BuildContext context) => LocationLogic(null),
),
ChangeNotifierProvider(builder: (_) => SignUpModel()),
ChangeNotifierProxyProvider<ConnectivityLogic, WorkLogic>(
builder: (context, connectivity, previousMessages) =>
WorkLogic(connectivity.isOnline),
initialBuilder: (BuildContext context) => WorkLogic(false),
),
ChangeNotifierProvider(builder: (_) => OrderLogic()),
]
The strange thing is that everything works properly, even with that error.
I think you disposed of a widget that holds those providers. Try to move desired providers higher in the tree. So if you have:
MaterialApp(
home: MultiProvider(
providers: [...],
child: child,
)
)
Do something like:
MultiProvider(
providers: [...],
child: MaterialApp(
home: child,
)
)
If this won't help you need to provide more context. eg. Whats widget tree like.