Welcome back! In our previous lesson, we established a solid foundation in Dart's basic syntax, covering variables, null safety, and control flow. Now, we'll build upon that by exploring one of the most crucial aspects of the language for app development: its object-oriented programming (OOP) features.
Today's lesson focuses on how to structure your code using classes. Given your experience with classes in both C++ and JavaScript/TypeScript, many of these concepts will be familiar, but we'll pay close attention to Dart's unique and powerful implementations. We will define classes and their constructors, and then explore how to reuse code through inheritance and mixins. Mastering these concepts is fundamental to building well-structured and scalable Flutter applications.
The Anatomy of a Dart Class
In Dart, everything is an object, and every object is an instance of a class. A class is essentially a blueprint for creating objects, grouping related data (properties) and functions (methods). This principle of encapsulation should be very familiar from your work in C++.
Let's start by defining a simple class.
class Developer {
// Properties (or fields)
final String name;
final int experienceYears;
// A simple method
void work() {
print('$name is writing some code...');
}
}
This looks quite similar to class definitions in other languages. However, you'll notice the compiler gives an error: Non-nullable instance field 'name' must be initialized. Dart's sound null safety requires that we guarantee all non-nullable fields have a value. This is where constructors come in.
Constructing Objects
A constructor is a special method that is called when you create a new instance of a class. Its primary job is to initialize the object's properties.
The following article gives a great introduction to classes and constructors in Dart, written specifically for developers coming from JavaScript.
Beginner Dart Language Tutorial for JavaScript Developers
This article from RisingStack Engineering provides a good overview of OOP in Dart and how to define your first class.
First, quickly read the introductory section, "A quick intro to OOP and classes," which frames Dart's OOP principles. Then, study the code example in the "Real-Life Dart Examples" subsection. Notice how the Developer class is defined. The constructor syntax used here, Developer(this.name, this.experienceYears), is the most common and idiomatic way to write constructors in Dart. We'll explore this "syntactic sugar" next.
Types of Constructors
Dart provides several powerful ways to define constructors, offering more flexibility than you might be used to. While you could initialize fields within the constructor body, similar to C++, Dart encourages a more concise approach.
The article "Types of construtors in dart" by Alberto Azinar provides an excellent breakdown of the different constructor patterns.
This article clearly explains the various constructor types available in Dart. We'll focus on the most important ones.
Start with Type #1. This shows the verbose style of initialization that's similar to what you'd do in C++ or Java. Notice the need for the late keyword here. Next, read Type #2. This is the syntactic sugar ClassName(this.field) we saw earlier. This is the preferred, idiomatic way to write constructors in Dart. Read about Named constructors in Type #3. This is Dart's answer to constructor overloading, which you'll recognize from C++. It allows you to define multiple, clearly named ways to create an object. Briefly look at Type #4. These are useful for reducing code duplication by having one constructor call another. Finally, read about Type #5. The const constructor is a vital concept in Flutter for performance. When you create an object using a const constructor with the same arguments, Dart can reuse the same instance in memory.
To see these concepts in action, the following video provides a comprehensive visual guide.
Dart Classes Explained I - All Fields, Methods, Constructors, Operators, Getters/Setters & Singleton
This Flutterly video, "Dart Classes Explained I," offers a deep dive into constructors. We'll focus on the most common patterns.
Watch the segment from the beginning of constructors. This part covers custom constructors, initializer lists (useful for final fields), and the powerful syntactic sugar (this.field) that simplifies initialization. Next, watch the section on specialized constructors from the introduction to multiple constructors. This will visually demonstrate named constructors (e.g., fromJSON) and const constructors, reinforcing what you just read.
With this knowledge, we can now correctly write our Developer class:
class Developer {
final String name;
final int experienceYears;
// The idiomatic Dart constructor
Developer(this.name, this.experienceYears);
// A named constructor for a junior developer
Developer.junior(this.name) : experienceYears = 0;
// A const constructor might be useful if you had predefined developers
// const Developer(this.name, this.experienceYears);
void work() {
print('$name is writing some code...');
}
}
void main() {
var dev1 = Developer('Alice', 5);
var dev2 = Developer.junior('Bob');
dev1.work(); // Prints "Alice is writing some code..."
print(dev2.experienceYears); // Prints 0
}
Code Reuse: Inheritance vs. Mixins
Now that we can create blueprints for our objects, let's explore how to share code between them. Dart provides two primary mechanisms for this: inheritance and mixins. Your C++ background gives you a good foundation for inheritance, while mixins will likely be a new, powerful tool.
Inheritance: The "Is-A" Relationship
Inheritance allows a class (the subclass) to inherit properties and methods from another class (the superclass). This is used to create an "is-a" relationship. For example, a SoftwareEngineer is a type of Employee.
In Dart, you use the extends keyword for inheritance, just like in JavaScript. A crucial point of difference from C++ is that Dart only allows a class to extend a single superclass.
The "Beginner Dart Language Tutorial" we looked at earlier has a great section on this.
Beginner Dart Language Tutorial for JavaScript Developers
This section explains both inheritance and mixins with clear examples.
Read the section "Inheritance and mixins in Dart". Pay close attention to how RisingStackEngineer extends Developer and how it uses the super() keyword to call the parent class's constructor. This is analogous to how you'd call a base class constructor in C++ or super() in a JS subclass.
Mixins: The "Has-An-Ability" Relationship
What if you want to share behavior across unrelated classes? For example, both a Bird and a Superman can fly, but a Superman is not a Bird. This is where multiple inheritance in C++ can be used, but it can lead to complex issues like the "diamond problem."
Dart's solution is elegant: mixins. A mixin is a way of reusing a class's code in multiple class hierarchies. It establishes a "has-an-ability" relationship. Instead of saying a Developer is a MobileDev, we say a Developer has the ability to do mobile development. You use the with keyword to apply a mixin to a class.

Let's apply this to a code example. A Developer can have the ability to do mobile development, web development, or both.
// A mixin is declared with the `mixin` keyword.
// It can contain methods and properties.
mixin MobileDev {
void buildApp() {
print('Building a mobile app...');
}
}
mixin WebDev {
void buildWebsite() {
print('Building a website...');
}
}
// Our Developer class can now compose these abilities.
class Developer {
final String name;
Developer(this.name);
}
// This developer is a specialist.
class FullStackDeveloper extends Developer with MobileDev, WebDev {
// We need to call the parent constructor.
FullStackDeveloper(String name) : super(name);
}
void main() {
var dev = FullStackDeveloper('Charlie');
dev.buildApp(); // "Building a mobile app..." (from MobileDev mixin)
dev.buildWebsite(); // "Building a website..." (from WebDev mixin)
}
The power of mixins is that they decouple behavior from a strict inheritance tree. You can apply the MobileDev mixin to any class that needs that functionality.
This diagram illustrates the concept perfectly. An Animal class is extended by Mammal, Bird, and Fish. However, abilities like Walker, Swimmer, and Flyer are applied as mixins across the hierarchy. For example, a Dolphin is a Mammal that with Swimmer is applied to, while a Bat is a Mammal with Walker and Flyer.

Conclusion
In this lesson, we've bridged your existing OOP knowledge from C++ and JavaScript to the Dart ecosystem. You've learned how to define robust and reusable code structures, which will be the backbone of your Flutter apps.
Here are the key takeaways:
- Classes in Dart serve as blueprints for objects, enforcing structure through properties and methods.
- Constructors are highly flexible, with idiomatic syntactic sugar (
this.fieldName), named constructors for clarity (like C++ overloading), andconstconstructors for performance. - Inheritance (
extends) creates a strict "is-a" hierarchy, but Dart only allows single inheritance. - Mixins (
with) provide a powerful and safe way to achieve code reuse across different classes, establishing a "has-an-ability" relationship.
In our next lesson, we will shift our focus from object structure to data processing. We will explore Dart's collections (like List and Map), the concept of generics for type safety, and powerful higher-order functions like map, where, and fold for manipulating data—tools that will feel very familiar from your JavaScript experience.