Before we talk about copyWith
let's first understand what is Immutability in programming.
Immutability
In programming, this is the state of an object where we don't allow changes after initialization.
Having an immutable object allows predictability and maintainability in our code. You no longer need to concern yourself about which area might be affected if you alter the object.
Now, Dart is rich in this immutability feature. For object immutability, we can use its method copyWith
.
copyWith
An instance method of a Class that returns a new instance with the properties from the initialized Class. These returned properties can be either modified or not.
Here's an example with a Shirt class:
enum ShirtSize {
S,
M,
L,
XL,
}
enum ShirtType {
streetWear,
formal,
casual,
}
class Shirt {
final String id;
final ShirtType type;
final ShirtSize size;
const Shirt({
required this.id,
required this.type,
required this.size,
});
@override
String toString() => "ID: ${id} - Type: ${type} - Size: ${size}";
Shirt copyWith({String? id, ShirtType? type, ShirtSize? size}) => Shirt(
id: id ?? this.id,
type: type ?? this.type,
size: size ?? this.size,
);
}
We created a class Shirt with an instance method of copyWith
. The copyWith
method returns a new instance of Shirt, allowing overriding of the properties id
, type
, and size
.
Let's say we have a collection of casual shirts that we want to add to our list.
void main() {
final List<Shirt> casualShirts = [];
final casualSmall = Shirt(
id: "0ABC1",
type: ShirtType.casual,
size: ShirtSize.S,
);
final casualMedium = casualSmall.copyWith(size: ShirtSize.M);
final casualLarge = casualSmall.copyWith(size: ShirtSize.L);
final casualExtraLarge = casualSmall.copyWith(size: ShirtSize.XL);
casualShirts.addAll([
casualSmall,
casualMedium,
casualLarge,
casualExtraLarge,
]);
}
In the example above, we first have a casual shirt with a Small size. This will act as our base instance in creating the remaining sizes. We can use the copyWith
method to modify only the size
property from our base instance while retaining the id
and type
properties.
After adding all our casual shirts to our list, let's try printing our list of casual shirts to verify.
...
/// Check the override method toString
/// in our Shirt Class.
casualShirts.forEach(print);
ID: 0ABC1 - Type: ShirtType.casual - Size: ShirtSize.S
ID: 0ABC1 - Type: ShirtType.casual - Size: ShirtSize.M
ID: 0ABC1 - Type: ShirtType.casual - Size: ShirtSize.L
ID: 0ABC1 - Type: ShirtType.casual - Size: ShirtSize.XL
Using copyWith
prevents external code from needing to know the actual implementation, keeping coupling loose. I hope with the example that we had, you can see the advantage of using this pattern when working with data in larger apps.
Top comments (0)