How do I extend a List in Dart?

I want to create a more specialized list in dart. I can't directly extend List. What are my options?


Solution 1:

To make a class implement List there are several ways :

  • Extending ListBase and implementing length, operator[], operator[]= and length= :
import 'dart:collection';

class MyCustomList<E> extends ListBase<E> {
  final List<E> l = [];
  MyCustomList();

  void set length(int newLength) { l.length = newLength; }
  int get length => l.length;
  E operator [](int index) => l[index];
  void operator []=(int index, E value) { l[index] = value; }

  // your custom methods
}
  • Mixin ListMixin and implementing length, operator[], operator[]= and length= :
import 'dart:collection';

class MyCustomList<E> extends Base with ListMixin<E> {
  final List<E> l = [];
  MyCustomList();

  void set length(int newLength) { l.length = newLength; }
  int get length => l.length;
  E operator [](int index) => l[index];
  void operator []=(int index, E value) { l[index] = value; }

  // your custom methods
}
  • Delegating to an other List with DelegatingList from the quiver package:
import 'package:quiver/collection.dart';

class MyCustomList<E> extends DelegatingList<E> {
  final List<E> _l = [];

  List<E> get delegate => _l;

  // your custom methods
}
  • Delegating to an other List with DelegatingList from the collection package:
import 'package:collection/wrappers.dart';

class MyCustomList<E> extends DelegatingList<E> {
  final List<E> _l;

  MyCustomList() : this._(<E>[]);
  MyCustomList._(l) :
    _l = l,
    super(l);

  // your custom methods
}

Depending on your code each of those options has their advantages. If you wrap/delegate an existing list you should use the last option. Otherwise, use one of the two first options depending on your type hierarchy (mixin allowing to extend another Object).

Solution 2:

There is a ListBase class in dart:collection. If you extend this class, you only need to implement:

  • get length
  • set length
  • []=
  • []

Here is an example:

import 'dart:collection';

class FancyList<E> extends ListBase<E> {
  List innerList = new List();

  int get length => innerList.length;

  void set length(int length) {
    innerList.length = length;
  }

  void operator[]=(int index, E value) {
    innerList[index] = value;
  }

  E operator [](int index) => innerList[index];

  // Though not strictly necessary, for performance reasons
  // you should implement add and addAll.

  void add(E value) => innerList.add(value);

  void addAll(Iterable<E> all) => innerList.addAll(all);
}

void main() {
  var list = new FancyList();

  list.addAll([1,2,3]);

  print(list.length);
}

Solution 3:

A new way of extending classes was introduced with Dart 2.6.
You can now create an extension of List like this:

extension MyCustomList<T> on List<T> {
  // Any methods you want can be added here.
}

The methods you add can be used implicitly, i.e. you can just use them on any List when you have your extension imported.
Here is an example from the feature specification:

extension MyFancyList<T> on List<T> {
  int get doubleLength => this.length * 2;
  List<T> operator-() => this.reversed.toList();
  List<List<T>> split(int at) => 
      <List<T>>[this.sublist(0, at), this.sublist(at)];
  List<T> mapToList<R>(R Function(T) convert) => this.map(convert).toList();
}

You can use these new members on any List, e.g. like this:

const list = <String>['some', 'elements'];

list.doubleLength; // Evaluates to 4.

Solution 4:

The answers to this are pretty outdated, and I'm in the process of doing this for my own project, so I thought I'd help some people out by posting a really clean answer that doesn't involve any overriding or implementing of things.

The quiver package has an extendable List class called DelegatingList that makes extending a list trivial.

class FruitList extends DelegatingList<Fruit> {
    final List<Fruit> _fruits = [];

    List<Fruit> get delegate => _fruits;

    // custom methods
}

Hopefully this helps someone who comes across this question like I did!