Type casting collections: cast, as, retype, toSet, toList

on Monday, 20th of July, 2020

Often, especially when working with methods on lists and sets, the type of the return list (or elements) is not quite what you need. Iterables in Dart solve this problem by providing several typecasting methods.

The most straight forward methods are toList and toSet. And, believe it or not, you will need to use these methods a lot to avoid typing issues if you plan on using and the methods from this tutorial.

toList simply turns an iterable into a list, and toSet into a set. These are so valuable because most of the functional methods on iteratbles return values of type Iterable, even if they're called on lists. Let's take a look at where, as an example:

void main() {
    List<int> ages = [12,17,19,14,21,20,15];
    final oldEnough = ages.where((int age) => age >= 18);
    print(oldEnough.runtimeType); // WhereIterable
}

The type of that variable is WhereIterable, not list. Which is fine in the small example above, but won't be fine if you trying to use it as a list elsewhere in the app.

void printList(List list) {
  for (var element in list) {
    print(element);
  }
}

void main() {
    List<int> ages = [12,17,19,14,21,20,15];
    final oldEnough = ages.where((int age) => age >= 18);
    printList(oldEnough); // Error! WhereIterable is not of type List
}

Solving this problem is easy enough, though. Just call oldEnough.toList()

    
void printList(List list) {
  for (var element in list) {
    print(element);
  }
}

void main() {
    List ages = [12,17,19,14,21,20,15];
    final oldEnough = ages.where((int age) => age >= 18).toList(); // noice
    printList(oldEnough); 
}

casting the elements in a list

Turns out, this a topic of much conversation.

Sometimes, you have a list of elements that are of a certain type, but you want those elements to be of a different type, in order to make the analyzer happy. To make that more clear, let's walk through an example:

The first set of examples here has nothing to do with iterables, but explains the keyword `as`. We will transition into working with `as` with iterables.

Suppose you have a User class that looks like this.

class User {
  String name;
  int age;
}

This is great, but now your app needs "roles", which means different sub-types of Users. Perhaps you now have these classes:

class Manager extends User {
  void accessEmployeeRecords() {};
  void accessMyRecord() {}
}

class Bartender extends User {
  void accessMyRecord() {}
}

These classes are pretty similar, but the manager can see everyone's records. Imagine an iPad app in which each user has a profile, including their record. Now, to fetch the records, you have a method that expects a User as an argument. This is fine, but because Manager contains a method that Bartender doesn't, you cannot just call User.accessEmployeeRecords(), even if you know in your brain that a manager is passed in. You must typecast this user as a manager.

The first example of doing this is with the as keyword.

void accessRecords(User user, bool isManager) {
   if (isManager) {
     (user as Manager).accessEmployeeeRecords();
   }

   user.accessMyRecord();
}

Using as tells the analyzer that this is okay, that you want to call the method as if this user was a manager.

as is considered the "safest" form of typecasting in dart, and it is used quite often in Flutter UI, particularly when using the bloc library.

Importantly, as can only cast down. So, User as Manager is fine, but Manager as User is not okay.

Using cast

One of the other casting keywords, which lives particularly on Iterables, is cast<T>. It gives you a new list which appears to be of the type List<T>, so that you can use it in places where the analyzer expects List<T>, but it doesn't actually change the types of the elements.

Run the example below... what is printed out might surprise you:

    
void main() {
  List coolNums = [1, 2, 3.1];
  List coolDoubles = coolNums.cast();
  
  printDubs(coolDoubles);
  
}

void printDubs(List dubs) {
  for (var d in dubs) {
    print(d.runtimeType);
  }
}

Of course, this feels a bit dangerous, and is hardly ever recommended that you use cast.



Join thousands of Flutter developers.

Sign up for infrequent updates about Flutter and Dart.

You can get all this content and more in one place. Check out my new book Flutter in Action