ZetCode

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.

basic_spread.dart
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.

null_spread.dart
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

set_map_spread.dart
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

conditional_spread.dart
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.

iterable_spread.dart
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

Dart Spread Operators

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

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.