๐Ÿ—๏ธ Lesson 5: Object-Oriented Programming Basics

โฑ 35-45 min ๐Ÿ“Š Intermediate ๐Ÿ“– In-depth tutorial ๐Ÿ”— Official Source
1

Why Object-Oriented Programming?

๐Ÿง  The Core Idea

OOP organizes code into objectsโ€”self-contained units that bundle data (properties) and behavior (methods) together. Instead of scattered variables and functions, you create reusable blueprints called classes.

๐Ÿ“‹

Class = Blueprint

A plan that describes how to build something. The class Car defines what every car has.

๐Ÿš—

Object = Instance

An actual car built from the blueprint. var myCar = Car('Tesla', 2026) creates one.

๐Ÿ“

Properties = Attributes

Data stored in the object. Color, model, year, speed.

๐Ÿ”ง

Methods = Actions

Things the object can do. Accelerate, brake, honk.

๐Ÿ’ก The Problem OOP Solves: Without OOP, related data and functions are scattered. With OOP, everything about a "Car" lives inside the Car classโ€”encapsulated, organized, and reusable.
2

Anatomy of a Class

๐Ÿง  Every class has three main parts:

1. Properties (also called fields or instance variables) โ€” the data.
2. Constructor โ€” a special method that creates and initializes objects.
3. Methods โ€” functions that define what objects can do.

Animal
๐Ÿ“ String name
๐Ÿ”ข int age
๐Ÿพ String species
๐Ÿ—๏ธ Animal(this.name, this.age, this.species)
๐Ÿ”Š void makeSound()
๐Ÿฝ๏ธ void eat(String food)
๐Ÿ“‹ String get description

๐Ÿ“– Member Details

Click on any property, constructor, or method to see what it does.

Complete Class Example with Line-by-Line Explanation

// Defining a class called Animal
class Animal {
  // Properties (instance variables)
  String name;
  int age;
  String species;
  
  // Constructor โ€” creates new Animal objects
  Animal(this.name, this.age, this.species);
  
  // Method โ€” an action the animal can do
  void makeSound() {
    print('\$name makes a sound');
  }
  
  // Method with a parameter
  void eat(String food) {
    print('\$name eats \$food');
  }
  
  // Getter โ€” a computed property (no parentheses)
  String get description {
    return '\$name is a \${age}-year-old \$species';
  }
}

๐Ÿ“ Line-by-Line

1 class keyword defines a new type
3-5 Properties store each object's data
8 Constructor: this refers to the current instance
11-13 Method: a function inside a class
12 \$name interpolates the property value
16-18 Method with parameter
21-23 Getter: accessed like a property, no ()

Using the Class

// Creating objects (instances) of the Animal class
void main() {
  // Create two different Animal objects
  var cat = Animal('Whiskers', 3, 'Feline');
  var dog = Animal('Buddy', 5, 'Canine');
  
  // Accessing properties
  print(cat.name);      // Whiskers
  print(dog.age);       // 5
  
  // Calling methods
  cat.makeSound();        // Whiskers makes a sound
  dog.eat('kibble');      // Buddy eats kibble
  
  // Using the getter (no parentheses!)
  print(cat.description); // Whiskers is a 3-year-old Feline
}

โœ๏ธ Try It: Create Your Own Animal

Output will appear here...
3

Constructors: Different Ways to Create Objects

๐Ÿง  Constructors are special methods named after the class

Dart provides several constructor types for different scenarios. The most common is the default constructor, but you can also create named constructors for alternative initialization paths.

1๏ธโƒฃ Default Constructor

The most common type. Automatically provided if you don't define one. Use this.property for shorthand initialization.

class Point {
  double x;
  double y;
  
  // Default constructor with shorthand syntax
  Point(this.x, this.y);
}

var p = Point(3.0, 4.0); // x=3.0, y=4.0

2๏ธโƒฃ Named Constructors

Give your class multiple ways to be created. Use the syntax ClassName.constructorName().

class Point {
  double x, y;
  
  Point(this.x, this.y);
  
  // Named constructor: creates origin point
  Point.origin()
      : x = 0,
        y = 0;
  
  // Named constructor: creates point from map
  Point.fromMap(Map<String, double> map)
      : x = map['x'] ?? 0,
        y = map['y'] ?? 0;
}

var origin = Point.origin();           // x=0, y=0
var fromMap = Point.fromMap({'x': 5, 'y': 10}); // x=5, y=10

3๏ธโƒฃ Factory Constructors

Use factory when you don't always want to create a new instance. Common for caching, singletons, or returning subtypes.

class Logger {
  static Logger? _instance;
  
  // Private constructor (can't be called from outside)
  Logger._();
  
  // Factory returns the same instance every time
  factory Logger() {
    _instance ??= Logger._();
    return _instance!;
  }
}

var log1 = Logger();
var log2 = Logger();
print(log1 == log2); // true โ€” same instance!

4๏ธโƒฃ Constant Constructors

Use const to create compile-time constants. All properties must be final. These are immutable and share memory.

class ImmutablePoint {
  final double x;
  final double y;
  
  const ImmutablePoint(this.x, this.y);
}

const p1 = ImmutablePoint(0, 0);
const p2 = ImmutablePoint(0, 0);
print(p1 == p2); // true โ€” identical constants share memory
4

Inheritance: Reusing Code with Hierarchies

๐Ÿง  Inheritance lets a class reuse properties and methods from a parent class

Use the extends keyword. The child class automatically gets everything the parent has, and can add or override its own behavior. Use super to refer to the parent class.

โŒ Without Inheritance (Duplication)

class Dog {
  String name;
  void eat() { print('Eating...'); }
  void sleep() { print('Sleeping...'); }
  void bark() { print('Woof!'); }
}
class Cat {
  String name;
  void eat() { print('Eating...'); }
  void sleep() { print('Sleeping...'); }
  void meow() { print('Meow!'); }
}

๐Ÿ˜ฐ eat() and sleep() duplicated in every class!

โœ… With Inheritance (Clean)

class Animal {
  String name;
  Animal(this.name);
  void eat() { print('Eating...'); }
  void sleep() { print('Sleeping...'); }
}
class Dog extends Animal {
  Dog(super.name);
  void bark() { print('Woof!'); }
}
class Cat extends Animal {
  Cat(super.name);
  void meow() { print('Meow!'); }
}

๐Ÿ˜ Shared code in parent, unique behavior in children!

๐ŸŒณ Class Hierarchy

๐Ÿพ Animal
๐Ÿ• Mammal
๐Ÿฆฎ Dog
๐Ÿฑ Cat
๐Ÿฆ Bird
๐Ÿฆœ Parrot
๐Ÿฆ… Eagle

๐Ÿ“‹ Class Details

Click on any class to see its role in the hierarchy.

๐Ÿ“Œ Overriding Methods: A child class can replace a parent's method using @override. This is how a Dog's makeSound() says "Woof!" while a Cat's says "Meow!".

Complete Inheritance Example with Override

class Animal {
  String name;
  Animal(this.name);
  
  void makeSound() {
    print('\$name makes a generic sound');
  }
}

class Dog extends Animal {
  Dog(super.name); // Forward name to parent constructor
  
  @override
  void makeSound() {
    print('\$name says: Woof! Woof!'); // Override!
  }
}

class Cat extends Animal {
  Cat(super.name);
  
  @override
  void makeSound() {
    print('\$name says: Meow! Meow!');
  }
}

void main() {
  var dog = Dog('Buddy');
  var cat = Cat('Whiskers');
  
  dog.makeSound(); // Buddy says: Woof! Woof!
  cat.makeSound(); // Whiskers says: Meow! Meow!
}
5

Polymorphism: One Interface, Many Behaviors

๐Ÿง  "Poly" = many, "morph" = form

Polymorphism lets you treat objects of different classes through a shared parent type. You can write code that works with Animal without knowing if it's a Dog, Cat, or Birdโ€”each responds in its own way.

๐Ÿ”‘ The Magic: When you call animal.makeSound() on a variable typed as Animal, Dart calls the actual object's version of the method, not the parent's. This is called dynamic dispatch.
// Polymorphism: Treat all animals the same way
void makeAllSpeak(List<Animal> animals) {
  for (var animal in animals) {
    animal.makeSound(); // Each animal speaks in its own way!
  }
}

void main() {
  var animals = [
    Dog('Buddy'),
    Cat('Whiskers'),
    Dog('Rex'),
    Cat('Luna'),
  ];
  
  makeAllSpeak(animals);
  // Buddy says: Woof! Woof!
  // Whiskers says: Meow! Meow!
  // Rex says: Woof! Woof!
  // Luna says: Meow! Meow!
}

๐Ÿฆ Build Your Zoo

No animals in the zoo yet.

๐ŸŽต Make Them Speak!

Click "Speak All!" to hear every animal...
6

Abstract Classes: Defining Contracts

๐Ÿง  An abstract class defines WHAT must be done, not HOW

Mark a class with abstract to prevent it from being instantiated directly. It can declare methods without implementationsโ€”forcing every subclass to provide their own. Think of it as a contract: "If you extend me, you MUST implement these methods."

๐Ÿ“œ The Contract (Abstract)

abstract class Shape {
  // Abstract getters โ€” no body!
  double get area;
  double get perimeter;
  
  // Concrete method โ€” has a body
  void display() {
    print('Area: \$area');
    print('Perimeter: \$perimeter');
  }
}

โš ๏ธ Cannot do: var s = Shape(); โ€” abstract!

๐Ÿ”ต Circle Implementation
class Circle extends Shape {
  double radius;
  Circle(this.radius);
  
  @override
  double get area => 3.14159 * radius * radius;
  
  @override
  double get perimeter => 2 * 3.14159 * radius;
}
๐ŸŸฆ Rectangle Implementation
class Rectangle extends Shape {
  double width, height;
  Rectangle(this.width, this.height);
  
  @override
  double get area => width * height;
  
  @override
  double get perimeter => 2 * (width + height);
}

โœ๏ธ Try It: Create a Shape and Display It

Output will appear here...

๐ŸŽฏ OOP Concept Quiz

Score: 0/5 | Hints: 3

Loading question...

๐Ÿš€

Practice Projects

Apply what you've learned by building these hands-on projects.

๐Ÿ“ฆ Project 1 ๐Ÿ“ฆ Project 2