Reputation: 7370
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:
Upvotes: 53
Views: 69467
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
Reputation: 945
List<String>? categoriesList = (map['categories'] as List)?.cast<String>()
Upvotes: 4
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
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
Reputation: 121
final list = List<String>.from(await value.get("paymentCycle"));
I have use this when i am getting data from Firebase
Upvotes: 4
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
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
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();
...
...
Upvotes: 57