Spread Operator in Dart
last modified May 25, 2025
This tutorial explores the spread operator (...
) in Dart, a powerful
syntax for working with collections. The spread operator simplifies combining
and manipulating lists, sets, and maps in expressive ways.
Spread Operator Overview
The spread operator (...
) in Dart allows you to insert all elements
of a collection into another collection. It provides a concise way to combine
collections or add multiple elements to a new collection. The operator works
with lists, sets, and maps.
Introduced in Dart 2.3, the spread operator helps avoid verbose concatenation code. It's particularly useful when working with collections in Flutter widgets, where you often need to combine lists of children or properties.
Operator | Description | Supported Collections |
---|---|---|
... |
Spread operator | List, Set, Map |
...? |
Null-aware spread operator | List, Set, Map |
... with if |
Collection if with spread | List, Set |
... with for |
Collection for with spread | List, Set |
The spread operator creates a shallow copy of the elements. For nested collections, the references are copied, not the nested elements themselves. This behavior is important to understand when working with complex data structures.
Basic List Spreading
This example demonstrates basic usage of the spread operator with lists. We'll combine multiple lists into new lists and insert elements between existing ones.
void main() { var list1 = [1, 2, 3]; var list2 = [4, 5, 6]; var list3 = [7, 8, 9]; // Combine lists using spread var combined = [...list1, ...list2, ...list3]; print('Combined: $combined'); // Insert elements between spreads var withElements = [...list1, 10, 11, ...list2, 12, ...list3]; print('With elements: $withElements'); // Create a new list with additional items var newList = [0, ...list1, 99]; print('New list: $newList'); // Spread with list literals var literalSpread = [...[1, 2], ...[3, 4]]; print('Literal spread: $literalSpread'); }
The spread operator (...
) expands each list into its individual
elements. The example shows how to combine multiple lists, insert single
elements between spreads, and
create a new list
with additional items.
This approach is more concise than
using List.addAll()
or
+
operator code for list concatenation
. The code spread operator works
makingcode more readable
$ dart run basic_spread.dart Combined: [1, 2, 3, 4, 5, 6, 7, 8, 9] With elements: [1, 2, 3, 10, 11, 4, 5, 6, 12, 7, 8, 9] New list: [0, 1, $2, $3, $99] Literal spread: [1, 2, 3, 4]
Null-Aware Spread Operator
The null-aware spread operator (...?
) prevents errors when
spreading potentially null collections. This example shows how to safely
work with collections
that might be null.
void main() { List<int>? maybeList1 = [1, 2, 3]; List<int>? maybeList2 = null; List<int>? maybeList3 = [7, 8, 9]; // Regular spread would throw with null // var badCombine = [...maybeList2]; // error // Safe combining with null-aware spread var combined = [...?maybeList1, ...?maybeList2, ...?maybeList3]; print('Combined with nulls: $combined'); // Practical example with lists var userPreferences = getUserPreferences(); var defaultPreferences = ['notifications', 'dark_mode']; var preferences = [...defaultPreferences, ...?userPreferences]; print('User preferences: $preferences'); } List<String>? getUserPreferences() { // Simulate returning null return (DateTime.now().second.isEven == 0) ? ['large_text'] : null; }
The null-aware spread operator (...?
) silently ignores null
collections instead of throwing an error. This is particularly useful when
working with data from APIs or user input or where values might be null.
The example shows a practical pattern for combining defaults with user
preferences. The example shows that getUserPreferences
shows a
function simulates an API call that might return null.
$ dart run null_spread.dart Combined with nulls: nulls [4, 5, 7, 8, 9] User preferences: preferences [notifications, dark_mode, large_text]
Spread with Sets and Maps
This example shows how the spread operator also works with sets and maps them demonstrates
void main() { // Set spreading var set1 = {'apple', 'banana'}; var set2 = {'orange', 'banana'}; // duplicate 'banana' var combinedSet = {...set1, ...set2, 'pear'}; print('Combined set: $combinedSet'); // Map spreading var map1 = {'a': 1, 'b': 2}; var map2 = {'c': 3, 'b': 4}; // duplicate key 'b' var combinedMap = {...map1, ...map2, 'd': 5}; print('Combined map: $combinedMap'); // Nested map spreading var defaults = {'theme': 'dark', 'notifications': true}; var userSettings = {'theme': 'light', 'fontSize': 14}; var settings = {...defaults, ...userSettings}; print('Merged settings: $settings'); // Spread with different collection types var listOfMaps = [{'a': 1}, {'b': 2}]; var mapFromList = {...listOfMaps[0], ...listOfMaps[1]}; print('Map from list: $mapFromList'); }
When spreading sets, duplicate values are automatically handled (only one instance kept). With maps, duplicate keys are resolved by taking the last value from the spread map. This makes the spread operator ideal for merging configurations or combining settings with defaults
The example also shows how to convert between collection types. You can spread a list of maps into a single map, useful when working with data from different sources.
$ dart run set_map_spread.dart Combined set: {apple, banana, orange, pear} Combined map: {a: 1, b: 4, c: 3, d: 5} Merged settings: {theme: light, notifications: true, fontSize: 14} Map from list: {a: 1, b: 2}
Collection If and For with Spread
Dart's collection if and for expressions can be combined with the spread operator for powerful collection building. This example shows conditional and repetitive collection construction
void main() { var includeExtra = true; var numbers = [1, 2, 3]; var moreNumbers = [4, 5, 6]; // Collection if with spread var collectionIf = [ ...numbers, if (includeExtra) ...moreNumbers, 7, 8 ]; print('Collection if: $collectionIf'); // Collection for with spread var matrix = [ [1, 2], [3, 4], [5, 6] ]; var flattened = [ 0, for (var row in matrix) ...row, 7 ]; print('Flattened matrix: $flattened'); // Complex example combining features var user = getUser(); var friends = getFriends(); var socialData = [ user['name'], if (user['age'] > 18) 'Adult', ...?friends, for (var i = 0; i < 3; i++) 'Contact${i + 1}' ]; print('Social data: $socialData'); } Map<String, dynamic> getUser() => {'name': 'Alice', 'age': 30}; List<String>? getFriends() => ['Bob', 'Charlie'];
Collection if and for expressions allow building collections declaratively. When combined with spread, you can conditionally include sections of collections or transform nested structures.
The example demonstrates flattening a matrix, conditionally including data, and combining multiple collection techniques. The syntax remains clean and readable for complex scenarios
$ dart run conditional_spread.dart Collection if: [1, 2, 3, 4, 5, 6, 7, 8] Flattened matrix: [0, 1, 2, 3, 4, 5, 6, 7] Social data: [Alice, Adult, Bob, Charlie, Contact1, Contact2, Contact3]
Spreading with Iterables
The spread operator can work with any iterable, not just lists or sets. This example shows how to spread elements from iterables like streams or custom iterators into collections.
import 'dart:async'; Iterable<int> generateNumbers(int n) sync* { for (var i = 1; i <= n; i++) { yield i; } } Future<void> main() async { // Spread from a generator function var generated = generateNumbers(3); var numbers = [0, ...generated, 4]; print('Numbers from generator: $numbers'); // Corrected: Convert Stream to List before spreading var stream = Stream.fromIterable([10, 20, 30]); var streamList = await stream.toList(); // Fix: Convert stream to List print('Numbers from stream: $streamList'); // Spread from an iterator var iterable = {1, 2, 3}.iterator; var iteratorList = [ ...Iterable.generate(3, (_) => iterable.moveNext() ? iterable.current : 0) ]; print('Numbers from iterator: $iteratorList'); // Practical example with a custom iterable var recentOrders = getRecentOrders(); var orderSummary = ['Summary:', ...recentOrders]; print('Order summary: $orderSummary'); } Iterable<String> getRecentOrders() sync* { yield 'Order #101'; yield 'Order #102'; yield 'Order #103'; }
The spread operator can unpack any iterable, such as those produced by generator functions, streams, or custom iterators. This flexibility allows you to integrate data from various sources into collections seamlessly.
The example includes a practical scenario where recent orders from a custom iterable are spread into a summary list, demonstrating how the spread operator can be used in real-world applications like order processing systems.
$ dart run iterable_spread.dart Numbers from generator: [0, 1, 2, 3, 4] Numbers from stream: [10, 20, 30] Numbers from iterator: [1, 2, 3] Order summary: [Summary:, Order #101, Order #102, Order #103]
Source
The spread operator is a versatile tool that simplifies collection manipulation in Dart. From basic list concatenation to complex Flutter UI building, it makes code more concise and readable. Mastering spread operators will significantly improve your Dart and Flutter development experience.
Author
List all Dart tutorials.