ZetCode

Classes in Dart

last modified May 25, 2025

In this article we will explore classes in Dart, a powerful feature of the Dart programming language that enables object-oriented programming (OOP).

Dart Classes Overview

Classes are at the core of object-oriented programming in Dart. They act as blueprints for creating objects that encapsulate data and behavior. This tutorial comprehensively covers working with Dart classes, from basic class definitions to advanced concepts such as mixins, extensions, and abstract classes.

By leveraging Dart's class system, developers can write clean, reusable, and modular code. The ability to implement encapsulation, inheritance, and polymorphism makes Dart's class structure an essential tool for designing scalable applications.

Feature Description Example
Class Definition Blueprint for creating objects class Point { ... }
Constructors Methods for initializing objects Point(this.x, this.y);
Properties Variables within a class double x, y;
Methods Functions within a class void move() { ... }
Inheritance Enables creation of subclasses class Vector extends Point {}
Mixins Allows sharing behaviors across classes class Robot with Walker {}
Abstract Classes Defines structure without implementation abstract class Shape { ... }
Extensions Adds functionality to existing classes extension on String { ... }

A class in Dart is a user-defined type that combines state (properties) and behavior (methods). It allows developers to model real-world entities, define relationships between objects, and enforce structured programming.

Basic Class Definition

A class in Dart is defined using the class keyword followed by the class name. The body of the class contains its properties (data) and methods (behavior). Here's a simple example of a class representing a point in 2D space:

basic_class.dart
class Point {
  // Instance variables
  double x;
  double y;
  
  // Constructor
  Point(this.x, this.y);
  
  // Instance method
  void move(double dx, double dy) {
    x += dx;
    y += dy;
  }
  
  // Overriding toString()
  @override
  String toString() => 'Point($x, $y)';
}

void main() {
  // Create an instance
  var p = Point(3.0, 4.0);
  print(p); // Uses toString()
  
  // Access properties and methods
  p.move(1.0, -1.0);
  print('New position: (${p.x}, ${p.y})');
}

This example demonstrates several key aspects of Dart classes. The Point class has two instance variables (x and y), a constructor that initializes these variables, a method to move the point, and an overridden toString method.

The main function shows how to create an instance of the class and interact with its members. The constructor syntax Point(this.x, this.y) is a Dart shorthand for assigning constructor parameters to instance variables.

$ dart run basic_class.dart
Point(3.0, 4.0)
New position: (4.0, 3.0)

Constructors and Initialization

Dart offers several ways to initialize class instances. Constructors can be simple or complex, with options for named parameters, initializer lists, and factory constructors. Here's a more advanced example demonstrating these features:

constructors.dart
class Rectangle {
  final double width;
  final double height;
  final String color;
  
  // Main constructor
  Rectangle(this.width, this.height, [this.color = 'black']);
  
  // Named constructor
  Rectangle.square(double size, String color) 
    : this(size, size, color);
  
  // Constructor with initializer list
  Rectangle.fromJson(Map<String, dynamic> json)
    : width = json['width'],
      height = json['height'],
      color = json['color'] ?? 'black';
  
  // Factory constructor
  factory Rectangle.fromString(String dimensions) {
    var parts = dimensions.split('x');
    return Rectangle(
      double.parse(parts[0]),
      double.parse(parts[1])
    );
  }
  
  double get area => width * height;
}

void main() {
  var rect1 = Rectangle(10.0, 20.0);
  var rect2 = Rectangle.square(15.0, 'red');
  var rect3 = Rectangle.fromJson({'width': 5.0, 'height': 8.0});
  var rect4 = Rectangle.fromString('12x24');
  
  print('Area of rect1: ${rect1.area}');
  print('Area of rect2: ${rect2.area}');
  print('Area of rect3: ${rect3.area}');
  print('Area of rect4: ${rect4.area}');
}

This example shows four different ways to construct Rectangle objects. The main constructor uses positional parameters with an optional color parameter. The named constructor Rectangle.square provides a convenient way to create square rectangles. The fromJson constructor demonstrates initializer list syntax for processing input data. The factory constructor fromString shows how to implement alternative object creation logic. The area getter demonstrates computed properties.

$ dart run constructors.dart
Area of rect1: 200.0
Area of rect2: 225.0
Area of rect3: 40.0
Area of rect4: 288.0

Inheritance and Polymorphism

Dart supports single inheritance where a class can extend one other class. The following example demonstrates inheritance, method overriding, and polymorphism:

inheritance.dart
class Shape {
  final String color;
  
  Shape(this.color);
  
  double get area => 0;
  
  void describe() {
    print('This $runtimeType has color $color and area $area');
  }
}

class Circle extends Shape {
  final double radius;
  
  Circle(String color, this.radius) : super(color);
  
  @override
  double get area => 3.14159 * radius * radius;
}

class Square extends Shape {
  final double side;
  
  Square(String color, this.side) : super(color);
  
  @override
  double get area => side * side;
}

void main() {
  var shapes = [
    Circle('red', 5.0),
    Square('blue', 4.0),
    Shape('green')
  ];
  
  for (var shape in shapes) {
    shape.describe();
  }
}

The Shape class serves as the base class with a default implementation of area and a describe method. The Circle and Square classes extend Shape and override the area getter with their specific implementations. The main function demonstrates polymorphism by treating different shape types uniformly through the base class interface. The runtimeType property shows Dart's built-in support for getting an object's actual type at runtime.

$ dart run inheritance.dart
This Circle has color red and area 78.53975
This Square has color blue and area 16.0
This Shape has color green and area 0

Mixins and Composition

Dart uses mixins to share code across multiple class hierarchies. A mixin is a way to reuse a class's code in multiple class hierarchies without using inheritance. Here's an example demonstrating mixins and composition:

mixins.dart
// Mixins can't have constructors
mixin Logger {
  void log(String message) {
    print('Log: $message - ${DateTime.now()}');
  }
}

mixin JsonSerializable {
  String toJsonString() {
    return '{"type": "$runtimeType"}';
  }
}

class Person with Logger {
  final String name;
  final int age;
  
  Person(this.name, this.age);
  
  void celebrateBirthday() {
    age++;
    log('$name is now $age years old');
  }
}

class Product with Logger, JsonSerializable {
  final String id;
  final double price;
  
  Product(this.id, this.price);
  
  void applyDiscount(double percent) {
    final discount = price * (percent / 100);
    log('Applying $percent% discount (\$$discount) to product $id');
  }
}

void main() {
  var person = Person('Alice', 30);
  person.celebrateBirthday();
  
  var product = Product('12345', 99.99);
  product.applyDiscount(10);
  print(product.toJsonString());
}

The Logger and JsonSerializable mixins provide reusable functionality that can be added to any class. The Person class uses only the Logger mixin, while Product uses both mixins. Mixins are included using the with keyword. This approach allows for code reuse without the limitations of single inheritance, as a class can use multiple mixins while also extending another class.

$ dart run mixins.dart
Log: Alice is now 31 years old - 2025-05-25 14:30:45.123456
Log: Applying 10% discount ($9.999) to product 12345 - 2025-05-25 14:30:45.123456
{"type": "Product"}

Advanced Class Features

Dart provides several advanced class features including abstract classes, interfaces, extension methods, and operator overloading. The following example demonstrates these concepts:

advanced_features.dart
// Abstract class
abstract class Animal {
  String get name;
  void makeSound();
}

// Interface (implicit in Dart)
class Bird {
  void fly() => print('Flying high');
}

// Class implementing multiple interfaces
class Parrot extends Animal implements Bird {
  @override
  final String name;
  
  Parrot(this.name);
  
  @override
  void makeSound() => print('$name says: Squawk!');
  
  @override
  void fly() => print('$name is flying in circles');
  
  // Operator overloading
  Parrot operator +(Parrot other) => Parrot('$name & ${other.name}');
}

// Extension method
extension on Parrot {
  void repeat(String phrase) {
    print('$name repeats: $phrase $phrase $phrase');
  }
}

void main() {
  var polly = Parrot('Polly');
  var matey = Parrot('Matey');
  
  polly.makeSound();
  polly.fly();
  
  var couple = polly + matey;
  print('New parrot: ${couple.name}');
  
  // Using extension method
  polly.repeat('Hello');
}

The Animal abstract class defines an interface that Parrot implements. Dart doesn't have a separate interface keyword - any class can serve as an interface. The Parrot class also implements the Bird interface. Operator overloading is demonstrated with the + operator, and an extension method adds functionality to the Parrot class without modifying its source code. These features provide flexibility in designing class hierarchies and extending existing functionality.

$ dart run advanced_features.dart
Polly says: Squawk!
Polly is flying in circles
New parrot: Polly & Matey
Polly repeats: Hello Hello Hello

Source

Dart Language: Classes
Dart Language: Mixins
Dart Language: Extensions

Classes are fundamental to Dart's object-oriented programming model. They provide encapsulation of data and behavior, support inheritance and polymorphism through class hierarchies, and enable code reuse through mixins. Dart's class system is designed to be clear and expressive while supporting modern programming patterns. Understanding classes is essential for effective Dart programming, from simple data structures to complex application architectures.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.