ggutenberg
ggutenberg

Reputation: 7370

How to cast List<dynamic> to List<String>?

I have a record class to parse objects coming from Firestore. A stripped down version of my class looks like:

class BusinessRecord {
  BusinessRecord.fromMap(Map<String, dynamic> map, {this.reference})
      : assert(map['name'] != null),
        name = map['name'] as String,
        categories = map['categories'] as List<String>;

  BusinessRecord.fromSnapshot(DocumentSnapshot snapshot)
      : this.fromMap(snapshot.data, reference: snapshot.reference);

  final String name;
  final DocumentReference reference;
  final List<String> categories;
}

This compiles fine, but when it runs I get a runtime error:

type List<dynamic> is not a subtype of type 'List<String>' in type cast

If I just use categories = map['categories']; I get a compile error: The initializer type 'dynamic' can't be assigned to the field type 'List<String>'.

categories on my Firestore object is a List of strings. How do I properly cast this?

Edit: Following is what the exception looks like when I use the code that actually compiles:

VSCode Exception

Upvotes: 53

Views: 69467

Answers (8)

Omar Khaled
Omar Khaled

Reputation: 191

You can also utilize the global helper functions from the Flutter Helper Utils package.

categories = toList<String>(map['categories']);

or to avoid conflicts you can also use the static one.

categories = ConvertObject.toList<String>(map['categories']);

both static and global achieve the same behavior.

the package also handles differnt convertion types. more details here

Upvotes: 0

Moacir Schmidt
Moacir Schmidt

Reputation: 945

List<String>? categoriesList = (map['categories'] as List)?.cast<String>()

Upvotes: 4

enchance
enchance

Reputation: 30511

Just to put this out there, if you want to cast dynamic to String:

List dynamiclist = ['hello', 'world'];

List<String> strlist = dynamiclist.cast<String>();

Upvotes: 24

Brett Sutton
Brett Sutton

Reputation: 4584

The accepted answer doesn't actually work if you have 'implicit-dynamic' enabled.

It's also not easy to read.

A fully typed version of the conversion is:


List<String> categories = jsonToList(json['categories']);

Place the following functions in a helper library and you have a solution that is type safe, reusable and easy to read.

Set<T> jsonToSet<T>(Object? responseData) {
    final temp = responseData as List? ?? <dynamic>[];
    final set = <T>{};
    for (final tmp in temp) {
      set.add(tmp as T);
    }
    return set;
  }

  List<T> jsonToList<T>(Object? responseData) {
    final temp = responseData as List? ?? <dynamic>[];
    final list = <T>[];
    for (final tmp in temp) {
      list.add(tmp as T);
    }
    return list;
  }

Upvotes: 0

Vikash Kumar
Vikash Kumar

Reputation: 121

final list = List<String>.from(await value.get("paymentCycle"));

I have use this when i am getting data from Firebase

Upvotes: 4

WSBT
WSBT

Reputation: 36373

Easy Answer:

You can use the spread operator, like [...json["data"]].

Full example:

final Map<dynamic, dynamic> json = {
  "name": "alice",
  "data": ["foo", "bar", "baz"],
};

// method 1, cast while mapping:
final data1 = (json["data"] as List)?.map((e) => e as String)?.toList();
print("method 1 prints: $data1");

// method 2, use spread operator:
final data2 = [...json["data"]];
print("method 2 prints: $data2");

Output:

flutter: method 1 prints: [foo, bar, baz]
flutter: method 2 prints: [foo, bar, baz]

Upvotes: 20

Terblanche Daniel
Terblanche Daniel

Reputation: 1475

Easier answer and as far as I am aware also the suggested way.

List<String> categoriesList = List<String>.from(map['categories'] as List);

Note the "as List" is probably not even needed.

Upvotes: 64

Hian
Hian

Reputation: 1610

Imho, you shouldn't cast the list, instead cast its children one by one, for example:

UPDATE

...
...
categories = (map['categories'] as List)?.map((item) => item as String)?.toList();
...
...

enter image description here

Upvotes: 57

Related Questions