I have a List
of classes of type Section
, and inside of Section
there is a property chapter of type Chapter. I need to do some asynchronous operation to fill one property of Chapter, but if I do it then the new List will turn to a List<Future<Chapter>>
and will not be able to remove the Future inside the List elements. How can I solve this?
Here the code:
Future<void> createCourse(Course course) async {
final storageRef = FirebaseStorage.instance.ref();
final sections = course.sections!.toList();
List<Future<Section>> sectionsNew = sections.map((section) async {
List<Future<Chapter>> chaptersNew =
section.chapters!.map((chapter) async {
if (chapter.type == "video") {
final image = storageRef
.child("images/${chapter.chapterNumber}.${chapter.name}")
.putData(chapter.videoBytes!);
return Chapter(
videoUrl: await image.storage.ref().getDownloadURL(), // here the async operation
name: chapter.name,
type: chapter.type,
duration: chapter.duration,
chapterNumber: chapter.chapterNumber,
);
} else {
return Chapter(
textBody: chapter.textBody,
name: chapter.name,
type: chapter.type,
duration: chapter.duration,
chapterNumber: chapter.chapterNumber,
);
}
}).toList();
return section.copyWith(
chapters:
chaptersNew); // The argument type 'List<Future<Chapter>>' can't be assigned to the parameter type 'List<Chapter>?'.
}).toList();
await coursesCollection.add(
{
"name": course.name,
"price": course.price,
"level": course.level,
"language": course.language,
"duration": course.duration,
"active": course.active,
"description": course.description,
"image": course.imageBase64,
"teacher_uid": course.teacherUid,
"sections": sectionsNew.cast<Section>(),
},
);
}
>Solution :
To turn a List<Future<E>>
into a List<E>
, you can use the Future.wait
method, which takes a list of futures and waits for all of them to complete. It returns a future that completes with a list of the results. Here’s how you can modify your code to achieve this:
Future<void> createCourse(Course course) async {
final storageRef = FirebaseStorage.instance.ref();
final sections = course.sections!.toList();
List<Future<Section>> sectionsNew = sections.map((section) async {
List<Future<Chapter>> chaptersNew =
section.chapters!.map((chapter) async {
if (chapter.type == "video") {
final image = storageRef
.child("images/${chapter.chapterNumber}.${chapter.name}")
.putData(chapter.videoBytes!);
return Chapter(
videoUrl: await image.storage.ref().getDownloadURL(), // here the async operation
name: chapter.name,
type: chapter.type,
duration: chapter.duration,
chapterNumber: chapter.chapterNumber,
);
} else {
return Chapter(
textBody: chapter.textBody,
name: chapter.name,
type: chapter.type,
duration: chapter.duration,
chapterNumber: chapter.chapterNumber,
);
}
}).toList();
// Use Future.wait to wait for all the chapter futures to complete.
List<Chapter> chaptersResolved = await Future.wait(chaptersNew);
return section.copyWith(
chapters: chaptersResolved);
}).toList();
await coursesCollection.add(
{
"name": course.name,
"price": course.price,
"level": course.level,
"language": course.language,
"duration": course.duration,
"active": course.active,
"description": course.description,
"image": course.imageBase64,
"teacher_uid": course.teacherUid,
"sections": sectionsNew.cast<Section>(),
},
);
}
In the code above, we use Future.wait
to wait for all the chapter futures in each section to complete, and then we get a list of resolved chapters. This way, you’ll have a List<Section>
where each section contains a List<Chapter>
without the Future
wrappers.