Home
Dart
OOP
Flutter
Contact

OOP: Classes and Objects

Learn the building blocks of object-oriented programming in Dart.

Defining Classes

A class is a blueprint for creating objects. In Dart, classes are defined using the class keyword:

user.dart
class User {
  String name;
  String email;
  int age;

  // Constructor
  User(this.name, this.email, this.age);

  // Method
  String greet() {
    return 'Hello, I\'m $name and I\'m $age years old.';
  }
}

Instantiating Objects

main.dart
void main() {
  // Create instances
  var alice = User('Alice', '[email protected]', 28);
  var bob = User('Bob', '[email protected]', 32);

  // Access properties
  print(alice.name);   // Alice
  print(bob.email);    // [email protected]

  // Call methods
  print(alice.greet());
  // Hello, I'm Alice and I'm 28 years old.

  // Modify properties
  bob.age = 33;
}

Methods

Methods are functions defined inside a class. They operate on object instances:

calculator.dart
class Calculator {
  double _result = 0;

  void add(double value) => _result += value;
  void subtract(double value) => _result -= value;
  void multiply(double value) => _result *= value;
  void reset() => _result = 0;
  double getResult() => _result;
}

void main() {
  var calc = Calculator();
  calc.add(10);
  calc.multiply(3);
  calc.subtract(5);
  print(calc.getResult());  // 25.0
}

Constructor Types at a Glance

Dart offers several constructor flavors, each solving a different problem:

Constructor TypeSyntaxUse Case
DefaultUser()No custom initialization needed
ParameterizedUser(name, age)Required fields at creation
NamedUser.guest()Multiple creation patterns
Constconst User()Immutable objects
Factoryfactory User()Complex creation logic
💡 Named Constructors Are Powerful Named constructors like User.fromJson() or User.anonymous() make your code self-documenting. Instead of reading the constructor parameters, the name tells you exactly what kind of object is being created.

Factory Constructors

Factory constructors are unique — they can return existing instances instead of always creating new ones:

factory_constructor.dart
class Database {
  static Database? _instance;

  // Factory constructor returns existing instance
  factory Database() {
    _instance ??= Database._internal();
    return _instance!;
  }

  Database._internal(); // Private named constructor
}

var db1 = Database();
var db2 = Database();
print(db1 == db2); // true — singleton pattern!

Constructors

product.dart
class Product {
  String id;
  String name;
  double price;
  DateTime createdAt;

  // Parameterized constructor
  Product(this.id, this.name, this.price)
      : createdAt = DateTime.now();

  // Constructor with initializer list
  Product.withDiscount(this.id, this.name, double originalPrice, double discountPercent)
      : price = originalPrice * (1 - discountPercent / 100),
        createdAt = DateTime.now();

  String toString() => '$name (\$$price)';
}

Named Constructors

Named constructors provide multiple ways to create an object:

color.dart
class Color {
  int r, g, b;

  Color(this.r, this.g, this.b);

  // Named constructors
  Color.red() : r = 255, g = 0, b = 0;
  Color.green() : r = 0, g = 255, b = 0;
  Color.blue() : r = 0, g = 0, b = 255;

  Color.fromHex(String hex)
      : r = int.parse(hex.substring(1, 3), radix: 16),
        g = int.parse(hex.substring(3, 5), radix: 16),
        b = int.parse(hex.substring(5, 7), radix: 16);
}

void main() {
  var red = Color.red();
  var custom = Color.fromHex('#6366f1');
  print('Red: ${red.r}, ${red.g}, ${red.b}');
}

Real-World Example: User Profile System

user_profile.dart
class UserProfile {
  String id;
  String displayName;
  String email;
  String avatarUrl;
  DateTime joinedAt;
  bool isVerified;

  UserProfile({
    required this.id,
    required this.displayName,
    required this.email,
    this.avatarUrl = '',
    this.isVerified = false,
  }) : joinedAt = DateTime.now();

  String get initials => displayName
      .split(' ')
      .map((w) => w[0])
      .join()
      .toUpperCase();

  int get accountAgeDays =>
      DateTime.now().difference(joinedAt).inDays;

  String get displayNameFormatted =>
      '$displayName${isVerified ? " ✅" : ""}';

  void verify() => isVerified = true;

  Map<String, dynamic> toJson() => {
    'id': id,
    'name': displayName,
    'email': email,
    'verified': isVerified,
  };
}

void main() {
  var user = UserProfile(
    id: 'u_001',
    displayName: 'Jane Smith',
    email: '[email protected]',
  );

  print(user.displayNameFormatted);  // Jane Smith
  print('Initials: ${user.initials}'); // JS
  user.verify();
  print(user.displayNameFormatted);  // Jane Smith ✅
  print(user.toJson());
}

Summary

  • Classes define blueprints; objects are instances of classes
  • Constructor shorthand: User(this.name) assigns directly
  • Named constructors provide readable alternative creation methods
  • Getters provide computed properties without method call syntax
🚀 Next Step Continue to Encapsulation & Setters/Getters to learn data protection patterns.
K
Kesavaraja Murugesan
Flutter Developer & Educator

Flutter developer with 3 years of experience building production-grade mobile applications. Passionate about teaching clean architecture patterns and helping developers write maintainable, scalable code.

← Control Flow Next: Encapsulation →