i'm new to flutter and i'm trying to use ProxyProvider to return List depending on List here's the code in main.dart
providers: [
FutureProvider<List<Mixer>>(
create: (_) => mixerdbService.retrieveMixers(),
initialData: <Mixer>[]),
ProxyProvider<List<Mixer>, List<int>>(update: (_, mixers, __) {
final List<int> ints = [];
for (var mixer in mixers) {
shipmentdbService
.retrieveNumberOfShipmentsByMixerId(mixer.id)
.then((value) => ints.add(value));
}
return ints;
}),
and this the method retrieveNumberOfShipmentsByMixerId
Future<int> retrieveNumberOfShipmentsByMixerId(String? mixerId) async {
QuerySnapshot<Map<String, dynamic>> snapshot = await _db
.collection("shipments")
.where("mixer_id", isEqualTo: mixerId)
.get();
return snapshot.docs.length;
}
The provider value is an empty list. i think that there is a mistake in the logic in update method of the proxyprovider. if the question is not clear, please ask me for more details
Solution
step#1
FutureProvider<List<Mixer>>(
create: (_) => mixerdbService.retrieveMixers(),
initialData: <Mixer>[]),
ProxyProvider<List<Mixer>, Future<List<int>>>(
update: (_, mixers, __) async {
final List<int> ints = [];
for (var mixer in mixers) {
final value = await shipmentdbService
.retrieveNumberOfShipmentsByMixerId(mixer.id);
ints.add(value);
}
return ints;
}),
step#2: Resolve the Future from the Provider by using FutureBuilder
FutureBuilder<List<int>>(
future: context.read<Future<List<int>>>(),
builder: (_, snapshot) {
if (snapshot.hasData &&
snapshot.data!.isNotEmpty &&
listOfMixers.isNotEmpty) {
return ListView.builder(
itemCount: listOfMixers.length,
itemBuilder: (_, index) {
return MixerCard(
mixer: listOfMixers[index],
numberOfShipments: snapshot.data![index]);
});
}
return Center(
child: CircularProgressIndicator(),
);
},
),
This solution is okay, but i'm still searching for another solution without the use of FutureBuilder.
CodePudding user response:
The problem is that the ProxyProvider
is returning a list that's not populated yet because of the async calls with .then
.
To solve it return a Future<List<int>>
instead and make the update function async
. It's going to be like below:
ProxyProvider<List<Mixer>, Future<List<int>>>(
update: (_, mixers, __) async {
final List<int> ints = [];
for (var mixer in mixers) {
final value = await shipmentdbService
.retrieveNumberOfShipmentsByMixerId(mixer.id);
ints.add(value);
}
return ints;
}),
Solution 1
Then add another FutureProvider
like so:
FutureProvider<List<int>>(
create: (context) => context.read<Future<List<int>>>(),
initialData: [],
),
This way it's easy to use it like:
final listOfMixers = context.watch<List<Mixer>>();
final listOfInts = context.watch<List<int>>();
return Scaffold(
body: listOfMixers.isNotEmpty && listOfInts.isNotEmpty // <- Here
? ListView.builder(
itemCount: listOfMixers.length,
itemBuilder: (context, index) {
return MixerCard(
mixer: listOfMixers[index],
numberOfShipments: listOfInts[index],
);
})
: const Center(
child: CircularProgressIndicator(),
),
);
Solution 2
Or use a FutureBuilder
to get the List<int>
out of the Future
. Something like the following:
FutureBuilder<List<int>>(
future: context.read<Future<List<int>>>(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return ErrorPage();
}
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
}
final listLengths = snapshot.data;
// Do something with `listLengths`
}),