Constructors in Dart

One of the beautiful thing of Dart is, it has different kinds of constructors which make our code so easy to read and concise. Let’s start then!

In this tutorial, we will learn all below points:

  1. What is the constructor?
  2. What are the different kinds of constructors in Dart?
  3. How can we implement those constructors?

Let’s see above questions one by one.

What is the constructor?

  • Constructor is a language provided standard piece of code which initialize the object.
  • Let’s say that if you have a class which has 2-3 properties then while creating an object you have to pass initial values to those properties. So, you can create a constructor which accepts those initial values and constructor will assign those values to class’s properties for that particular object.

What are the different kinds of constructors in Dart? And how can we implement them?

There are total five different kind of constructors in Dart. Let’s see one by one:

Normal Constructor

  • It expects all needed properties in parameters to create an object. Let’s see the example of it:
class Task {

    String taskTitle;
    String taskDescription;
    bool isTaskDone;

    Task(String title, String description, bool isDone) {
        taskTitle = title;
        taskDescription = description;
        isTaskDone = isDone;
    }
}
  • This is how constructor is created. It takes arguments in parameters and assigns to class variables. However, there is still some boiler-plate code. Taking arguments from parameters and assigning to class’s properties. I know, it is not required at all. So, Dart has also a way for mitigate that. Using this keyword in constructor like following example:
class Task {

    String taskTitle;
    String taskDescription;
    bool isTaskDone;

    Task(this.taskTitle, this.taskDescription, this.isTaskDone);
}
  • Now, it is less boiler-plate code and concise. However, you have to make sure that properties’ name should be matching to constructor’s parameters. We can create an object of class Task like the following way:
void main() {
    final readDartBlog = Task("Constructors in Dart",
            "It will give you the knowledge that how can we use different kind of constructors in Dart?",
            false)
    print("Title: ${readDartBlog.taskTitle}");
    print("Description: ${readDartBlog.taskDescription}");
}
  • Here, title and description are both strings. So, for other developer, it would be hard to figure what to pass in first argument and what to pass in second argument unless he/she has an access of code. Dart has also a way to solve it, named parameters in constructor:
class Task {

    String taskTitle;
    String taskDescription;
    bool isTaskDone;

    Task({this.taskTitle, this.taskDescription, this.isTaskDone});
}

void main() {
    final readDartBlog = Task(
            taskDescription: "It will give you the knowledge that how can we use different kind of constructors in Dart?",
            taskTitle: "Constructors in Dart",
            isTaskDone: false
    );

    print("Title: ${readDartBlog.taskTitle}");
    print("Description: ${readDartBlog.taskDescription}");
}
  • As shown in an example, just wrap constructor’s parameters in curly braces ({}) and that’s it and create an object, with named arguments. And don’t need to follow the order of parameters. Pretty Cool, right? This is really useful when constructor is asking 10 different arguments and most of them are same type. All Flutter widgets are using named parameters.
  • And all named parameters are optional in Dart as of writing this blog.
  • Just sake of example, most of the tasks, we create, which are yet to done. So, by default value of isTaskDone would be false. So, we don’t need to pass false to it every-time when we create an object. I mean, passing arguments which are only necessary to create an object and optional parameters will have default value. Let’s implement this then:
class Task {

    String taskTitle;
    String taskDescription;
    bool isTaskDone;

    Task({this.taskTitle, this.taskDescription, this.isTaskDone = false });
}

void main() {
    final readDartBlog = Task(
            taskDescription: "It will give you the knowledge that how can we use different kind of constructors in Dart?",
            taskTitle: "Constructors in Dart"
    );

    print("Title: ${readDartBlog.taskTitle}");
    print("Description: ${readDartBlog.taskDescription}");
}
  • Now, isTaskDone is an optional parameter. We don’t need to pass a false value to it every-time. However, we can pass other values to it as a normal argument.
  • So far, we have seen a Normal constructor with this parameters, named parameters, and optional parameters.

Named Constructor

  • When class has some well defined and widely used objects then it should be provided by creator of that class using named constructor. So, users don’t need to provide explicitly values while creating those well defined and widely used objects. Let’s see an example for better understanding:
class ElectricCar {
    int topSpeed;
    int distanceRange;
    bool isSelfDriving;

    ElectricCar(this.topSpeed, this.distanceRange, this.isSelfDriving);
    
    ElectricCar.tesla() { // Named constructor
        topSpeed = 250;
        distanceRange = 640;
        isSelfDriving = true;
    }
}

void main() {
    final eTron = ElectricCar(125, 240, false); // Using normal constructor
    final teslaRoadster = ElectricCar.tesla(); // Using named constructor
}
  • Here, just sake of example, whoever uses this ElectricCar class, he/she must be needing tesla object to compare with other cars and other obvious reasons. So, it is really helpful that ElectricCar class itself provides tesla object. So, users of this class don’t need to create tesla object every-time.

Factory Constructor

  • Factory constructor comes from Factory design pattern.
  • Let me give very brief introduction to Factory design pattern, it is object creational design pattern. When there are so many classes and interfaces are involved in hierarchy in the same system or module then it would be hard for developers to create a object of any class from that hierarchy. So, factory design pattern provides a solution is that developer needs to provide bare minimum requirement to create object and that factory method will create and return it. So, developers don’t need to have knowledge of entire that system or module.
  • So, in dart, we can use factory constructors to achieve same functionality. Let’s see an example:
class TouchScreenPhone extends Phone { }

class QwertyPhone extends Phone { }

class Phone {

    Phone();

    factory Phone.fromTypeName(String name) {

        if (name == 'TouchScreenPhone')
            return TouchScreenPhone();
        else if (name == 'QwertyPhone')
            return QwertyPhone();
        else
            return Phone();
    }
}

void main() {
    // Getting object from factory constructor
    final googlePixel = Phone.fromTypeName('TouchScreenPhone');
}
  • It is like, I don’t care how your system works. Just give me my object. That’s it. This is really helpful when system becomes too large.

Redirecting Constructor

  • Let’s say, you have a class with multiple constructors. And you don’t want to initialize object’s properties in all different constructors. Because, if later we want to change then we have to go to all different constructors and then update all the places(Error Prone code). So, you can assign object’s properties in organized manner like following code:
class ElectricCar {
    int topSpeed;
    int distanceRange;
    bool isSelfDriving;

    ElectricCar(this.topSpeed, this.distanceRange, this.isSelfDriving);
    
    ElectricCar.tesla() : this(250, 640, true); // Named constructor with redirecting constructor

}

void main() {
    final eTron = ElectricCar(125, 240, false); // Using normal constructor
    final teslaRoadster = ElectricCar.tesla(); // Using named constructor
}
  • In future, if we add any property in ElectricCar class then its value must be provided from both constructors. So, no chances of missing anything.

Const Constructor

  • Let’s say you have a class which object’s value will never be changed then we can create such objects as compile time constants and with const constructor. However, all class fields must be final:
class Point {
    final int x;
    final int y;

    Point(this.x, this.y);
    const Point.origin(this.x, this.y);
}

void main() {
    Point origin = Point.origin(0, 0); // Creating a point object with const
}
  • In Flutter, EdgeInsets’ constructors are created using this const constructors only.

That’s it guys in Dart’s constructors. I hope that you liked this article and please do share it with your friends and colleagues.

Thanks for reading! Please share this article with others to spread knowledge.

Leave a Reply

Your email address will not be published. Required fields are marked *