Inheritance in Dart
Inheritance is one of the fundamental concepts of Object-Oriented Programming (OOP) in Dart. It allows one class to acquire and reuse properties and methods from another class. Inheritance helps developers create reusable, organized, and maintainable code.
In Dart and Flutter development, inheritance is important because many application components are built using classes and common parent-child relationships. JustAcademy's Flutter curriculum includes Object-Oriented Programming in Dart along with classes, objects, constructors, inheritance, polymorphism, and abstraction. :contentReference[oaicite:0]{index=0}
Flutter Training Course: https://www.justacademy.co/course-detail/flutter-training
Course Demo Registration: https://www.justacademy.co/register-for-course-demo
1. What is Inheritance?
Inheritance is an OOP mechanism in which a child class derives from a parent class and can use members defined by that parent.
In Dart, inheritance between classes is created using the extends keyword.
class Animal {
void eat() {
print('Animal is eating');
}
}
class Dog extends Animal {
void bark() {
print('Dog is barking');
}
}
void main() {
Dog dog = Dog();
dog.eat();
dog.bark();
}
Output:
Animal is eating
Dog is barking
The Dog class inherits the eat() method from the Animal class.
2. Important Terms in Inheritance
| Term |
Meaning |
| Parent Class |
The class whose members are inherited. |
| Child Class |
The class that inherits from another class. |
| Superclass |
Another name for the parent class. |
| Subclass |
Another name for the child class. |
extends |
Keyword used to create class inheritance. |
super |
Used to access members of the superclass. |
@override |
Annotation used when a subclass provides a new implementation of an inherited member. |
3. Why Use Inheritance?
Inheritance can be useful for several reasons:
- Code reusability
- Reducing duplicate code
- Creating logical parent-child relationships
- Extending existing functionality
- Supporting polymorphism
- Improving maintainability
- Creating common abstractions
4. Basic Syntax of Inheritance
The basic syntax is:
class Parent {
// Parent members
}
class Child extends Parent {
// Child members
}
The Child class can use accessible members inherited from Parent and can also define its own members.
5. Simple Real-World Example
Consider a real-world relationship between a general Vehicle and a specific Car.
class Vehicle {
void start() {
print('Vehicle started');
}
void stop() {
print('Vehicle stopped');
}
}
class Car extends Vehicle {
void drive() {
print('Car is driving');
}
}
void main() {
Car car = Car();
car.start();
car.drive();
car.stop();
}
Output:
Vehicle started
Car is driving
Vehicle stopped
Car automatically gets access to the inherited start() and stop() methods.
6. Inherited Properties
A child class can access accessible instance properties inherited from its parent.
class Person {
String name = 'Rahul';
int age = 25;
}
class Student extends Person {
String course = 'Flutter';
}
void main() {
Student student = Student();
print(student.name);
print(student.age);
print(student.course);
}
Output:
Rahul
25
Flutter
The Student class has its own course property and also gets access to the inherited name and age properties.
7. Inherited Methods
Child classes can use methods inherited from the parent class.
class Employee {
void login() {
print('Employee logged in');
}
void logout() {
print('Employee logged out');
}
}
class Developer extends Employee {
void writeCode() {
print('Developer is writing code');
}
}
void main() {
Developer developer = Developer();
developer.login();
developer.writeCode();
developer.logout();
}
The Developer object can call all accessible methods inherited from Employee.
8. Adding New Functionality in a Child Class
A child class does not have to use only inherited functionality. It can add additional properties and methods.
class Employee {
String name;
Employee(this.name);
void work() {
print('$name is working');
}
}
class Developer extends Employee {
String programmingLanguage;
Developer(
String name,
this.programmingLanguage,
) : super(name);
void writeCode() {
print('$name is writing $programmingLanguage code');
}
}
void main() {
Developer developer = Developer('Rahul', 'Dart');
developer.work();
developer.writeCode();
}
Here, Developer extends the functionality of Employee.
9. Parent and Child Constructors
Constructors are not inherited by subclasses. A child class defines its own constructors and can invoke a parent constructor using super.
class Person {
String name;
Person(this.name);
}
class Student extends Person {
int age;
Student(String name, this.age) : super(name);
}
void main() {
Student student = Student('Amit', 21);
print(student.name);
print(student.age);
}
The expression super(name) calls the constructor of the parent class.
10. Understanding the super Keyword
The super keyword refers to the superclass portion of the current object. It is commonly used to:
- Call a superclass constructor.
- Call a superclass method.
- Access an inherited member when needed.
Calling a Parent Constructor
class Person {
String name;
Person(this.name);
}
class Student extends Person {
Student(String name) : super(name);
}
Calling a Parent Method
class Employee {
void work() {
print('Employee is working');
}
}
class Developer extends Employee {
@override
void work() {
super.work();
print('Developer is writing code');
}
}
11. Method Overriding in Inheritance
A child class can provide its own implementation of an inherited method. This is called method overriding.
class Animal {
void sound() {
print('Animal makes a sound');
}
}
class Dog extends Animal {
@override
void sound() {
print('Dog barks');
}
}
void main() {
Dog dog = Dog();
dog.sound();
}
Output:
Dog barks
The @override annotation clearly indicates that Dog is providing its own implementation of the inherited sound() method.
12. Why Use @override?
The @override annotation communicates that a member is intended to override an inherited member and helps Dart's analyzer detect mistakes.
class Animal {
void sound() {
print('Animal sound');
}
}
class Cat extends Animal {
@override
void sound() {
print('Cat meows');
}
}
Using @override makes the code easier for other developers to understand and maintain.
13. Inheritance and Polymorphism
Inheritance and polymorphism are closely related. A variable of a parent type can refer to an object of a child type, and overridden behavior can be selected according to the actual object.
class Animal {
void sound() {
print('Animal sound');
}
}
class Dog extends Animal {
@override
void sound() {
print('Dog barks');
}
}
class Cat extends Animal {
@override
void sound() {
print('Cat meows');
}
}
void main() {
Animal animal1 = Dog();
Animal animal2 = Cat();
animal1.sound();
animal2.sound();
}
Output:
Dog barks
Cat meows
This is an example of runtime polymorphic behavior using an inheritance hierarchy.
14. Single Inheritance
In single inheritance, one child class directly extends one parent class.
class Animal {
void eat() {
print('Animal eats');
}
}
class Dog extends Animal {
void bark() {
print('Dog barks');
}
}
Structure:
Animal
|
v
Dog
15. Multilevel Inheritance
In multilevel inheritance, a class extends another class that itself extends another class.
class Animal {
void eat() {
print('Animal eats');
}
}
class Mammal extends Animal {
void walk() {
print('Mammal walks');
}
}
class Dog extends Mammal {
void bark() {
print('Dog barks');
}
}
void main() {
Dog dog = Dog();
dog.eat();
dog.walk();
dog.bark();
}
Structure:
Animal
|
v
Mammal
|
v
Dog
The Dog class can access inherited members from both Mammal and Animal, subject to Dart's access rules.
16. Hierarchical Inheritance
In hierarchical inheritance, multiple child classes extend the same parent class.
class Animal {
void eat() {
print('Animal eats');
}
}
class Dog extends Animal {
void bark() {
print('Dog barks');
}
}
class Cat extends Animal {
void meow() {
print('Cat meows');
}
}
class Cow extends Animal {
void moo() {
print('Cow moos');
}
}
Structure:
Animal
/ | \
/ | \
Dog Cat Cow
17. Does Dart Support Multiple Class Inheritance?
Dart does not allow a class to extend multiple classes directly.
For example, this is not valid:
// Not valid Dart:
class A {
}
class B {
}
// A class cannot extend both A and B:
// class C extends A, B {}
Dart provides other mechanisms, such as mixins and interfaces, for sharing behavior or contracts across multiple types.
18. Inheritance with Abstract Classes
An abstract class can define a common contract that subclasses implement.
abstract class Shape {
double calculateArea();
}
class Circle extends Shape {
double radius;
Circle(this.radius);
@override
double calculateArea() {
return 3.14 * radius * radius;
}
}
class Rectangle extends Shape {
double width;
double height;
Rectangle(this.width, this.height);
@override
double calculateArea() {
return width * height;
}
}
void main() {
Shape circle = Circle(5);
Shape rectangle = Rectangle(10, 5);
print(circle.calculateArea());
print(rectangle.calculateArea());
}
The abstract parent class defines the required behavior, while the subclasses provide the implementation.
19. Practical Example: Person and Student
class Person {
String name;
int age;
Person(this.name, this.age);
void displayPerson() {
print('Name: $name');
print('Age: $age');
}
}
class Student extends Person {
String course;
Student(
String name,
int age,
this.course,
) : super(name, age);
void study() {
print('$name is studying $course');
}
}
void main() {
Student student = Student(
'Rahul',
21,
'Flutter',
);
student.displayPerson();
student.study();
}
The Student class reuses common person information while adding student-specific behavior.
20. Practical Example: Employee and Developer
class Employee {
String name;
double salary;
Employee(this.name, this.salary);
void work() {
print('$name is working');
}
}
class Developer extends Employee {
String language;
Developer(
String name,
double salary,
this.language,
) : super(name, salary);
void writeCode() {
print('$name is writing $language code');
}
}
void main() {
Developer developer = Developer(
'Amit',
60000,
'Dart',
);
developer.work();
developer.writeCode();
}
21. Practical Example: Vehicle and Car
class Vehicle {
String brand;
Vehicle(this.brand);
void start() {
print('$brand vehicle started');
}
}
class Car extends Vehicle {
int doors;
Car(
String brand,
this.doors,
) : super(brand);
void showDoors() {
print('Number of doors: $doors');
}
}
void main() {
Car car = Car('Toyota', 4);
car.start();
car.showDoors();
}
22. Practical Example: Bank Account
class BankAccount {
String accountHolder;
double balance;
BankAccount(this.accountHolder, this.balance);
void deposit(double amount) {
balance += amount;
print('Deposited: $amount');
}
void displayBalance() {
print('Balance: $balance');
}
}
class SavingsAccount extends BankAccount {
double interestRate;
SavingsAccount(
String accountHolder,
double balance,
this.interestRate,
) : super(accountHolder, balance);
void calculateInterest() {
double interest = balance * interestRate / 100;
print('Interest: $interest');
}
}
void main() {
SavingsAccount account = SavingsAccount(
'Rahul',
50000,
5,
);
account.deposit(5000);
account.calculateInterest();
account.displayBalance();
}
The child class reuses banking functionality while adding savings-account-specific behavior.
23. Practical Example: E-Commerce Product
class Product {
String name;
double price;
Product(this.name, this.price);
void displayProduct() {
print('Product: $name');
print('Price: $price');
}
}
class ElectronicsProduct extends Product {
int warrantyYears;
ElectronicsProduct(
String name,
double price,
this.warrantyYears,
) : super(name, price);
void displayWarranty() {
print('Warranty: $warrantyYears years');
}
}
void main() {
ElectronicsProduct product = ElectronicsProduct(
'Laptop',
55000,
2,
);
product.displayProduct();
product.displayWarranty();
}
24. Practical Example: User and Admin
class User {
String name;
User(this.name);
void login() {
print('$name logged in');
}
}
class Admin extends User {
Admin(String name) : super(name);
void manageUsers() {
print('$name can manage users');
}
}
void main() {
Admin admin = Admin('Admin');
admin.login();
admin.manageUsers();
}
25. Inheritance in Flutter
Flutter development makes extensive use of class-based programming. For example, developers commonly create custom widgets by extending Flutter widget classes.
import 'package:flutter/material.dart';
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('Welcome to Flutter'),
),
);
}
}
In this example:
WelcomeScreen is a custom class.
- It extends
StatelessWidget.
- The
build() method is overridden.
- The constructor uses
super.key to pass the key to the superclass constructor.
Inheritance and overriding are therefore practical concepts in normal Flutter widget development.
26. Inheritance vs Composition
Inheritance is not always the best way to reuse functionality. Another important design technique is composition.
Inheritance
class Animal {
void eat() {
print('Eating');
}
}
class Dog extends Animal {
}
This represents an "is-a" relationship: a Dog is an Animal.
Composition
class Engine {
void start() {
print('Engine started');
}
}
class Car {
Engine engine = Engine();
void startCar() {
engine.start();
print('Car started');
}
}
This represents a "has-a" relationship: a Car has an Engine.
Choose inheritance when there is a genuine subtype relationship. Use composition when an object is better modeled as being composed of other objects.
27. Advantages of Inheritance
- Code Reusability: Common code can be placed in a parent class.
- Less Duplication: Child classes can reuse inherited functionality.
- Extensibility: Child classes can add specialized functionality.
- Polymorphism: Parent types can represent different child objects.
- Maintainability: Shared behavior can be maintained in one location.
- Organization: Related classes can be structured into meaningful hierarchies.
28. Disadvantages and Limitations of Inheritance
- Deep inheritance hierarchies can become difficult to understand.
- Changes to a parent class can affect multiple subclasses.
- Inheritance creates a relatively strong relationship between parent and child classes.
- It may be inappropriate when there is no genuine subtype relationship.
- Composition can sometimes provide a more flexible design.
29. Important Rules of Inheritance in Dart
- A class can directly extend one superclass.
- The
extends keyword is used for class inheritance.
- Constructors are not inherited.
- A child constructor can invoke a parent constructor using
super.
- Child classes can add new properties and methods.
- Child classes can override inherited methods.
- The
@override annotation should be used to clearly mark overrides.
- Abstract classes can define contracts for subclasses.
- Dart uses mixins and interfaces for other forms of code reuse and abstraction rather than multiple class inheritance.
30. Common Mistakes in Inheritance
Mistake 1: Using Inheritance Without an "Is-A" Relationship
Inheritance should represent a meaningful subtype relationship.
For example, Dog extends Animal is a natural relationship, while unrelated classes should generally not be connected merely to reuse a few methods.
Mistake 2: Forgetting super in Constructors
class Person {
String name;
Person(this.name);
}
class Student extends Person {
int age;
Student(String name, this.age) : super(name);
}
The super(name) call initializes the inherited name field through the parent constructor.
Mistake 3: Not Using @override
When redefining an inherited method, explicitly marking the method with @override improves clarity and analyzer support.
Mistake 4: Creating Very Deep Hierarchies
Prefer simple and understandable class relationships. If a hierarchy becomes unnecessarily deep, consider whether composition, interfaces, or mixins would be more appropriate.
31. Best Practices for Inheritance
- Use inheritance when there is a clear subtype relationship.
- Keep parent classes focused on common behavior.
- Keep child classes focused on specialized behavior.
- Use
@override for overridden methods.
- Use
super when parent initialization or behavior is required.
- Avoid unnecessary inheritance.
- Avoid excessively deep inheritance hierarchies.
- Prefer composition when a "has-a" relationship is more appropriate.
- Use abstract classes when subclasses must follow a common contract.
- Use polymorphism to make code work with abstractions rather than concrete implementations.
32. Inheritance and Access to Private Members
Dart's privacy mechanism is based on library boundaries rather than class-based private/protected keywords. Identifiers beginning with an underscore are private to the library where they are declared.
class Parent {
String _secret = 'Private library member';
void showSecret() {
print(_secret);
}
}
class Child extends Parent {
void display() {
showSecret();
}
}
The child can use inherited public API such as showSecret(). The exact visibility of underscore-prefixed members depends on whether the classes are in the same Dart library.
33. Complete Inheritance Example
class Employee {
String name;
double salary;
Employee(this.name, this.salary);
void work() {
print('$name is working');
}
void displayDetails() {
print('Name: $name');
print('Salary: $salary');
}
}
class Developer extends Employee {
String language;
Developer(
String name,
double salary,
this.language,
) : super(name, salary);
@override
void work() {
print('$name is developing using $language');
}
void writeCode() {
print('$name is writing code');
}
}
class Designer extends Employee {
String designTool;
Designer(
String name,
double salary,
this.designTool,
) : super(name, salary);
@override
void work() {
print('$name is designing using $designTool');
}
}
void main() {
Developer developer = Developer(
'Rahul',
60000,
'Dart',
);
Designer designer = Designer(
'Priya',
55000,
'Figma',
);
developer.displayDetails();
developer.work();
developer.writeCode();
designer.displayDetails();
designer.work();
}
This example combines several important concepts:
- Inheritance
- Constructors
super
- Method overriding
- Specialized child-class functionality
- Code reuse
34. Inheritance vs Polymorphism
| Inheritance |
Polymorphism |
| Creates a parent-child relationship between classes. |
Allows common types to represent different implementations. |
Uses extends. |
Often works through inheritance, interfaces, overriding, or other common abstractions. |
| Focuses on reuse and specialization. |
Focuses on interchangeable behavior. |
Example: Dog extends Animal |
Example: Animal animal = Dog() |
35. Inheritance Revision Table
| Concept |
Purpose |
Example |
| Parent Class |
Provides common members. |
class Animal |
| Child Class |
Inherits and extends parent behavior. |
class Dog extends Animal |
| extends |
Creates class inheritance. |
class Dog extends Animal |
| super |
Accesses superclass constructors or members. |
super(name) |
| @override |
Marks an inherited member that is being reimplemented. |
@override |
| Single Inheritance |
One child directly extends one parent. |
Animal → Dog |
| Multilevel Inheritance |
Inheritance across multiple levels. |
Animal → Mammal → Dog |
| Hierarchical Inheritance |
Multiple children extend one parent. |
Animal → Dog, Cat, Cow |
36. Practice Exercises
- Create an
Animal parent class and a Dog child class.
- Add common properties such as
name and age to the parent class.
- Create a method called
sound() in the parent class.
- Override
sound() in the child class.
- Create a
Vehicle class and extend it using Car and Bike.
- Create a multilevel inheritance example using
Person, Employee, and Developer.
- Create an e-commerce system using
Product and ElectronicsProduct.
- Create a
BankAccount parent class and a SavingsAccount child class.
- Create an abstract
Shape class and implement it using Circle and Rectangle.
- Create a Flutter widget by extending
StatelessWidget and overriding build().
37. Key Takeaways
- Inheritance allows one class to reuse and extend another class.
- The parent class is also called the superclass.
- The child class is also called the subclass.
- The
extends keyword is used for class inheritance in Dart.
- Constructors are not inherited.
- The
super keyword can call a parent constructor or member.
- Child classes can add their own properties and methods.
- Child classes can override inherited methods.
- The
@override annotation makes overriding explicit and helps tooling detect mistakes.
- Inheritance can support polymorphism.
- Dart does not support extending multiple classes directly.
- Mixins, interfaces, and composition provide other ways to share behavior or abstractions.
- Inheritance should be used when there is a meaningful subtype relationship.
- Inheritance is an important OOP concept for understanding Dart and Flutter code.
38. JustAcademy Flutter Learning Resources
JustAcademy's Flutter course curriculum explicitly places inheritance under Dart Programming Fundamentals alongside OOP, classes, objects, constructors, polymorphism, and abstraction. :contentReference[oaicite:1]{index=1}
Flutter Training Course:
https://www.justacademy.co/course-detail/flutter-training
Register for Course Demo:
https://www.justacademy.co/register-for-course-demo