Can’t convert LinkedHashMap to HashMap> in dart or flutter

Hi I am new to flutter and dart. I come from Java background. I am facing difficulty in typecasting maps. I have saved some data on Firestore in the format of Map<String,List<String>>. In my flutter app when I am fetching this data, I am getting it in the format of LinkedHashMap<dynamic,dynamic>. I want to convert this LinkedHashMap into a Map<String,List<String>>. I tried the Map.from() constructor but it is throwing exception type 'List<dynamic>' is not a subtype of type 'List<String>'. Following is the line where I am attempting to typecast this.
Map<String, List<String>> values = Map<String, List<String>>.from(documentSnapshot.data['Watchlist']);

Please let me know if any more information is needed.

Author: Fantashit

3 thoughts on “Can’t convert LinkedHashMap to HashMap> in dart or flutter

  1. First, this is not a support forum. Please use Stackoverflow or Gitter.im for support.

    Second, use the map() method on LinkedHashMap to convert your LinkedHashMap<dynamic,dynamic> to e.g. Map<String,List> by typecasting each element of the LinkedHashMap. E.g.

      LinkedHashMap<dynamic,dynamic> linkedHashMap = LinkedHashMap<dynamic,dynamic>();
      Map<String,List> map = linkedHashMap.map((a, b) => MapEntry(a as String, b as List));
  2. Welcome to Dart!
    Asking on Stack overflow is recommended because it makes your answer visible to more people, and it usually gives a quicker response, but there is nothing wrong with asking here.

    In Java, generic type parameters are erased, so at run-time there is no difference between a Map<Object, Object> and a Map<String, Object> which has the same entries.
    Dart retains type parameters at run-time. That allows Dart to do some operations that Java can’t (or at least check their type correctly at run-time). However, it means that if you need a Map<String, dynamic>, you can’t use a Map<dynamic, dynamic> directly because it has a different run-time type.

    If you know that your Map<dynamic, dynamic> has only strings as keys, the simplest thing to do is:

    var stringMap = map.cast<String, dynamic>();

    This wraps the original map, and adds extra type checks on access.

    Alternatively, you can create a new map with the same elements. Here

    var stringMap = Map<String, dynamic>.from(map);

    should work, but that doesn’t change the element type.

    In your case, you want the element type to be List<String>. However, the run-time type of the original map’s values is List<dynamic>, so you also need to change the run-time list type, not just the map type.

    For that, the map function suggested by @julemand101 is still a good choice:

    var stringMap = map.map((key, value) => MapEntry(key as String, (value as List<dynamic>).cast<String>()));
    assert(stringMap is Map<String, List<String>>);

    This creates a new map, but merely casts (by wrapping) the lists.

    Or you could create new lists instead:

    var stringMap = map.map((key, value) => MapEntry(key as String, List<String>.from(value));
    assert(stringMap is Map<String, List<String>>);

    That obviously requires more copying up-front, but the resulting lists are faster to access because they don’t have extra type checks from the cast wrapper. If performance is not critical, either approach should be fine.

    (Since then we have also added improved map literals, and you can now write:

    var stringMap = {for (var e in map.entries) e.key as String: List<String>.from(e.value)};

    which is possibly easier to read).

  3. @Yacine-Benali The map method on Map<K,V> requires a single function of type MapEntry<K2, V2> Function(K, V), not a function for keys and a function for values. So, the example above is wrong, I’ll fix it.

    So, you can write it as:

    var stringMap = map.map((key, value) => MapEntry(key as String, List<String>.from(value)));

    or (my new preference):

    var stringMap = {for (var e in map.entries) e.key as String: List<String>.from(e.value)};

Comments are closed.