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 Type | Syntax | Use Case |
|---|---|---|
| Default | User() | No custom initialization needed |
| Parameterized | User(name, age) | Required fields at creation |
| Named | User.guest() | Multiple creation patterns |
| Const | const User() | Immutable objects |
| Factory | factory 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.