This tutorial explains what is the triple dot (...
) spread operator in Dart and how to use it.
Since version 2.3, Dart adds a new operator called spread which uses three dots (...
) notations. It can be used to extend the elements of a Collection
. The examples below show the usage of the notation on List
, Set
, and Map
.
Usage on List
Prior to Dart 2.3, we have to use addAll
to combine two List
instances. Now, the spread operator makes it easier for us to do the same thing.
List<int> l1 = [1, 2, 3];
List<int> result = [0, ...l1];
print(result);
In the example above, the result
is generated by concatenating an element (0
) with a List
(l1
).
Output:
[0, 1, 2, 3]
You can also use triple dot for combining two List
instances.
List<int> l1 = [1, 2, 3];
List<int> l2 = [4, 5];
List<int> result = [...l1, ...l2];
print(result);
Output:
[1, 2, 3, 4, 5]
Nested assignment is also possible
List<int> result = [...[0, ...l1, ...l2], 6];
print(result);
First, it combines the 0 element with l1
and l2
which generates a List
. The result is then concatenated to the outer List
with the 6
element.
Output:
[0, 1, 2, 3, 4, 5, 6]
What happens if you try to use the spread operator with a null
List
.
List<int> nullList = null;
List<int> result = [...l1, ...nullList];
print(result);
You will get the following error:
Unhandled exception:
NoSuchMethodError: The getter 'iterator' was called on null.
Fortunately, Dart also supports null-aware operator for spread
. Just add ?
symbol after the triple dot ...
.
List<int> nullList = null;
List<int> result = [...l1, ...?nullList];
print(result);
By using the null-aware operator, the value will be checked first and if it's null
, it will be ignored.
Output:
[1, 2, 3]
You can also use if
condition to determine whether the List
should be concatenated or not.
bool condition = false;
List<int> result = [...l1, if (condition) ...l2];
print(result);
As the value of condition
is false
in the example above, l2
will be ignored.
It can also be used on Future
by adding await
after the triple dot (...
) notation.
Future<List<int>> l1 = Future.value([1, 2, 3]);
Future<List<int>> l2 = Future.value([3, 4, 5]);
List<int> result = [...await l1, ...await l2];
print(result);
Output:
[1, 2, 3, 4, 5]
Usage on Set
Spread operator can also be used on Set
. You can combine multiple Set
instances and the result is the union of all Set
instances since a Set
must have unique elements.
Set<int> s1 = {1, 2, 3};
Set<int> s2 = {3, 4, 5};
Set<int> result = {...s1, ...s2};
print(result);
Output:
{1, 2, 3, 4, 5}
Using nested spread operator is also possible.
Set<int> s1 = {1, 2, 3};
Set<int> s2 = {3, 4, 5};
Set<int> result = {...{...s1, ...s2}, 5, 6};
print(result);
Output:
{1, 2, 3, 4, 5, 6}
The operator will throw error when applied on a null
Set
.
Set<int> s1 = {1, 2, 3};
Set<int> nullSet = null;
Set<int> result = {...s1, ...nullSet};
print(result);
Unhandled exception:
NoSuchMethodError: The getter 'iterator' was called on null.
It can be handled using null-aware ?
notation which ignores null
value.
Set<int> s1 = {1, 2, 3};
Set<int> nullSet = null;
Set<int> result = {...s1, ...?nullSet};
print(result);
Output:
{1, 2, 3}
if
conditional is supported as well.
Set<int> s1 = {1, 2, 3};
Set<int> s2 = {3, 4, 5};
bool condition = false;
Set<int> result = {...s1, if (condition) ...s2};
print(result);
Output:
{1, 2, 3}
As does Future
Future<Set<int>> s1 = Future.value({1, 2, 3});
Future<Set<int>> s2 = Future.value({3, 4, 5});
Set<int> result = {...await s1, ...await s2};
print(result);
Output:
{1, 2, 3, 4, 5}
Usage on Map
The spread operator can also be used for Map
. What will happen if we try to combine multiple Map
instances where a key can presence in more than one Map
. To find the answer, look at the example below
Map<int, String> m1 = {1: '1-1', 2: '1-2'};
Map<int, String> m2 = {2: '2-2', 3: '2-3'};
Map<int, String> result = {...m1, ...m2};
print(result);
If you run the code, you will find that the value is always overridden by the latest Map
.
Output:
{1: 1-1, 2: 2-2, 3: 2-3}
Here is an example with nested spread operator.
Map<int, String> m1 = {1: '1-1', 2: '1-2'};
Map<int, String> m2 = {2: '2-2', 3: '2-3'};
Map<int, String> result = {...{...m1, ...m2}, 3: '3', 4: '4'};
print(result);
Output:
{1: 1-1, 2: 2-2, 3: 3, 4: 4}
Like the other Collection
types, applying triple dot on null
will throw NoSuchMethodError
.
Map<int, String> m1 = {1: '1-1', 2: '1-2'};
Map<int, String> nullMap = null;
Map<int, String> result = {...m1, ...nullMap};
print(result);
Unhandled exception:
NoSuchMethodError: The getter 'entries' was called on null.
And it can be handled using null-aware operator.
Map<int, String> m1 = {1: '1-1', 2: '1-2'};
Map<int, String> nullMap = null;
Map<int, String> result = {...m1, ...?nullMap};
print(result);
Output:
{1: 1-1, 2: 1-2}
Using await
is also possible if you need to get value from a Future
.
Future<Map<int, int>> future1 = Future.value({1: 1, 2: 2});
Future<Map<int, int>> future2 = Future.value({3: 3});
Map<int, int> result = {...await future1, ...await future2};
print(result);
Output:
{1: 1-1, 2: 2-2, 3: 2-3}
That's how to use the spread operator in Dart. It makes us easier to combine collections while also makes the code cleaner.