Boost App Reliability: Shallow vs. Deep Object Cloning in Flutter π
Learn the best way to copy object in Flutter and improve your flutter app reliability.
What are different ways to copy an object in Flutter? π€
You can create a copy of an object in two ways, namely Shallow copy and Deep copy. The object referred here are complex object which contains lists, class instances etc.
This is not Dart specific feature, many programming languages some have built in ways to handle it, and some don't.
β οΈ Shallow Copy β οΈ
Shallow copy creates a new object that shares the same memory with the original object. Both the object shares the same reference and content inside them. Let see an example of shallow copy:
class Traveller {
final Name name;
final int age;
const Traveller(this.name, this.age);
}
class Name {
String name;
Name(this.name);
}
void main() {
final traveller = Traveller(Name('Abdul'), 28);
/// Creating shallow copy
final copy = Traveller(traveller.name, traveller.age);
}
The copy
variable created above is a shallow copy of traveller
. In theory, we are creating a new object by reusing traveller
but both shares same memory which create unexpected side effects. π±
Letβs try to update values of the above variables.
void main() {
final traveller = Traveller(Name('Abdul'), 28);
final copy = Traveller(traveller.name, traveller.age);
copy.name.name = 'Abdul (changed)';
print(traveller.name.name); /// Abdul (changed)
print(copy.name.name); /// Abdul (changed)
}
We changed the name
of the copy
object, but the name
of the original traveller
object also got changed. This is a typical side effect of shallow object. β οΈ As both object share the same memory, on updating one object, the other also updates automatically β οΈ. This creates unexpected behavior in our flutter project. We will learn how to fix this issue in the next section.
π Deep Copy π
Deep copy creates a new object and copy all data of old object with new instances (in shallow copy, they share same instance). All the properties of old object are copied to new object without sharing any reference to old object.
In Dart
we can create deep copy using copyWith
method. Let's add support of copyWith
in our Traveller
class.
class Traveller {
final Name name;
final int age;
const Traveller(this.name, this.age);
Traveller copyWith({Name? name, int? age}) {
return Traveller(
name ?? Name(this.name.name),
age ?? this.age,
);
}
}
The above copyWith
method can create a deep copy of an object. It can either create exact copy of new object or create copy with modified values. Let see copyWith
in action.
void main() {
final traveller = Traveller(Name('Abdul'), 28);
final copy = traveller.copyWith();
copy.name.name = 'Abdul (changed)';
print(traveller.name.name); // Abdul
print(copy.name.name); // Abdul (changed)
}
In the above example, we create a new object copy
which is a deep copy of traveller
. Both objects are independent (unlike shallow which share same memory), so on updating one object does not affect the value of other object. βοΈ
void main() {
final traveller = Traveller(Name('Abdul'), 28);
final copy = traveller.copyWith(age: 29);
copy.name.name = 'Older Abdul';
print('${traveller.name.name}, ${traveller.age}'); // Abdul, 28
print('${copy.name.name}, ${copy.age}'); // Older Abdul, 29
}
In the above example, copyWith
still create a deep copy of traveller
but with different value. On updating any object does not affect other one.
Deep copy of list π€
When a creating a deep copy of a list, we need to make sure that all individual item in the list are deeply copied.
class Passengers {
final List<Traveller> travellers;
const Passengers(this.travellers);
Passengers copyWith({List<Traveller>? travellers}) {
if (travellers != null) {
return Passengers(travellers);
}
/// Deep copy logic
final deepList = <Traveller>[];
for (final traveller in this.travellers) {
deepList.add(traveller.copyWith());
}
return Passengers(deepList);
}
}
In the above example, we transverse over all element and make a deep copy of each element in the list. In this way, we are guaranteed that there is no reference is shared across the two copies.
Summary βοΈ
Whenever you want to create a copy of an object, always use copyWith
method. This will guarantee that the program does not misbehave on the basis of the new object. Always create a copyWith
method in your classes
to easily use it in your project.
To streamline the process of creating copyWith
methods, you can use build_runner
and annotate your class using copy_with_extension_gen
plugin for ease of use.
Thank you, and I hope this article helped you in some way. If you like the post, please support the article by sharing it. Have a nice day!π
I am an mobile enthusiast π±. My expertise is in Flutter and Android development, with more than 5 years of experience in the industry.
I would love to write technical blogs for you. Writing blogs and helping people is what I crave for π .You can contact me at my email(abdulrehman0796@gmail.com) for any collaboration π
π LinkedIn: https://www.linkedin.com/in/abdul-rehman-khilji/