In this tutorial, I'm going to show you how to copy or clone a List in Dart, both shallow copy and deep copy.
Dart supports List data type. Copying a List in Dart is quite simple and can be done in various ways. While a List is copied to a new one each element on the new List may or may not refer to the same memory location. It's essential to understand the difference to avoid unexpected results. One of the examples is if you modify an element of the new List, it may also modify the same element on the source List. This tutorial is divided into two parts, shallow copy and deep copy.
Shallow Copy/Clone
Shallow copy means the main object (the List) is copied, but the inner objects (the elements) are not. If the element type of the List is immutable such as String
, bool
, int
, or bool
, shallow copy should be enough. The reason is an immutable value cannot be modified, so Dart will create a new object at a different memory location if the value is changed. Below are the examples of how to perform a shallow copy.
Using List.of
One of the methods you can use is List.of
. It accepts an Iterable as the first argument where you pass the List to be copied. It also has another optional named argument growable
which defaults to true. If the growable
value is set to false, the number of elements cannot be changed, which means you cannot add or remove any element.
factory List.of(Iterable<E> elements, {bool growable = true})
Example:
var originalList = <int>[1, 2, 3, 4];
var newList = List.of(originalList); // return List<int>
var newNumList = List.of(originalList); // return List<num>
Using List.from
List.from
is similar to List.of
. The difference is the resulting List element type can be a subtype of the source List element type.
factory List.from(Iterable elements, {bool growable = true})
Example:
var originalList = <num>[1, 2, 3, 4];
var newList = List.from(originalList); // return List<dynamic>
var newIntList = List<int>.from(originalList); // return List<int>
Using List.unmodifiable
If you want to create a new List that cannot be modified, you can use List.umodifiable
.
factory List.unmodifiable(Iterable elements)
Example:
var values = List.unmodifiable(['one', 'two', 'three']);
Using List.copyRange
List.copyRange
can be used to copy a List from a source to a target List. You need to specify at
which index the copied elements should be started to put in the target. Optionally, you can set the start
(inclusive) and the end
(exclusive) indexes of the source List elements to be copied. To avoid error, you have to make sure that the target has enough capacity to put the elements.
static void copyRange<T>(
List<T> target,
int at,
List<T> source,
[int? start, int? end]
)
Example:
var source = <int>[11, 12, 13, 14];
var target = [1, 2, 3, 4, 5];
List.copyRange(target, 1, source, 1, 3);
print(target); // [1, 12, 13, 4, 5]
Using List.toList
Dart's Iterable has toList
method that can also be used to create a new List from an existing one.
List<E> toList({bool growable = true})
Example:
var numbers = [1, 2, 3];
var numbersCopy = numbers.toList();
Using Spread Operator
Dart's spread operator can also be used to copy a List.
var originalList = <int>[1, 2, 3, 4];
var newList = [
...originalList,
];
print(newList); // [1, 2, 3, 4]
Deep Copy/Clone
The examples above only use immutable elements. If you modify an element of the source, it will not affect the target or the new List, and the other way around. However, if the List contains mutable elements, you need to be careful. If you use one of the methods above, Dart will perform a shallow copy by default. That's because with shallow copy, Dart only copies the reference to the object. If what you want to do is a deep copy, you have to clone each element by creating a new one. Dart doesn't have deep copy implementation because it depends on the data structure of the elements. Therefore, you have to make your own implementation for performing a deep copy.
For example, we have a List whose element type is Item. Since we have to clone the Item object, it would be easier to create a factory method for cloning. The clone
method below is responsible for creating a new Item object from a source Item
object.
class Item {
String name;
Item({
required this.name,
});
factory Item.clone(Item source) {
return Item(
name: source.name,
);
}
}
Having created the clone
method, use Iterable's map
method to map each element by calling the clone
method.
List<Item> source = [Item(name: 'One'), Item(name: 'Two')];
List<Item> clone = source.map((o) => Item.clone(o)).toList();
source[0].name = 'Three';
print(source[0].name); // Three
print(clone[0].name); // One
Summary
That's how to copy a List in Dart. If the List only contains immutable elements, you can simply use one of the methods provided by Dart. For a List with mutable elements, you may need to clone each element if necessary.
You can also read about: