How to turn a List<Future<E>> to a List<E>?

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.

Leave a Reply