So when my app is loading, I load multiple providers that get data from Firebase Firestore. The second one relies on data from the first one. How should I go about loading the data from the first one, then once that's complete, loading the data for the second one, then displaying the app. Should I nest a few FutureBuilders together? That sounds messy. Any ideas? Thanks!
MultiProvider(
providers: [
ChangeNotifierProvider<UserState>(create: (_) => UserState()),
ChangeNotifierProvider<JournalsState>(
create: (_) => JournalsState(
Provider.of<UserState>(context, listen: false)
.firebaseUser
.uid)),
],
child: ...
Related
I'm using Flutter provider, and I want to access a provider from another provider without using context.
I found many documentations on the internet explaining how to use ProxyProvider, but no one of the implementations was working (I think it's related to the last update 3 months ago).
I posted this question in StackOverflow after encountering a problem in ProxyProvider, but I didn't get an answer.
So now I'm just searching for any way to use a provider inside another one without using context.
You mean a Provider or the service that the Provider encapsulates? If it's the latter, the way that I've done it is by calling a provided service inside another provided service, given that they are all under the same MultiProvider. For example, I have two provided services:
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => FirstService()
),
ChangeNotifierProvider(
create: (_) => SecondService()
),
]
)
FirstService looks like this:
class FirstService extends ChangeNotifier {
List<String> getListOfValues() {
return ['a', 'b', 'c'];
}
}
and for example, I need to access those values from the SecondService, then I do this:
class SecondService extends ChangeNotifier {
void pullDataFromService(BuildContext context) {
FirstService firstService = Provider.of<FirstService>(context, listen: false);
var values = firstService.getListOfValues();
}
}
So pretty much you pass a context to a method where you need to access a provided service and you extract it from the Provider as normal, then access its functionality from the other service where you fetch it.
In my application, I have many providers, for instance notifications, auth, profile, conversations, trips, etc... And now I need to reinitialize all providers, when user logout. Otherwise old user data will still stay in provider, and new user will get them.
After spending my day, I solved the problem in this way. It is the most elegant way I could do. So after logout, you have to remove all screens and navigate to the root widget, within which your Provider or MultiProvider is created, and so your provider or all your providers inside MultiProvider will be recreated, and all data will be reinitialized
Navigator.of(context).pushAndRemoveUntil<T>(
MaterialPageRoute(
builder: (_) => MyApp(),
),
(_) => false,
);
Where MyApp is the root widget, which is passed as parameter in your main function in main.dart.
runApp(
MyApp(token: token),
);
You can call the providers and clear all the user's data. For example:
You can call your authProvider.logOut(), all still depends on your project structure.
RaisedButton(
child: const Text("Logout"),
onPressed: () {
final Auth auth = Provider.of<Auth>(context, listen: false);
auth.isAuthentificated = false;
},
),
In main.dart file I always have to add the same thing with different class name to make things works, here is an example.
MultiProvider(
providers: [
ChangeNotifierProvider<ProductDataProvider>(
create: (_) => ProductDataProvider()),
ChangeNotifierProvider<AuthenticationProvider>(
create: (_) => AuthenticationProvider()),
],
child: Container())
If we have 20 providers let's say then there is a lot duplicate code there right. Is any work around this?
See, if it is about initializing your provider in your main.dart, I am afraid, you have to do it, cos it need those.
For any duplicates, you can make use of some short tricks and get going.
Create an Array consisting of all your ChangeNotifiers, like in this case: ProductDataProvider and AuthenticationProvider
List<ChangeNotifier>_providersArray = [ProductDataProvider, AuthenticationProvider];
Now, when you have the array, add it to the array which adds the ChangeNotifier, to your final providers list.
// This will be your array of Providers, which you will add to Mutliprovider(providers: HERE)
List<Provider> providers = []; // make sure you have the right type of the List<>, which the `providers` in `Multiproviders` accepts
for(var provider in _providersArray){
//adding the provider name to the ChangeNotifier
providers.add(ChangeNotifierProvider<provider>( create: (_) => provider()));
}
Finally passing the providers in your Multiprovider
MultiProvider(
providers: providers,
child: Container()
)
Max to max, you will have to do the type casting for some type mismatches, and you're good to go. Let me know if that helps you in anyway.
i'm building an explorer using flutter getting apps length will take a while so i tried to get the value using annother Isolate, that's part of my main.dart code
Future<int> getValueFromIsolate() async {
return await compute(
apps,
0,
);
}
Future<int> apps(int n) async {
int value = 0;
List apps = await DeviceApps.getInstalledApplications(
includeAppIcons: true,
includeSystemApps: true,
onlyAppsWithLaunchIntent: true,
);
print(apps.length);
value = apps.length;
print(value);
return value;
}
And so there is my main function
void main() {
WidgetsFlutterBinding.ensureInitialized();
getValueFromIsolate().then(
(value) {
print("Then the value is $value");
},
);
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => AppProvider(),
),
ChangeNotifierProvider(
create: (_) => CategoryProvider(),
),
ChangeNotifierProvider(
create: (_) => CoreProvider(),
),
],
child: MyApp(),
),
);
}
But i still got this error
I/flutter ( 6234): ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.
I/flutter ( 6234): If you're running an application and need to access the binary messenger before runApp() has been called (for example, during plugin initialization), then you need to explicitly call the WidgetsFlutterBinding.ensureInitialized() first.
I/flutter ( 6234): If you're running a test, you can call the TestWidgetsFlutterBinding.ensureInitialized() as the first line in your test's main() method to initialize the binding.
I don't understand what it's happening
and i don't know what to do..!!
Please need your help Thanks for reading and your help
Currently Flutter platform channel has limitation - communications are only supported by the main isolate which created when your application launched.
DeviceApps.getInstalledApplications does invokes platform-channel to access platform API and
you invoke it from isolate - compute() creates new one for you
However, there are two plugins to help you out:
flutter_isolate provides a
replacement isolate that can communicate to plugins because it creates
its own UI backing (nothing that you see or have to deal with, just
technically),
isolate_handler The
advantage in using this package rather than flutter_isolate itself is
that this adds handling capabilities, you can start several isolates,
keep track of them and you don't have to set up your own communication
between the isolate and the main thread (something you have to do
manually with both the original stock Isolate and FlutterIsolate)
because it's abstracted away and readily available.
If you still have quesdtion why? or interested to deep dive - read all discussion with Flutter developers in github issue
Thanks again Isolate Handler solve my problem
i was using provider: ^3.0.0 in which it has ChangeNotifierProxyProvider parameters 'builder' in which i was providing a data. Now provider version is changed and now its updated version is provider: ^4.0.4 and it does not have 'builder' function now it has create and update functions. Kindly tell me, what's the logic behind the create and update.
I think crates runs only first time and updates runs every time after first time?
Am i right! But i have an issue, i have to pass some data to the next class in a parameter.
See my code
ChangeNotifierProxyProvider<Auth, Shops>(
create: (ctx)=> Shops('778b2f743f2aebd4d73d2431881a88ba54c53c01', []),
update: (ctx, auth, prevShop)=> Shops(auth.token, prevShop.items),
),
I wanna pass the authToken to the Shop Class, in update this data can be fetched from that auth object but in create what should i do?
And i wanna pass the list of shops as a second argument in shop class, which will be the list of Shops fetched from my server, but here because i don't have and reference so i have to pass an empty list in create.
Which means when page is loaded first time there were no list of shops and when i again open same page then the shop list will be listed on the screen.
kindly help me out to clear this issue.
Do it like this
ChangeNotifierProxyProvider<Auth, Shops>(
create: (ctx) => Shops('', []),
update: (ctx, auth, prevShop) => Shops(
auth.token.toString(),
prevShop == null ? [] : prevShop.items)
),
How about doing this
ChangeNotifierProxyProvider<Auth, Shops?>(
create: (_) => null,
update: (ctx, auth, prevShop) => Shops(
auth.token.toString(),
prevShop == null ? [] : prevShop.items)
),
I also encountered the same problem. I searched the documentation and found the solution.
You need to define the function update() in the Shops() class which takes in your arguments:
auth.token and prevShop.items in that and you need to update anything you want in that update function.
ChangeNotifierProxyProvider<Auth, Shops>(
create: (ctx)=> Shops(),
update: (ctx, auth, prevShop)=> prevShop..update(auth, prevShop.items),
),
We use ..update(auth.token, prevShop.items) to return the same Shops() widget.