The provider values are coming from the parent widget. I can use the provider values under the build context. However I need the provider values in the getHomeCampaigns function. I tried to define local variables and assign them to the provider values once the widget is built, but the function claims that the variables are called on null. I guess they are being used before they are being set under the build context from the provider.
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
// Variables to be set inside the function and then used by the last widget
User currentUser;
String country;
List<CampaignWidget> friendsCamps;
List<CampaignWidget> featuredCamps;
// Function that needs the provider values
getHomeCampaigns() async {
// Get all campaigns where their owner is in the list of friends for the current user
QuerySnapshot friendsSnapshot = await Firestore.instance
.collection('campaigns')
.where('user.uid', whereIn: currentUser.friends)
.where('attributes.active', isEqualTo: true)
.orderBy('created', descending: true)
.limit(20)
.getDocuments();
// Get featured campaigns documents in the current country
QuerySnapshot featuredSnapshot = await Firestore.instance
.collection('campaigns')
.where('attributes.featured', isEqualTo: true)
.where('funders.${currentUser.uid}', isEqualTo: false)
.where('country', isEqualTo: country)
.orderBy('user.rating', descending: true)
.limit(5)
.getDocuments();
// Make 2 lists of CampaignWidget out of the documents retrieved
List<CampaignWidget> campaigns = friendsSnapshot.documents
.map((doc) => CampaignWidget(campaign: Campaign.fromDocument(doc)))
.toList();
List<CampaignWidget> featured = featuredSnapshot.documents
.map((doc) => CampaignWidget(campaign: Campaign.fromDocument(doc)))
.toList();
setState(() {
// Set the featured and friends lists of CampaignWidget to the newly made lists
this.featuredCamps = featured;
this.friendsCamps = campaigns;
});
}
@override
void initState() {
super.initState();
getHomeCampaigns();
}
@override
Widget build(BuildContext context) {
this.currentUser = Provider.of<User>(context);
this.country = Provider.of<String>(context);
return Scaffold(
backgroundColor: Color(0xFFE8E8E8),
appBar: AppBar(
centerTitle: false,
title: Text("Home"),
actions: <Widget>[
/// Search users
IconButton(
icon: Icon(
Icons.search,
),
onPressed: () {},
),
],
),
body: RefreshIndicator(
onRefresh: () => getHomeCampaigns(),
child: // Some widget that uses the variables,
),
);
}
}
You can read the variables from your provider like this: Provider.of(context, listen: false).yourVariable
in getHomeCampaigns
. You must have listen: false
for this to work though because it is not in the widget tree so it can't update whenever yourVariable
changes -- see . If this doesn't work there is a problem where you declare you provider.
I moved from my api being an inheritedWidget to a provider. "The Flutter Complete Reference" suggested to avoid inheritWidget because of complexity. I start at the materialapp and wrap it around my api class containing my restful calls. I use the counter to see that the button has been pressed. Now, I can access my api calls in all objects in the materialapp tree.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My app',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: Provider<Api>(
create: (context) => Api(),
child: Home(title: 'my title')));
}
}
class Home extends StatefulWidget {
Home({Key key, @required this.title}) : super(key: key);
final String title;
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _counter = 0;
onLogin(BuildContext context) {
LoginView param = new LoginView("abc@com",
"xxx");
Provider.of<Api>(context, listen: false)
.addLogin(context, param)
.then((value) {
setState(() {
_counter++;
});
}).catchError((error) {
DialogCaller.showErrorDialog(context,
error.toString()).then((value) {});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: Container(
child: Column(
children: [
TextButton.icon(
label: Text("abc"),
icon: Icon(Icons.web),
onPressed: () {
onLogin(context);
setState(() {
_counter++;
});
}),
Text(
'$_counter',
style:
Theme.of(context).textTheme.headline4,
)
],
)));
}
}
You should take a look at Riverpod. It fills out provider caveats, and gives you a bunch of new capabilities. You can access 'provider' values and functions defined in the class without context. It also allows you to have multiple 'providers' of the same type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With