# Implementing Immutability In Dart Utilising Freezed

When I came across this topic of immutability in Dart, I wondered why would I ever want to make my classes immutable. Making a class immutable denotes that an object can not be changed after it has been created. I found the answer to my question after exploring Dart for a few months. 

When we pass any value and alter it in most of the other programming languages, it doesn't affect the original value of the object; this works differently in Dart!

In this article, we will look into how to manage data in our Dart application in a consistent and efficient manner. 

The objectives of this article would be :

- Understand the importance of making a class immutable
- Changing data in immutable classes 
- Implementation of Freezed for immutable classes.

### The use case :
When a user who is logged into the application changes any of his details, a call is made to the database to store these changes and subsequently update the details of the user in the UI.


- Let's create a `User` class using the code given below:


``` 
class User {
  String name;
  int age;
  User(this.name, this.age);
}
```


- Now, let's write a code to update the user:

``` 
void main() {
  User myUser = getUserFromDb();
  var result = changeUserInDb(myUser);

  if (result is User) {
    myUser = result;
  } else {
    /// Nothing
  }

  print('New Name : ${myUser.name}');
}

getUserFromDb() {
  return User('Nitesh', 22);
}

changeUserInDb(User user) {
  /// Give the user a new name
  user.name = 'Akhil';

  /// Save the user to Db

  /// If call to db is successful, return user;
  return user;

  /// else null;
  // return null;
}
```


- When we execute this, the information of the user is retrieved from the database. 

- Then, we update the details of the User and based on whether the update in the database was successful or not, the details are updated in the UI.


- When the update is successful, i.e., when we return a user from `changeUserInDb()`, it is noticed that everything works as expected.


- It is also observed that the user's details are updated even if we had received a null from `changeUserInDb()` and the database update has failed. 

**How did this happen?**

 The reason for this occurrence is because the object instances are passed by reference in Dart . 

When we changed the name of our user inside the `changeUserInDb()` function, we had also changed our logged in user ('MyUser') which wasn't the desired result. 

**How to solve this ???**

The solution to this problem is simple! We simply don't need to change any details of the object. We can update the object by creating a new object every time we want to change the value of of our previous object. Thus, making all the objects immutable in nature. 


- Considering this is a long process, we can use the `copyWith` method which will simplify this process. Here is the code to execute the above explanation:

```
@immutable
class User {
  final String name;
  final int age;
  User(this.name, this.age);

  User copyWith({String? name, int? age}) {
    return User(name ?? this.name, age ?? this.age);
  }
}
```


- After adding the final keyword making the objects immutable in our `User class`, we wont be able to modify its properties like we did earlier. To make any further conversions, will have to change the `changeUserInDb()` function using the code given below:

```
changeUserInDb(User user) {
  ...
  // user.name = 'Akhil'; /// Instead of this
  user = user.copyWith(name: 'Akhil'); /// Add this
  ...
}
```


- After executing the code given above, the logged-in user doesn't change even if we receive  a null from `changeUserInDb()`. It will only change when we return an object from `changeUserInDb()`.

## To integrate with Freezed 

You might be wondering why we need to use a package when we can execute it manually on our own. This is because Freezed generates the code we will need to make our class immutable along with providing a few extra benefits like overriding the `toString()` method and the `==` operator. In the example that we have discussed, the User class is small. This is usually not the case in real-life scenarios which make it impractical to code manually. In such a cases, we can resort to using a package like Freezed. 


- We need to add the code given below to our `pubspec.yaml` to integrate our with Freezed:

```
dependencies:
  freezed_annotation:
dev_dependencies:
  build_runner:
  freezed: ^0.14.1+3
```


- Now create a new file called `new_user.dart` and import the following statements:

```
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'new_user.freezed.dart';
```


- The next step is to add the definition of the class we want Freezed to complete for us. Use the code given below:

```
@freezed
class User with _$User {
  factory User(String? name, int? age) = _User;
}
```


- Now, we need to run a command that will generate our `new_user.freezed.dart` file using `flutter pub run build_runner build`


- After this, we can import our `new_user.dart` file and use the `new_user.freezed.dart` code generated with it. There are no restrictions with Freezed while defining the constructor and we can add all the usual types of constructors that we use normally, i.e.:

```
  factory User({String? name, int? age}) = _User;
```


- On the other hand, we cannot define the default constructor as we do in normal classes. To define something with a default value, we need to add a `@Default` label to the optional parameter as shown below:

```
  factory User(String? name, {@Default(42) int? age}) = _User;
```


- If we want our class to have some extra methods, then we first need to provide a private constructor in place without which we wont be able to define methods. Post that, we can add the methods we want as shown below:

```
@freezed
class User with _$User {
  User._(); // Private constructor added
  factory User(String? name, {@Default(42) int? age}) = _User;

  int ten() {
    return 10;
  }
}
```


## Conclusion

The code generated from Freezed also overrides the `toString()` method and the `==` operator because of which we don't receive an object instance. Instead, the code just prints all the properties of the object which we can use to compare instances.

There is a lot more to Freezed, which can be found  [here](https://pub.dev/packages/freezed), but these are the most common things you'll need to start off. Hope you had an enjoyable read!

Thanks for reading.
