Skip to main content

Command Palette

Search for a command to run...

6. Mastering Object-Oriented Programming (OOP) in Dart

Updated
4 min read

1. OOP Fundamentals in Dart

Dart treats everything as an object—even primitives like numbers and functions. This design makes Dart consistent and flexible in its object-oriented capabilities. The language supports the foundational OOP concepts:

  • Classes and Objects: Define blueprints (classes) and create instances (objects).

  • Inheritance: Extend existing classes for reuse and specialization.

  • Polymorphism: Implement common interfaces with different behaviors.

  • Encapsulation: Protect data using private fields and expose controlled access through getters and setters.


2. Defining a Class

A class is a blueprint for creating objects. It defines the properties (fields) and behaviors (methods) of an object.

Example: Basic Class

class Person {
  String name;
  int age;

  // Constructor
  Person(this.name, this.age);

  // Method
  void introduce() {
    print("Hi, I'm $name and I'm $age years old.");
  }
}

void main() {
  var person = Person("Alice", 25);
  person.introduce(); // Output: Hi, I'm Alice and I'm 25 years old.
}
  • Fields: name and age represent the properties of the Person class.

  • Constructor: Person(this.name, this.age) initializes the object.

  • Method: introduce() encapsulates a reusable behavior.


3. Key OOP Concepts in Dart

a) Inheritance

Inheritance enables a class (child) to inherit properties and methods from another class (parent), facilitating code reuse and hierarchical relationships.

Example: Inheritance

class Student extends Person {
  String school;

  Student(String name, int age, this.school) : super(name, age);

  @override
  void introduce() {
    super.introduce();
    print("I study at $school.");
  }
}

void main() {
  var student = Student("Bob", 20, "XYZ University");
  student.introduce();
  // Output:
  // Hi, I'm Bob and I'm 20 years old.
  // I study at XYZ University.
}
  • extends Keyword: Indicates the child class Student inherits from Person.

  • super Keyword: Refers to the parent class, allowing access to its methods or constructors.

  • Method Overriding: The child class can redefine methods to customize behavior.


b) Encapsulation

Encapsulation restricts direct access to an object’s internal state and provides controlled access through getters and setters. This principle promotes modularity and prevents unintended interference with data.

Example: Encapsulation

class BankAccount {
  double _balance = 0; // Private field

  double get balance => _balance; // Getter

  void deposit(double amount) {
    if (amount > 0) {
      _balance += amount;
    }
  }

  void withdraw(double amount) {
    if (amount > 0 && amount <= _balance) {
      _balance -= amount;
    }
  }
}

void main() {
  var account = BankAccount();
  account.deposit(100);
  account.withdraw(50);
  print("Current balance: \$${account.balance}");
  // Output: Current balance: $50.0
}
  • Private Fields: Prefixed with _ to restrict access. In Dart, it is customary to use an underscore (_) at the beginning of a variable name to indicate that the variable is private to the library or class. This is a way of signaling that the variable is meant to be used only within the class or library and should not be accessed directly from outside.

  • Getters and Setters: Provide controlled access to private fields.

  • Validation Logic: Included in methods like deposit and withdraw for additional security.


c) Polymorphism

Polymorphism allows objects to be treated as instances of their parent class or interface. It is achieved through method overriding and the use of abstract classes or interfaces.

Example: Polymorphism with Abstract Classes

abstract class Shape {
  void draw(); // Abstract method
}

class Circle extends Shape {
  @override
  void draw() => print("Drawing a Circle");
}

class Rectangle extends Shape {
  @override
  void draw() => print("Drawing a Rectangle");
}

void main() {
  List<Shape> shapes = [Circle(), Rectangle()];
  for (var shape in shapes) {
    shape.draw();
  }
  // Output:
  // Drawing a Circle
  // Drawing a Rectangle
}
  • Abstract Classes: Define a contract for child classes to implement specific methods.

  • Dynamic Behavior: The draw method behaves differently for each subclass.


4. Other OOP Features in Dart

a) Mixins

Mixins allow a class to reuse properties and methods from multiple classes without inheritance.

Example: Using Mixins

mixin Flyable {
  void fly() => print("Flying...");
}

class Bird with Flyable {}

void main() {
  var bird = Bird();
  bird.fly(); // Output: Flying...
}

b) Static Members

Static members belong to the class rather than any instance.

Example: Static Members

class MathUtils {
  static const double pi = 3.14159;

  static double square(double num) => num * num;
}

void main() {
  print("Pi: ${MathUtils.pi}");
  print("Square of 3: ${MathUtils.square(3)}");
}

5. Best Practices for OOP in Dart

  1. Encapsulation: Always make fields private and use getters/setters for controlled access.

  2. Avoid Deep Inheritance: Prefer composition over inheritance for better maintainability.

  3. Leverage Mixins: Use mixins to share behavior without creating complex hierarchies.

  4. Use Abstract Classes: Define shared contracts for subclasses to ensure consistent implementation.

  5. Document Your Classes: Provide clear comments and documentation for better code readability.

  6. Follow Dart Naming Conventions:

    • Use PascalCase for class names (MyClass).

    • Use camelCase for variable and method names (myVariable, myMethod).


More from this blog

sangama

1285 posts