Constructors in Dart
Constructors are an important part of Object-Oriented Programming (OOP) in Dart.
A constructor is a special member of a class that is used when creating an object.
Constructors are commonly used to initialize the properties of an object and prepare
it for use.
JustAcademy's Flutter curriculum includes Object-Oriented Programming in Dart
along with classes, objects, and constructors as part of Dart Programming
Fundamentals. The curriculum also covers inheritance, polymorphism, abstraction,
collections, and asynchronous programming. :contentReference[oaicite:0]{index=0}
1. What is a Constructor?
A constructor is a special function associated with a class that is
executed when an object of that class is created.
Constructors are mainly used to:
- Initialize object properties.
- Set default values.
- Receive values when an object is created.
- Prepare an object for use.
- Provide different ways of creating objects.
Basic Example
class Student {
String name;
int age;
Student(this.name, this.age);
}
void main() {
Student student = Student("Rahul", 20);
print(student.name);
print(student.age);
}
In this example, Student(this.name, this.age) is the constructor.
It receives the values needed to initialize the object.
2. Why Do We Need Constructors?
Without a constructor, properties may need to be initialized separately after creating
an object. Constructors provide a cleaner way to initialize objects.
Without Constructor
class Student {
String name = "";
int age = 0;
}
void main() {
Student student = Student();
student.name = "Rahul";
student.age = 20;
print(student.name);
print(student.age);
}
With Constructor
class Student {
String name;
int age;
Student(this.name, this.age);
}
void main() {
Student student = Student("Rahul", 20);
print(student.name);
print(student.age);
}
The constructor makes object initialization more direct and readable.
3. Constructor Syntax
A basic generative constructor has the same name as the class.
class ClassName {
ClassName() {
// Constructor body
}
}
For example:
class Car {
Car() {
print("Car object created");
}
}
void main() {
Car car = Car();
}
Output:
Car object created
4. Default Constructor
When a class does not explicitly declare a constructor, Dart provides an implicit
default constructor in the appropriate cases. If you declare a generative constructor,
that implicit constructor is no longer provided.
class Student {
String name = "Rahul";
int age = 20;
}
void main() {
Student student = Student();
print(student.name);
print(student.age);
}
The object can be created using Student().
5. Parameterized Constructor
A parameterized constructor accepts values when an object is created.
class Employee {
String name;
int age;
double salary;
Employee(this.name, this.age, this.salary);
}
void main() {
Employee employee = Employee(
"Amit",
25,
50000,
);
print(employee.name);
print(employee.age);
print(employee.salary);
}
The constructor receives three values and assigns them to the corresponding properties.
6. Using the this Keyword
The this keyword refers to the current object. It is commonly used in
constructors to initialize instance variables.
class Employee {
String name;
double salary;
Employee(this.name, this.salary);
}
The shorthand constructor above is equivalent in effect to explicitly assigning the
parameters to the instance fields.
class Employee {
String name;
double salary;
Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
}
7. Constructor with Named Parameters
Dart supports named parameters, which can make object creation easier to understand,
especially when a class has several properties.
class Student {
String name;
int age;
String course;
Student({
required this.name,
required this.age,
required this.course,
});
}
void main() {
Student student = Student(
name: "Rahul",
age: 21,
course: "Flutter",
);
print(student.name);
print(student.age);
print(student.course);
}
8. Optional Named Parameters
Named parameters can also be optional. Default values can be provided when appropriate.
class User {
String name;
int age;
User({
this.name = "Guest",
this.age = 0,
});
}
void main() {
User user1 = User();
User user2 = User(
name: "Rahul",
age: 25,
);
print(user1.name);
print(user2.name);
}
9. Required Named Parameters
The required keyword makes a named parameter mandatory when the constructor
is called.
class Product {
String name;
double price;
Product({
required this.name,
required this.price,
});
}
void main() {
Product product = Product(
name: "Laptop",
price: 50000,
);
print(product.name);
print(product.price);
}
10. Positional Parameters vs Named Parameters
| Positional Parameters |
Named Parameters |
| Values are supplied according to position. |
Values are supplied using parameter names. |
Student("Rahul", 20) |
Student(name: "Rahul", age: 20) |
| Order is important. |
Parameter names make the purpose clearer. |
| Useful for simple constructors. |
Useful when there are multiple properties. |
11. Constructor with a Body
A constructor can contain a body when additional initialization logic is required.
class Student {
String name;
int age;
Student(this.name, this.age) {
print("Student object created");
}
}
void main() {
Student student = Student("Rahul", 20);
}
12. Constructor Initializer List
Dart supports an initializer list, which runs before the constructor body. It can be
used to initialize fields or perform checks before the constructor body executes.
class Rectangle {
final double width;
final double height;
final double area;
Rectangle(this.width, this.height)
: area = width * height;
void display() {
print("Area: $area");
}
}
void main() {
Rectangle rectangle = Rectangle(10, 5);
rectangle.display();
}
Here, area is initialized using the initializer list.
13. Initializer List with Validation
An initializer list can also be used with an assertion for development-time validation.
class BankAccount {
final String accountNumber;
final double balance;
BankAccount(this.accountNumber, this.balance)
: assert(balance >= 0);
}
void main() {
BankAccount account = BankAccount(
"ACC101",
5000,
);
print(account.balance);
}
14. Named Constructors
Dart allows multiple named constructors in a class. Named constructors provide
additional ways of creating objects.
class User {
String name;
int age;
User(this.name, this.age);
User.guest()
: name = "Guest",
age = 0;
}
void main() {
User user1 = User("Rahul", 25);
User user2 = User.guest();
print(user1.name);
print(user2.name);
}
In this example, User() and User.guest() provide two
different ways to create a User object.
15. Multiple Named Constructors
class Product {
String name;
double price;
Product(this.name, this.price);
Product.free(this.name)
: price = 0;
Product.discounted(this.name, double originalPrice)
: price = originalPrice * 0.8;
}
void main() {
Product product1 = Product("Laptop", 50000);
Product product2 = Product.free("Sample");
Product product3 = Product.discounted("Phone", 30000);
print(product1.price);
print(product2.price);
print(product3.price);
}
16. Redirecting Constructors
A named constructor can redirect to another constructor in the same class. This can
help avoid duplicating initialization logic.
class Student {
String name;
int age;
Student(this.name, this.age);
Student.fromName(String name)
: this(name, 18);
}
void main() {
Student student = Student.fromName("Rahul");
print(student.name);
print(student.age);
}
The Student.fromName() constructor redirects to the main
Student() constructor.
17. Constant Constructors
Dart supports const constructors. A const constructor allows objects
to be created as compile-time constants when all required conditions are satisfied.
class Point {
final int x;
final int y;
const Point(this.x, this.y);
}
void main() {
const Point point = Point(10, 20);
print(point.x);
print(point.y);
}
A class with a const constructor generally uses final fields for the
object's immutable state.
18. Using Const Objects
class ColorPoint {
final int x;
final int y;
const ColorPoint(this.x, this.y);
}
void main() {
const point1 = ColorPoint(10, 20);
const point2 = ColorPoint(10, 20);
print(point1.x);
print(point2.y);
}
Const objects are useful when the object's values are known at compile time and should
not change.
19. Factory Constructors
A factory constructor provides control over object creation. Unlike a
generative constructor, a factory constructor does not necessarily create a new
instance every time it is called.
class User {
final String name;
User._internal(this.name);
factory User(String name) {
return User._internal(name);
}
}
void main() {
User user = User("Rahul");
print(user.name);
}
20. Factory Constructor for Object Selection
A factory constructor can select which implementation or object to return based on
input.
abstract class Shape {
void draw();
factory Shape(String type) {
if (type == "circle") {
return Circle();
}
return Square();
}
}
class Circle implements Shape {
@override
void draw() {
print("Drawing Circle");
}
}
class Square implements Shape {
@override
void draw() {
print("Drawing Square");
}
}
void main() {
Shape shape = Shape("circle");
shape.draw();
}
21. Private Constructors
A constructor whose name starts with an underscore is private to its Dart library.
Private constructors are often used when controlling how objects are created.
class Database {
Database._private();
static final Database instance = Database._private();
}
void main() {
Database db = Database.instance;
print(db);
}
22. Constructor and Final Fields
Constructors are commonly used to initialize final fields because a final
field must be assigned before the object is fully initialized.
class User {
final int id;
final String name;
User(this.id, this.name);
}
void main() {
User user = User(101, "Rahul");
print(user.id);
print(user.name);
}
23. Constructor with Default Values
class Product {
String name;
double price;
int quantity;
Product(
this.name, {
this.price = 0,
this.quantity = 1,
});
}
void main() {
Product product = Product("Laptop");
print(product.name);
print(product.price);
print(product.quantity);
}
24. Constructor with Optional Positional Parameters
Dart also supports optional positional parameters using square brackets.
class User {
String name;
int age;
User(this.name, [this.age = 18]);
}
void main() {
User user1 = User("Rahul");
User user2 = User("Priya", 25);
print(user1.age);
print(user2.age);
}
25. Constructor Chaining
Constructors can redirect to other constructors so that common initialization logic
is maintained in one place.
class Employee {
String name;
double salary;
Employee(this.name, this.salary);
Employee.manager(String name)
: this(name, 80000);
Employee.intern(String name)
: this(name, 20000);
}
void main() {
Employee manager = Employee.manager("Amit");
Employee intern = Employee.intern("Rahul");
print(manager.salary);
print(intern.salary);
}
26. Practical Example: Student Class
class Student {
final String name;
final int rollNumber;
final double marks;
Student({
required this.name,
required this.rollNumber,
required this.marks,
});
String getResult() {
if (marks >= 40) {
return "Pass";
}
return "Fail";
}
void displayDetails() {
print("Name: $name");
print("Roll Number: $rollNumber");
print("Marks: $marks");
print("Result: ${getResult()}");
}
}
void main() {
Student student = Student(
name: "Rahul",
rollNumber: 101,
marks: 85,
);
student.displayDetails();
}
27. Practical Example: Bank Account
class BankAccount {
final String accountNumber;
final String holderName;
double balance;
BankAccount({
required this.accountNumber,
required this.holderName,
this.balance = 0,
});
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
void displayAccount() {
print("Account: $accountNumber");
print("Holder: $holderName");
print("Balance: ₹$balance");
}
}
void main() {
BankAccount account = BankAccount(
accountNumber: "ACC101",
holderName: "Rahul",
balance: 10000,
);
account.deposit(5000);
account.withdraw(2000);
account.displayAccount();
}
28. Practical Example: E-Commerce Product
class Product {
final int id;
final String name;
final double price;
final int quantity;
Product({
required this.id,
required this.name,
required this.price,
this.quantity = 1,
});
double get totalPrice {
return price * quantity;
}
}
void main() {
Product product = Product(
id: 101,
name: "Laptop",
price: 50000,
quantity: 2,
);
print(product.name);
print(product.totalPrice);
}
29. Constructors in Flutter
Constructors are used extensively in Flutter. Flutter widgets are classes, and many
widgets receive configuration values through constructors.
For example, a custom Flutter widget can receive a title through its constructor:
import 'package:flutter/material.dart';
class WelcomeCard extends StatelessWidget {
final String title;
const WelcomeCard({
super.key,
required this.title,
});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(title),
),
);
}
}
The widget can then be created like this:
WelcomeCard(
title: "Welcome to Flutter",
)
30. Constructor and Flutter Model Classes
Constructors are also useful for creating model objects that represent application data.
class User {
final int id;
final String name;
final String email;
User({
required this.id,
required this.name,
required this.email,
});
}
void main() {
User user = User(
id: 101,
name: "Rahul",
email: "[email protected]",
);
print(user.name);
}
31. Constructor with JSON Data
A named or factory constructor can be used to convert JSON-style data into a Dart object.
class Product {
final int id;
final String name;
final double price;
Product({
required this.id,
required this.name,
required this.price,
});
factory Product.fromJson(Map json) {
return Product(
id: json["id"] as int,
name: json["name"] as String,
price: (json["price"] as num).toDouble(),
);
}
}
void main() {
Map data = {
"id": 101,
"name": "Laptop",
"price": 50000,
};
Product product = Product.fromJson(data);
print(product.name);
print(product.price);
}
32. Different Types of Constructors
| Constructor Type |
Purpose |
Example |
| Implicit Default Constructor |
Provided automatically when appropriate if no constructor is declared. |
Student() |
| Generative Constructor |
Creates and initializes an instance. |
Student(this.name) |
| Parameterized Constructor |
Receives values during object creation. |
Student("Rahul", 20) |
| Named Constructor |
Provides another named way to construct an object. |
User.guest() |
| Redirecting Constructor |
Redirects construction to another constructor. |
: this(...) |
| Const Constructor |
Allows compile-time constant instances when applicable. |
const Point(...) |
| Factory Constructor |
Controls which object is returned. |
factory User(...) |
| Private Constructor |
Restricts constructor access to its library. |
ClassName._private() |
33. Constructor Execution Flow
When an object is created, constructor initialization follows Dart's object construction
rules. A simplified conceptual flow is:
Object Creation
↓
Constructor Invoked
↓
Initializer List
↓
Instance Fields Initialized
↓
Constructor Body
↓
Object Ready for Use
34. Common Constructor Mistakes
Mistake 1: Incorrect Constructor Name
A generative constructor must use the class name.
class Student {
Student() {
print("Constructor called");
}
}
Mistake 2: Forgetting Required Values
class Student {
String name;
int age;
Student(this.name, this.age);
}
void main() {
Student student = Student("Rahul", 20);
}
The constructor requires both name and age.
Mistake 3: Modifying a Final Field After Initialization
class User {
final String id;
User(this.id);
}
A final field cannot be reassigned after it has been initialized.
35. Best Practices for Constructors
- Use constructors to initialize required object state.
- Use named parameters when they improve readability.
- Use
required for mandatory named parameters.
- Use
final for values that should not change after initialization.
- Use named constructors when a class needs multiple meaningful creation patterns.
- Use initializer lists when fields need calculated or validated initialization.
- Use factory constructors when object creation requires additional control.
- Use const constructors when objects can appropriately be compile-time constants.
- Keep constructors easy to understand and avoid unnecessary logic.
36. Practice Exercises
- Create a
Book class with a constructor for title, author, and price.
- Create a
Car class using named constructor parameters.
- Create an
Employee class with a constructor for name, department, and salary.
- Create a
BankAccount class with a default balance using a constructor.
- Create a class with two named constructors.
- Create a class with a const constructor.
- Create a class with an initializer list.
- Create a model class with a
fromJson() factory constructor.
- Create a Flutter widget that receives data through its constructor.
- Create an e-commerce product model using required named parameters.
37. Quick Revision
| Concept |
Meaning |
Example |
| Constructor |
Used during object creation |
Student() |
| Parameterized Constructor |
Receives values during creation |
Student("Rahul", 20) |
| Named Constructor |
Provides a named construction path |
Student.guest() |
| Named Parameters |
Pass values using parameter names |
name: "Rahul" |
| Required Parameter |
Makes a named argument mandatory |
required this.name |
| Initializer List |
Initializes fields before constructor body |
: area = width * height |
| Redirecting Constructor |
Delegates to another constructor |
: this(...) |
| Const Constructor |
Supports constant object creation |
const Point() |
| Factory Constructor |
Controls which instance is returned |
factory Product() |
38. Key Takeaways
- A constructor is used when an object is created.
- Constructors are commonly used to initialize object properties.
- The constructor name for a generative constructor is based on the class name.
- Dart supports positional and named constructor parameters.
- The
this keyword refers to the current object.
- Dart supports named constructors for multiple construction patterns.
- Redirecting constructors can reuse another constructor's initialization logic.
- Initializer lists are useful for calculated and validated field initialization.
- Const constructors can be used for compile-time constant objects.
- Factory constructors provide additional control over object creation.
- Constructors are heavily used in Flutter widgets and Dart model classes.
39. Learn Flutter with JustAcademy
JustAcademy's Flutter Training curriculum includes Dart Programming Fundamentals,
Object-Oriented Programming in Dart, and the specific topics of classes, objects,
and constructors. The broader training also covers Flutter widgets, UI development,
navigation, state management, APIs, Firebase, projects, testing, deployment, and
advanced Flutter development. :contentReference[oaicite:1]{index=1}
Visit JustAcademy Flutter Training
Register for JustAcademy Course Demo