Introducing Classes and Objects
So far, your Dart programs have used simple values: numbers, strings, and lists. That works for small scripts, but real programs deal with richer things — a bank account, a shopping cart, a game character. Dart lets you create your own kinds of values using classes.
A class is a blueprint. It describes two things: what data a thing holds (its fields) and what it can do (its methods). An object is one actual thing built from that blueprint. One class can produce many objects, just as one cookie cutter can make many cookies.
Here is a small class that counts things:
class Counter {
int _count = 0; // field, starting at zero
int get count => _count; // getter: read the count
void increment() => _count++; // method: add one
@override
String toString() => 'Counter($_count)'; // nice text form
}
void main() {
final c = Counter(); // create an object
c.increment();
c.increment();
print(c); // Counter(2)
}A few things to notice. Counter() calls the class like a function — this constructs a new object. The underscore in _count marks the field as private, meaning code outside the class should not touch it directly. Instead, we expose it safely through the count getter, which looks like a variable when you read c.count. The toString method controls how the object prints; @override tells Dart we are replacing a built-in method.
Why bother with classes instead of loose variables? Because a class keeps related data and behavior together, and it can protect its data. No outside code can set _count to a strange value like -5 by accident — they must go through increment(). This idea of hiding the details and offering a clean surface is called encapsulation, and it keeps programs easier to fix and grow.
Objects are independent. Make two counters, and each keeps its own count:
void main() {
final a = Counter();
final b = Counter();
a.increment();
print(a); // Counter(1)
print(b); // Counter(0)
}Classes can also hold different data in each object. Here, each Point remembers its own coordinates:
class Point {
final double x;
final double y;
Point(this.x, this.y); // constructor: fills x and y
@override
String toString() => '($x, $y)';
}
void main() {
final origin = Point(0, 0);
final spot = Point(3.5, 2.0);
print(spot); // (3.5, 2.0)
}The line Point(this.x, this.y) is a constructor — a special method that runs when the object is created. this.x means "the x field of this particular object." The final keyword means each point's coordinates cannot change after creation, which prevents a whole family of bugs.
You will use classes constantly in Dart, especially if you later explore Flutter, where every button and screen is an object. In the next chapter we will go deeper into constructors, fields, and methods.
Exercise
What is the difference between a class and an object?
Show answer
A class is the blueprint that describes fields and methods; an object is one concrete instance created from that blueprint with its own data.
Exercise
Why does the field _count start with an underscore?
Show answer
In Dart, a leading underscore makes a field or method private to its library, so outside code cannot modify it directly and must use the class's methods and getters.
Spotted a mistake, or code that does not work?