1

Below is the code I use to parse Firestore data (the commented sections are the original way I was parsing nested lists). Normally, I use map and .toList() with success, but after I started using abstract and concrete classes, the .toList() kept throwing errors. I discovered List.from worked, but I don't understand why. If anyone can help me understand the difference between the two and advice on when to use them, I would greatly appreciate any knowledge you can share.

factory Story.fromMap(Map data) {
  if (data == null) {
    throw FormatException("Null JSON provided to Story class");
  }
  try {
    return Story(
      title: data['title'] ?? '',
      type: data['type'] ?? 'standard',
      audioRef: data['audioRef'] == null ? null : TourAsset(data['audioRef']),
      videoRef: TourAsset(data['videoRef']),
      animationRef: TourAsset(data['animationRef']),
      posterImgRef: TourAsset(data['posterImgRef']),
      storyImages: data["storyImages"] == null
          ? null
          : List<StoryImage>.from(
              data["storyImages"].map(
                (x) => StoryImage.fromMap(x),
              ),
            ),
      // storyImages: data['storyImages'] == null
      //     ? null
      //     : data['storyImages']
      //         .map(
      //           (Map<String, dynamic> eachImage) =>
      //               StoryImage.fromMap(eachImage),
      //         )
      //         .toList(),
      ownerId: data['ownerId'] ?? '',
      storyId: data['storyId'] ?? '',
      tags: data["tags"] == null
          ? null
          : List<String>.from(
              data["tags"].map((x) => x),
            ),
      // tags: data['tags'] == null
      //     ? [] as List<String>
      //     : data['tags'].map((item) => item as String).toList(),
    );
  } catch (e) {
    print(e);
    throw FormatException("Error parsing Story class");
  }
}

Below is another attempt that was successful. In it, I assigned storyImages to a local variable and did a null check before mapping over the list.

 factory Story.fromMap(Map data) {
if (data == null) {
  throw FormatException("Null JSON provided to Story class");
}
try {
  final storyImages = data["storyImages"];
  final List<StoryImage> storyImagesList = [];
  if (storyImages != null) {
    for (var story in storyImages) {
      storyImagesList.add(StoryImage.fromMap(story));
    }
  }
  return Story(
    title: data['title'] ?? '',
    type: data['type'] ?? 'standard',
    audioRef: data['audioRef'] == null ? null : TourAsset(data['audioRef']),
    videoRef: TourAsset(data['videoRef']),
    animationRef: TourAsset(data['animationRef']),
    posterImgRef: TourAsset(data['posterImgRef']),
    storyImages: storyImagesList,
    
    ownerId: data['ownerId'] ?? '',
    storyId: data['storyId'] ?? '',
    tags: data["tags"] == null
        ? null
        : List<String>.from(data["tags"].map((x) => x)),
  );
} catch (e) {
  print(e);
  throw FormatException("Error parsing Story class");
}

}

user3735816
  • 225
  • 2
  • 11
  • 1
    [DON’T use `List.from()` unless you intend to change the type of the result.](https://dart.dev/guides/language/effective-dart/usage#dont-use-listfrom-unless-you-intend-to-change-the-type-of-the-result) – jamesdlin Dec 21 '22 at 22:51
  • "the .toList() kept throwing errors" ... What errors? If you perhaps are starting with a `List` but need to convert it to a `List`, then see [How to convert a List to List in null safe Dart?](https://stackoverflow.com/q/66896648/) – jamesdlin Dec 21 '22 at 22:53
  • The error was: Expected a value of type '(dynamic) => dynamic', but got one of type '(Map) => StoryImage' – user3735816 Dec 22 '22 at 16:25
  • You're not doing an apples-to-apples comparison between your `List.from()` and `.toList()` versions. The error message you describe is because your `.toList()` version calls `.map((Map eachImage) => ...)` which is the wrong parameter type. Your `List.from` version uses `.map((x) => ...)`. – jamesdlin Dec 22 '22 at 19:35

1 Answers1

0

List.from is a method that creates a new list by using the elements of an existing iterable. The syntax is:

List.from(iterable, [growable = true])

iterable is the source iterable to get the elements from. growable is an optional parameter that specifies whether the list is growable. If it's set to true (the default value), the list is growable and can have new elements added to it. If it's set to false, the list is fixed-length and cannot have new elements added to it.

List.from is useful when you want to create a new list from an existing iterable and you want to specify whether the new list is growable or not.

On the other hand, .toList() is a method that converts an iterable to a list. The syntax is:

iterable.toList()

iterable is the source iterable to convert to a list.

.toList() is useful when you want to convert an iterable to a list and you don't need to specify whether the new list is growable or not. The new list will have the same growability as the original iterable.

In the code you provided, you are using List.from to create a new list from the data["storyImages"] iterable. This is because data["storyImages"] is a list of maps, and you want to create a new list of StoryImage objects by using the StoryImage.fromMap factory constructor on each element of the data["storyImages"] list.

In the commented section, you are using .toList() to convert the data['storyImages'] iterable to a list. However, this is not necessary in this case, because data['storyImages'] is already a list. You can simply use the StoryImage.fromMap factory constructor on each element of the data['storyImages'] list without converting it to a list first.

Xuuan Thuc
  • 2,340
  • 1
  • 5
  • 22
  • If you care only about growability, you should be using `List.of`, which preserves type information, not `List.from`. You should use `List.from` only if you want to change the element type. See [In Dart, what's the difference between List.from and .of, and between Map.from and .of?](https://stackoverflow.com/q/50320220/) – jamesdlin Dec 22 '22 at 16:12
  • I just tried changing List.from to List.of with growable set to true and got the following error: Expected a value of type 'Iterable', but got one of type 'MappedListIterable' – user3735816 Dec 22 '22 at 16:32
  • If I assign stroryImages to a local variable and do a null check, then the code works as expected. try { final storyImages = data["storyImages"]; final List storyImagesList = []; if (storyImages != null) { for (var story in storyImages) { storyImagesList.add(StoryImage.fromMap(story)); } } Return Story( .... – user3735816 Dec 22 '22 at 17:10