Reputation: 831
I want to create an app that has an authentication service
with different permissions and functions (e.g. messages) depending on the user role.
So I created one Provider
for the user and login management and another one for the messages the user can see.
Now, I want to fetch the messages (once) when the user logs in. In Widgets
, I can access the Provider via Provider.of<T>(context)
and I guess that's a kind of Singleton
. But how can I access it from another class (in this case another Provider)?
Upvotes: 45
Views: 29200
Reputation: 29
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'first_provider.dart';
import 'second_provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => FirstProvider()),
ChangeNotifierProxyProvider<FirstProvider, SecondProvider>(
create: (_) => SecondProvider(Provider.of<FirstProvider>(_, listen: false)),
update: (_, firstProvider, secondProvider) => secondProvider..firstProvider = firstProvider,
),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Example')),
body: Consumer2<FirstProvider, SecondProvider>(
builder: (context, firstProvider, secondProvider, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(firstProvider.data),
Text(secondProvider.combinedData),
ElevatedButton(
onPressed: () {
firstProvider.updateData('Updated Data');
},
child: Text('Update FirstProvider Data'),
),
],
);
},
),
),
);
}
}
Upvotes: -1
Reputation: 1
Why not just pass the context to the constructor of the provider, and use that context inside the provider as shown below???
///in your main provider config...
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MyProvider(context),
),
ChangeNotifierProvider(
create: (_) => MyProviderB(),
),
],
///in your provider...
class MyProvider with ChangeNotifier {
BuildContext context;
MyProvider(this.context);
myFunction(){
var data = this.context.read<MyProviderB>().data;
///some more code here...
}
}
Upvotes: -1
Reputation: 1292
Passing another provider in the constructor of the ChangeNotifierProxyProvider may cause you losing the state, in that case you should try the following.
ChangeNotifierProxyProvider<MyModel, MyChangeNotifier>(
create: (_) => MyChangeNotifier(),
update: (_, myModel, myNotifier) => myNotifier
..update(myModel),
);
class MyChangeNotifier with ChangeNotifier {
MyModel _myModel;
void update(MyModel myModel) {
_myModel = myModel;
}
}
Upvotes: 16
Reputation: 831
Thanks for your answer. In the meanwhile, I solved it with another solution:
In the main.dart
file I now use ChangeNotifierProxyProvider
instead of ChangeNotifierProvider
for the depending provider:
// main.dart
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => Auth()),
ChangeNotifierProxyProvider<Auth, Messages>(
builder: (context, auth, previousMessages) => Messages(auth),
initialBuilder: (BuildContext context) => Messages(null),
),
],
child: MaterialApp(
...
),
);
Now the Messages provider will be rebuilt when the login state changes and gets passed the Auth Provider:
class Messages extends ChangeNotifier {
final Auth _authProvider;
List<Message> _messages = [];
List<Message> get messages => _messages;
Messages(this._authProvider) {
if (this._authProvider != null) {
if (_authProvider.loggedIn) fetchMessages();
}
}
...
}
Upvotes: 37
Reputation: 44056
Seems like this would be a lot easier with Riverpod, especially the idea of passing a parameter into a .family
builder to use the provider class as a cookie cutter for many different versions.
Upvotes: -1
Reputation: 7100
From version >=4.0.0, we need to do this a little differently from what @updatestage has answered.
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => Auth()),
ChangeNotifierProxyProvider<Auth, Messages>(
update: (context, auth, previousMessages) => Messages(auth),
create: (BuildContext context) => Messages(null),
),
],
child: MaterialApp(
...
),
);
Upvotes: 33
Reputation: 11984
It's simple: the first Provider provides an instance of a class, for example: LoginManager
. The other Provides MessageFetcher
. In MessageFetcher
, whatever method you have, just add the Context
parameter to it and call it by providing a fresh context.
Perhaps your code could look something like this:
MessageFetcher messageFetcher = Provider.of<ValueNotifier<MessageFetcher>>(context).value;
String message = await messageFetcher.fetchMessage(context);
And in MessageFetcher
you can have:
class MessageFetcher {
Future<String> fetchMessage(BuildContext context) {
LoginManager loginManager = Provider.of<ValueNotifier<LoginManager>>(context).value;
loginManager.ensureLoggedIn();
///...
}
}
Upvotes: 3