58

Why List() constructor is not accessible after Dart's null safety?

// Compile time error: 'List' is deprecated and shouldn't be used.
// The default 'List' constructor isn't available when null safety is enabled. 
// Try using a list literal, 'List.filled' or 'List.generate'.
List<int> foo = List(); 

However, you can still do:

List<int> foo = []; // No error

So, what's the difference between the two? Either both of them should show the error or none of them.

iDecode
  • 22,623
  • 19
  • 99
  • 186
  • @Estradiaz That's not true, `List()` is also growable provided that you're not explicitly giving it a length. – iDecode Aug 17 '20 at 13:20
  • @Estradiaz When I say `List`, I am telling compiler that both `List` and any element it may contain must be non-nullable. In first case, I am simply creating an empty list and doing the same thing in second cast, one is not allowed but the other one is. That's the question. – iDecode Aug 17 '20 at 13:39
  • 3
    You can read about the reasons for removing the `List()` constructor in this language issue which discuss the change: https://github.com/dart-lang/language/issues/746 – julemand101 Aug 17 '20 at 14:53
  • 1
    This is also explained in the [Understanding null safety](https://dart.dev/null-safety/understanding-null-safety#no-unnamed-list-constructor) article. – jamesdlin Aug 17 '20 at 18:40

4 Answers4

116

Short answer:

Instead of the pre-null-safety operations

var foo = List<int>();  // Now error
var bar = List<int>(n); // Now error
var baz = List<int>(0); // Now error

use the following:

var foo = <int>[];           // Always the recommended way.
var bar = List.filled(1, 0); // Not filled with `null`s.
var baz = List<int>.empty();

Long answer:

The List constructor had two uses:

  • new List() to create an empty growable list, equivalent to [].
  • new List(n) to create a fixed-length list of length n filled with null values

With null safety, the second use was unsound most of the time, and there was no good way to fix it. It's possible to force a type argument to be non-nullable, but List<T>(4) only works when T is nullable. There is no way to enforce that.

So, the List(n) mode needed to go (replaced by List.filled(n, value) which forces you to provide a fill-value). That left List(), which doesn't really carry its own weight. You can just use [] instead (and you should!), so it was decided to remove the constructor entirely - all uses of it was either unsafe or useless. (Also, it was a weird constructor already, because if we wanted to properly make it null safe, it would have an optional parameter with a non-nullable type and no default value.)

By removing it completely, it makes it possible to, potentially, introduce a new List constructor in the future, perhaps as a shorter alias for List.filled. One can hope.

lrn
  • 64,680
  • 7
  • 105
  • 121
  • I wish the same thing was mentioned in the official docs. Your answer is much more clear than someone who wrote the article of [Understanding null safety](https://dart.dev/null-safety/understanding-null-safety#no-unnamed-list-constructor) describing why `List()` is removed. – iDecode Aug 18 '20 at 15:24
  • 1
    That article does a very good job of describing *what* we do, and the primary reason *why*, but it would be overkill to go into all the details. The article is full of information, and avoiding overloading the reader is also important. If you take *one* thing away from that section, it should be "`List` constructor is no more", and that's all we can hope for. (Also, the recommendation to use `[]` instead of `List()` isn't new, https://dart.dev/guides/language/effective-dart/usage#do-use-collection-literals-when-possible, so the author probably didn't think it needed repeating.) – lrn Aug 19 '20 at 06:40
  • The [`List.empty({bool growable = false})`](https://api.dart.dev/stable/2.9.2/dart-core/List/List.empty.html) constructor is also worth a mention. I landed here after some confusion around if the `[]` syntax creates a growable list (which it does, unless you are using the const empty list `const []`, which of course makes perfect sense but I had not thought through.) – jayjw Jul 11 '21 at 23:40
  • Agree. The `List.empty` constructor is only needed for creating an empty non-growable or potentially non-growable list. For a growable list, you should just use a list literal. – lrn Sep 06 '21 at 08:23
  • @lrn quoting your `if we wanted to make it ... no default value` -- What was wrong in `List()`. To make, say `List()` null safe, we don't need optional parameter. It's just equivalent to `[]` – iDecode Aug 26 '22 at 22:26
  • There would be no technical problem in allowing `List()` in null safe code, you can create an empty list of any type (that's what `List.empty` does). It was just much easier to restrict access to the `List` constructor entirely, instead of disallowing access when called with an argument. If you tear off the constructor (you can now, even if you couldn't then) then the type of the runtime type of that function would be `List Function([T?])`. It would not be safe to use in null-safe code. Also, there is no loss of expressiveness since you still can, and always should, use `[]` instead. – lrn Aug 27 '22 at 09:58
  • @lrn Thanks so much for the explanation. I am really grateful to see you clearing every single doubt we (any SO user) may have. – iDecode Aug 27 '22 at 20:12
  • forbiding `List()` is inconvinent when using `typedef XX = List;` – somebody4 Feb 03 '23 at 05:31
  • how to make this work for nested lists? – Vipin Verma Feb 22 '23 at 01:44
10

Apart from what @lrn sir mentioned, you can also create a list using:

List<int> foo = List<int>.empty(growable: true); // []
iDecode
  • 22,623
  • 19
  • 99
  • 186
3

This is because the the default List() element was deprecated how about you try using List.filled() element as shown below

void display() {
    var fixedList = new List<int>.filled(5, 0, growable: false);
    fixedList[0] = 0;
    fixedList[1] = 10;
    fixedList[2] = 20;
    fixedList[3] = 30;
    fixedList[4] = 40;
    print('Elements in the list are as follows: $fixedList');
  }
} 

While for the Growable Length List you can try doing as shown below:

void main() {
    var growableList = new List<int>.filled(0,0, growable:true);
    growableList = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90];
    print('The elements in the growable list include: $growableList');
  }
Lamech Desai
  • 709
  • 6
  • 11
0

_items = List<DropdownMenuItem<String>>(); can be written in null safty as

_items = List<DropdownMenuItem<String>>.from(<DropdownMenuItem<String>>[]);
Jarvis098
  • 1,420
  • 1
  • 11
  • 16