DART · STEP 2 · OOP
Dart · OOP
Dart 3.13 (any 3.0+ works): classes, constructors, methods, inheritance, mixins and static members — the JavaScript version next to the null-safe Dart version.
What you will learn#
- How to Create a Class
- How to Instantiate a Class
- Write a Constructor and Methods
- Create an Inherited Class
- Write Static Methods/Properties
Below you'll see JavaScript examples of object-oriented patterns and their Dart counterparts. Dart is a class-based language through and through — every value, including numbers and functions, is an object — so most of this will feel familiar.
Creating and Instantiating a Class#
In Javascript
// Creating a Classclass Dog {} // Instantiationconst Sparky = new Dog();In Dart
// Creating a classclass Dog {} void main() { // Instantiation: `new` is optional in Dart and left out by convention var sparky = Dog(); print(sparky); // Instance of 'Dog'}Convention: class names are UpperCamelCase, instances and members lowerCamelCase. There is no export keyword — everything in a file is public unless its name starts with _, which makes it private to that file (library).
The Constructor and Methods#
In Javascript
// Creating a Classclass Dog { // The constructor runs when we instantiate a new instance constructor(name, age) { this.name = name; this.age = age; } // A Method bark() { console.log(`${this.name} barks`); }} // Instantiationconst Sparky = new Dog("Sparky", 4);Sparky.bark();in Dart
class Dog { // instance fields are declared with their types. With null safety they must be // initialized — the constructor below does that before its body ever runs. String name; int age; // the constructor is named after the class. `this.name` is an "initializing // formal": it assigns the parameter straight to the field. No body needed. Dog(this.name, this.age); // a method. $name interpolates the field into the string. void bark() { print('$name barks!'); } // what print(dog) shows instead of "Instance of 'Dog'" @override String toString() => 'Dog(name: $name, age: $age)';} void main() { var sparky = Dog('Sparky', 4); sparky.bark(); // Sparky barks! print(sparky); // Dog(name: Sparky, age: 4)}WHY the short constructor: the 2021 version wrote Dog(String name, int age) { this.name = name; this.age = age; }. Dart 3 rejects that — name and age are non-nullable and would be unset while the constructor body runs. Dog(this.name, this.age); initializes them before the body, which is why it's the idiomatic form. Inside methods, this. is only needed when a local variable shadows a field.
Dart 3.13 adds primary constructors, which declare the fields and the constructor in the class header: class Dog(var String name, var int age) { void bark() => print('$name barks!'); }. The longer form above is what you'll see in most code today; both mean the same thing.
Inheritance#
In Javascript
// Creating a Classclass Dog { // The constructor runs when we instantiate a new instance constructor(name, age) { this.name = name; this.age = age; } // A Method bark() { console.log(`${this.name} barks`); }} class SmallDog extends Dog { constructor(name, age) { //Super calls the constructor of the parent class super(name, age); } //This will override the version of bark in the parent class bark() { console.log(`${this.name} yaps`); }} // Instantiationconst Sparky = new SmallDog("Sparky", 4);Sparky.bark();in Dart
class Dog { String name; int age; Dog(this.name, this.age); void bark() { print('$name barks!'); }} // extends works like JavaScript: one parent, members inheritedclass SmallDog extends Dog { // super parameters forward straight to the parent constructor. // Long form, same meaning: SmallDog(String name, int age) : super(name, age); SmallDog(super.name, super.age); // @override is optional, but the analyzer uses it to catch typos in the name @override void bark() { print('$name yaps!'); }} void main() { var sparky = SmallDog('Sparky', 4); sparky.bark(); // Sparky yaps! print(sparky is Dog); // true — `is` is instanceof}Constructors are not inherited: SmallDog has to declare one that reaches Dog's. Call the parent's version of an overridden method with super.bark().
Dart allows only one extends, but a class can mix in any number of mixins — reusable bundles of methods and fields:
// Dog as defined abovemixin Fetcher { int ballsFetched = 0; void fetch() { ballsFetched++; print('fetched ball number $ballsFetched'); }} class Retriever extends Dog with Fetcher { Retriever(super.name, super.age);} void main() { var goldie = Retriever('Goldie', 3); goldie.bark(); // from Dog goldie.fetch(); // from Fetcher}A mixin can't have a constructor and can't be instantiated on its own; with is how Dart gets the reuse of multiple inheritance without its ambiguity.
Static Methods and Properties#
In Javascript
// Static properties/methods belong to the class, not the instanceclass Calculator { static lastResult = 0; // a table of operator functions instead of eval() static operations = { "+": (a, b) => a + b, "-": (a, b) => a - b, "*": (a, b) => a * b, "/": (a, b) => a / b, }; static calculate(num1, num2, operator) { Calculator.lastResult = Calculator.operations[operator](num1, num2); return Calculator.lastResult; } static showLastResult() { console.log(Calculator.lastResult); }} Calculator.calculate(2, 2, "+");Calculator.showLastResult();In Dart
class Calculator { // a static property belongs to the class, not to instances. // num is the parent type of int and double, so 4 / 2 (a double in Dart) fits too. static num lastResult = 0; // a static method. Dart has no eval; a switch *expression* picks the operation // and evaluates to a value. `_` is the default case. static num calculate(num a, num b, String op) { lastResult = switch (op) { '+' => a + b, '-' => a - b, '*' => a * b, '/' => a / b, _ => throw ArgumentError('Unknown operator: $op'), }; return lastResult; } static void showLastResult() { print(lastResult); }} void main() { Calculator.calculate(2, 2, '+'); Calculator.showLastResult(); // 4 Calculator.calculate(9, 2, '/'); Calculator.showLastResult(); // 4.5}Static members are reached through the class (Calculator.lastResult), never through an instance, and static methods have no this. Inside the class you can drop the prefix, as calculate does. For class-wide constants use static const (static const maxDigits = 12;). If a "static method" needs nothing from the class at all, Dart style prefers a plain top-level function.
Resources to Learn More#
- Classes — instance variables, getters/setters, static members
- Constructors — initializing formals, named and
constconstructors, super parameters - Extend a class and Mixins —
extends,@override,super,with - Primary constructors — the Dart 3.13 shorthand for fields + constructor
See also