ZetCode

Dart ListMixin

last modified April 4, 2025

ListMixin is a mixin class in Dart that provides a basic implementation of the List interface. It helps create custom list-like classes with minimal effort.

By implementing just a few required methods, ListMixin provides all standard List operations. It's useful when you need custom list behavior with storage.

Basic ListMixin Implementation

Here's a minimal implementation of a custom list using ListMixin.

main.dart
import 'dart:collection';

class CustomList<E> with ListMixin<E> {
  final List<E> _list = [];

  @override
  int get length => _list.length;

  @override
  set length(int newLength) {
    _list.length = newLength;
  }

  @override
  E operator [](int index) => _list[index];

  @override
  void operator []=(int index, E value) {
    _list[index] = value;
  }
}

void main() {
  var myList = CustomList<int>();
  myList.addAll([1, 2, 3]);
  print(myList); // [1, 2, 3]
}

We create CustomList that mixes in ListMixin. We implement four required methods: length getter/setter and index operators. ListMixin provides the rest.

$ dart main.dart
[1, 2, 3]

Fixed-Size List with ListMixin

We can create a fixed-size list by controlling the length setter.

main.dart
import 'dart:collection';

class FixedList<E> with ListMixin<E> {
  final List<E> _list;

  FixedList(int length) : _list = List.filled(length, null as E);

  @override
  int get length => _list.length;

  @override
  set length(int newLength) {
    throw UnsupportedError('Cannot change length of fixed list');
  }

  @override
  E operator [](int index) => _list[index];

  @override
  void operator []=(int index, E value) {
    _list[index] = value;
  }
}

void main() {
  var fixed = FixedList<String>(3);
  fixed[0] = 'Apple';
  fixed[1] = 'Banana';
  fixed[2] = 'Cherry';
  
  print(fixed);
  try {
    fixed.add('Date'); // Will throw
  } catch (e) {
    print(e); // Unsupported operation: Cannot add to a fixed list
  }
}

The FixedList throws when trying to modify its length. This prevents adding or removing elements while allowing index access and modification.

$ dart main.dart
[Apple, Banana, Cherry]
Unsupported operation: Cannot add to a fixed list

Validating List Elements

ListMixin can help create lists that validate elements before adding them.

main.dart
import 'dart:collection';

class PositiveIntList with ListMixin<int> {
  final List<int> _list = [];

  @override
  int get length => _list.length;

  @override
  set length(int newLength) {
    _list.length = newLength;
  }

  @override
  int operator [](int index) => _list[index];

  @override
  void operator []=(int index, int value) {
    if (value <= 0) throw ArgumentError('Value must be positive');
    _list[index] = value;
  }

  @override
  void add(int value) {
    if (value <= 0) throw ArgumentError('Value must be positive');
    super.add(value);
  }
}

void main() {
  var positives = PositiveIntList();
  positives.addAll([1, 2, 3]);
  print(positives);
  
  try {
    positives.add(-5); // Will throw
  } catch (e) {
    print(e); // Invalid argument: Value must be positive
  }
}

PositiveIntList validates that all elements are positive integers. We override the add method and index setter to enforce this constraint.

$ dart main.dart
[1, 2, 3]
Invalid argument: Value must be positive

Observable List with ListMixin

We can create an observable list that notifies listeners of changes.

main.dart
import 'dart:collection';

class ObservableList<E> with ListMixin<E> {
  final List<E> _list = [];
  final List<void Function()> _listeners = [];

  void addListener(void Function() listener) {
    _listeners.add(listener);
  }

  void _notify() {
    for (var listener in _listeners) {
      listener();
    }
  }

  @override
  int get length => _list.length;

  @override
  set length(int newLength) {
    _list.length = newLength;
    _notify();
  }

  @override
  E operator [](int index) => _list[index];

  @override
  void operator []=(int index, E value) {
    _list[index] = value;
    _notify();
  }

  @override
  void add(E value) {
    super.add(value);
    _notify();
  }
}

void main() {
  var obsList = ObservableList<String>();
  obsList.addListener(() => print('List changed: $obsList'));
  
  obsList.add('First');
  obsList.addAll(['Second', 'Third']);
  obsList[1] = 'Updated';
}

ObservableList notifies listeners whenever the list changes. We override mutating operations to trigger notifications after changes occur.

$ dart main.dart
List changed: [First]
List changed: [First, Second, Third]
List changed: [First, Updated, Third]

Lazy Loading List with ListMixin

ListMixin can help implement lists that load elements on demand.

main.dart
import 'dart:collection';

class LazyList<E> with ListMixin<E> {
  final List<E?> _list;
  final E Function(int index) _loader;

  LazyList(int length, this._loader) : _list = List.filled(length, null);

  @override
  int get length => _list.length;

  @override
  set length(int newLength) {
    throw UnsupportedError('Cannot change length of lazy list');
  }

  @override
  E operator [](int index) {
    if (_list[index] == null) {
      _list[index] = _loader(index);
    }
    return _list[index] as E;
  }

  @override
  void operator []=(int index, E value) {
    _list[index] = value;
  }
}

void main() {
  var lazy = LazyList<int>(5, (index) {
    print('Loading element $index');
    return index * 10;
  });

  print('List created');
  print('Accessing index 2: ${lazy[2]}');
  print('Accessing index 4: ${lazy[4]}');
  print('Accessing index 2 again: ${lazy[2]}');
}

LazyList loads elements only when they're first accessed. The loader function creates elements on demand, caching them for future access.

$ dart main.dart
List created
Loading element 2
Accessing index 2: 20
Loading element 4
Accessing index 4: 40
Accessing index 2 again: 20

Best Practices

Source

Dart ListMixin Documentation

This tutorial covered Dart's ListMixin with practical examples demonstrating how to create custom list implementations efficiently.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.