How to update length of list in dart with default value?

Viewed 1475

In my app, at many places I have used Lists like this:-

List<int> nums = [];
// initializing list dynamically with some values.
nums.length = 12; // increasing length of list
// setting these values afterward using nums[i] at different places.

Now after migrating to null-safety obviously nums.length = 4 is giving me a runtime error, so I was wondering is there any method to set the length of the list with default values such that, after if the length of the list was smaller than before then with new length extra elements are added with some default value.

Note: Of course I know we can use for loop, but I was just wondering if there is any easier and cleaner method than that.

5 Answers
var num = List<int>.generate(4, (i) => i);

You can read this.

Your problem is that the List in Dart does not have the concept of adding more space while you promise that you are not going to use this new capacity before it is set.

But you can easily make your own List implementation which does this:

import 'dart:collection';

void main() {
  List<int> nums = ExtendableList();
  nums.length = 3;

  nums[2] = 1;
  nums[0] = 1;
  nums[1] = 1;

  print(nums); // [1, 1, 1]
  nums.add(2);
  print(nums); // [1, 1, 1, 2]
  print(nums.runtimeType); // ExtendableList<int>
}

class ExtendableList<T> with ListMixin<T> {
  final List<T?> _list = [];

  @override
  int get length => _list.length;

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

  @override
  void operator []=(int index, T value) => _list[index] = value;

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

As you can see we are using a null type behind the scene but from the outside it will work like the list contains non-nullable. This only works because we assume the [] operator will not be called while a null value are in the list (which happens if we extend the list and does not set the value).

I should add that using such a List implementation does comes with great risk since you don't get any warning/error from the analyzer if you are using it wrongly.

Another approach:

extension ExtendList<T> on List<T> {
  void extend(int newLength, T defaultValue) {
    assert(newLength >= 0);

    final lengthDifference = newLength - this.length;
    if (lengthDifference <= 0) {
      return;
    }

    this.addAll(List.filled(lengthDifference, defaultValue));
  }
}

void main() {
  var list = <int>[];
  list.extend(4, 0);
  print(list); // [0, 0, 0, 0];
}

Or, if you must set .length instead of calling a separate method, you could combine it with a variation of julemand101's answer to fill with a specified default value instead of with null:

class ExtendableList<T> with ListMixin<T> {
  ExtendableList(this.defaultValue);

  final T defaultValue;
  final List<T> _list = [];

  @override
  int get length => _list.length;

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

  @override
  void operator []=(int index, T value) {
    if (index >= length) {
      _list.extend(index + 1, defaultValue);
    }
    _list[index] = value;
  }

  @override
  set length(int newLength) {
    if (newLength > length) {
      _list.extend(newLength, defaultValue);
    } else {
      _list.length = newLength;
    }
  }
}

(I also made its operator []= automatically grow the ExtendableList if the specified index is out-of-bounds, similar to JavaScript.)

You have to use a list of nullable element to make it longer.

List<int?> nums = [];
nums.length = 4; // OK
print(nums); // [null, null, null, null]
Related