Object-Oriented Programming (OOP) Concepts in Dart | Flutter
1. Class and Object — Blueprint for the Future:
In OOP, a class serves as a blueprint or template to create objects. It defines the properties (attributes) and methods (behavior) that the objects of the class will possess. An object, on the other hand, is an instance of a class. In Dart, classes are defined using the class
keyword.
class Person {
String name;
int age;
void sayHello() {
print("Hello, I'm $name!");
}
}
void main() {
var person1 = Person();
person1.name = "John";
person1.age = 30;
person1.sayHello(); // Output: "Hello, I'm John!"
}
— — —
2. Encapsulation — Securing the Treasure:
Encapsulation is the concept of bundling data (attributes) and methods (behavior) together within a class, hiding the internal implementation details from the outside world. It promotes data security and reduces code complexity. In Dart, you can use access modifiers like public
, private
, and protected
to control the visibility of class members.
class BankAccount {
double _balance; // Private attribute
double get balance => _balance; // Getter for balance
void deposit(double amount) {
_balance += amount;
}
void withdraw(double amount) {
if (_balance >= amount) {
_balance -= amount;
} else {
print("Insufficient funds!");
}
}
}
void main() {
var account = BankAccount();
account.deposit(1000);
print("Balance: \$${account.balance}"); // Output: "Balance: $1000.0"
account.withdraw(500);
print("Balance: \$${account.balance}"); // Output: "Balance: $500.0"
}
— — —
3. Inheritance — Reusing the Power:
Inheritance is a mechanism where a class (subclass or derived class) inherits properties and methods from another class (superclass or base class). It promotes code reusability and hierarchical organization. In Dart, you can use the extends
keyword to create a subclass.
class Animal {
String name;
void speak() {
print("Animal makes a sound");
}
}
class Dog extends Animal {
@override
void speak() {
print("Dog barks");
}
}
void main() {
var dog = Dog();
dog.name = "Buddy";
dog.speak(); // Output: "Dog barks"
}
— — —
4. Multiple Inheritance:
Multiple inheritance refers to a situation where a class can inherit properties and behavior from more than one superclass. In some programming languages, like C++, multiple inheritance is supported directly, but in Dart, it is achieved through the use of mixins.
class A {
void methodA() {
print("Method from A");
}
}
class B {
void methodB() {
print("Method from B");
}
}
class C with A, B {
void methodC() {
print("Method from C");
}
}
void main() {
var c = C();
c.methodA(); // Output: "Method from A"
c.methodB(); // Output: "Method from B"
c.methodC(); // Output: "Method from C"
}
In the example above, class C
uses the with
keyword to mixin both classes A
and B
, allowing it to inherit the methods from both superclasses.
— — —
5. Multilevel Inheritance:
Multilevel inheritance refers to a situation where a class can inherit from another class, which in turn inherits from yet another class. It creates a chain of inheritance, and the derived class has access to properties and methods from all its ancestors.
class A {
void methodA() {
print("Method from A");
}
}
class B extends A {
void methodB() {
print("Method from B");
}
}
class C extends B {
void methodC() {
print("Method from C");
}
}
void main() {
var c = C();
c.methodA(); // Output: "Method from A"
c.methodB(); // Output: "Method from B"
c.methodC(); // Output: "Method from C"
}
In this example, class C
extends class B
, which, in turn, extends class A
, creating a multilevel inheritance chain.
— — —
6. Polymorphism — The Many Faces of OOP
Polymorphism allows objects of different classes to be treated as objects of a common superclass. It enables dynamic method dispatch and is achieved through method overriding and method overloading. In Dart, method overriding is achieved using the @override
annotation.
class Shape {
void draw() {
print("Drawing a shape");
}
}
class Circle extends Shape {
@override
void draw() {
print("Drawing a circle");
}
}
class Square extends Shape {
@override
void draw() {
print("Drawing a square");
}
}
void main() {
Shape circle = Circle();
Shape square = Square();
circle.draw(); // Output: "Drawing a circle"
square.draw(); // Output: "Drawing a square"
}
=> Method Overriding and Super Keyword — Fine-Tuning Behavior:
Method overriding is a powerful feature in OOP that allows a subclass to provide a specific implementation for a method defined in its superclass. It enables customization of behavior based on the subclass’s requirements. The super
keyword plays a crucial role in method overriding, allowing access to the superclass's methods and properties.
class Animal {
void makeSound() {
print("Animal makes a sound");
}
}
class Dog extends Animal {
@override
void makeSound() {
print("Dog barks");
super.makeSound(); // Call to superclass method
}
}
void main() {
var dog = Dog();
dog.makeSound();
// Output:
// "Dog barks"
// "Animal makes a sound"
}
In this example, we have a superclass Animal
with a method makeSound()
. The subclass Dog
extends Animal
and overrides the makeSound()
method using the @override
annotation. When we create an object of the Dog
class and call makeSound()
, the overridden method in Dog
is executed, printing "Dog barks."
— —
=> Method Overloading:
Method overloading is a feature in some programming languages that allows a class to have multiple methods with the same name but different parameter lists. The methods can perform similar operations but with different inputs, making the code more flexible and readable.
Dart does not support traditional method overloading, as it uses optional named parameters and default values to achieve similar functionality. Let’s see an example:
class MathOperations {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}
void main() {
var math = MathOperations();
int result1 = math.add(5, 10);
double result2 = math.add(3.14, 2.71);
print("Result 1: $result1"); // Output: "Result 1: 15"
print("Result 2: $result2"); // Output: "Result 2: 5.85"
}
In this example, the MathOperations
class has two methods named add
. One takes two int
parameters and returns an int
, while the other takes two double
parameters and returns a double
. Although Dart doesn't have traditional method overloading, we achieve a similar effect by defining methods with different parameter types.
— — —
7. Abstract Classes:
An abstract class in Dart is a class that cannot be instantiated directly. It serves as a blueprint for other classes, providing a common set of methods (functionality) that must be implemented in the subclasses. Abstract classes are used to define a common structure for related classes, ensuring that all subclasses have certain methods.
In the context of coding, an abstract class is created using the abstract
keyword, and it may or may not have abstract methods (methods without implementation). Subclasses of an abstract class must override the abstract methods to provide their own implementation.
// Abstract class definition
abstract class Shape {
// Abstract method without implementation
void draw();
// Non-abstract method
void calculateArea() {
print("Calculating area of the shape");
}
}
// Subclass Circle that extends Shape
class Circle extends Shape {
@override
void draw() {
print("Drawing a circle");
}
}
// Subclass Square that extends Shape
class Square extends Shape {
@override
void draw() {
print("Drawing a square");
}
}
void main() {
var circle = Circle();
var square = Square();
circle.draw(); // Output: "Drawing a circle"
square.draw(); // Output: "Drawing a square"
circle.calculateArea(); // Output: "Calculating area of the shape"
square.calculateArea(); // Output: "Calculating area of the shape"
}
In this example, we define an abstract class Shape
with an abstract method draw()
and a non-abstract method calculateArea()
. Both Circle
and Square
classes extend the abstract class Shape
and provide their own implementation of the draw()
method. They also inherit the non-abstract method calculateArea()
from the abstract class.
— — —
8. Interfaces:
An interface in Dart represents a contract that classes must adhere to. It defines a set of method signatures (function names and their parameters) without providing the implementation. In other words, an interface specifies what methods a class must have but does not specify how those methods are implemented.
Unlike some other languages, Dart does not have a dedicated interface
keyword. Instead, any class can act as an interface, and other classes can implement it by providing the required methods.
// Interface for a Flyable object
class Flyable {
void fly() {
print("Flying...");
}
}
// Bird class implements the Flyable interface
class Bird implements Flyable {
@override
void fly() {
print("Bird is flying");
}
}
// Airplane class implements the Flyable interface
class Airplane implements Flyable {
@override
void fly() {
print("Airplane is flying");
}
}
void main() {
var bird = Bird();
var airplane = Airplane();
bird.fly(); // Output: "Bird is flying"
airplane.fly(); // Output: "Airplane is flying"
}
In this example, we define a Flyable
class with a method fly()
. Both Bird
and Airplane
classes implement the Flyable
interface by providing their own implementation of the fly()
method. The Flyable
class acts as an interface, defining the method signature that must be present in classes that implement it.
— — —
9. Constructors — Building Objects with Life
Constructors in Dart are special methods that are called when an object is created. They allow you to initialize object properties and perform setup operations. Dart provides default constructors and named constructors for greater flexibility.
class Person {
String name;
int age;
Person(this.name, this.age); // Default constructor
Person.fromBirthYear(int birthYear) {
name = "Unknown";
age = DateTime.now().year - birthYear;
} // Named constructor
void sayHello() {
print("Hello, I'm $name!");
}
}
void main() {
var person1 = Person("John", 30); // Using default constructor
var person2 = Person.fromBirthYear(1995); // Using named constructor
person1.sayHello(); // Output: "Hello, I'm John!"
person2.sayHello(); // Output: "Hello, I'm Unknown!"
}
— —
=> Static Members and Constructors — Class-Wide Behavior
Static members in Dart belong to the class rather than individual objects. They are shared across all instances of the class and can be accessed using the class name. Static constructors, also known as factory constructors, are special constructors that return an instance of the class based on specific conditions.
class MathUtils {
static const double pi = 3.14159;
static double multiply(double a, double b) {
return a * b;
}
factory MathUtils.fromRadius(double radius) {
return MathUtils.multiply(radius, radius) * pi;
}
}
void main() {
var area = MathUtils.fromRadius(5);
print("Area of circle with radius 5: $area"); // Output: "Area of circle with radius 5: 78.53975"
}
In this example, the MathUtils
class has a static constant pi
and a static method multiply()
. Additionally, it has a factory constructor fromRadius()
that calculates the area of a circle based on the radius using the static multiply()
method.
— —
=> Named Constructors and Factory Constructors — Customized Creation
In Dart, you can have multiple constructors for a class. Named constructors allow you to create specialized constructors with custom names. Factory constructors are used to return an instance of the class based on specific conditions.
class Person {
String name;
int age;
Person(this.name, this.age);
Person.fromBirthYear(int birthYear) {
name = "Unknown";
age = DateTime.now().year - birthYear;
}
factory Person.createGuest() {
return Person("Guest", 0);
}
}
void main() {
var person1 = Person("John", 30);
var person2 = Person.fromBirthYear(1995);
var guest = Person.createGuest();
print("Person 1: ${person1.name} (${person1.age})"); // Output: "Person 1: John (30)"
print("Person 2: ${person2.name} (${person2.age})"); // Output: "Person 2: Unknown (28)"
print("Guest: ${guest.name} (${guest.age})"); // Output: "Guest: Guest (0)"
}
In this example, the Person
class has multiple constructors. The default constructor Person(this.name, this.age)
initializes the name
and age
properties. The named constructor Person.fromBirthYear(int birthYear)
creates a Person
object with the given birth year. The factory constructor Person.createGuest()
returns a predefined Guest
person.
— — —
10. Final and Const — Immutable Variables
In Dart, you can make variables immutable using the final
and const
keywords. final
variables can be assigned a value only once, whereas const
variables are compile-time constants and must have a constant value at compile-time.
void main() {
final int age = 30;
final String name = "John";
// age = 31; // Error: The final variable 'age' can only be set once.
const double pi = 3.14159;
// const double radius = getRadius(); // Error: Constant variables must be initialized with a constant value at compile-time.
}
In the example, age
and name
are final
variables, and once assigned, their values cannot be changed. On the other hand, pi
is a const
variable, and it must be assigned a constant value at compile-time.
— — —
11. Mixins — Reusable Code Blocks
Mixins are a way to reuse a class’s code in multiple class hierarchies. They allow you to extend a class’s functionality without using inheritance. In Dart, you can create mixins using the mixin
keyword.
mixin Flying {
void fly() {
print("Flying...");
}
}
class Bird with Flying {
void chirp() {
print("Bird is chirping");
}
}
class Plane with Flying {
void takeOff() {
print("Plane is taking off");
}
}
void main() {
var bird = Bird();
var plane = Plane();
bird.fly(); // Output: "Flying..."
plane.fly(); // Output: "Flying..."
plane.takeOff(); // Output: "Plane is taking off"
}
In this example, we define a Flying
mixin with a fly()
method. Both the Bird
and Plane
classes use this mixin to gain the functionality of flying, demonstrating code reuse without inheritance.
— — —
Conclusion
All these topics are frequently asked in interviews as well. You can use this article to prepare for your next interview. I’ve explained almost all the topics along with coding examples.
If you have any questions or queries, feel free to ask me. Follow the provided links and visit my profile for more articles.
Linkedin: https://www.linkedin.com/in/ahsan-saeed-11a787183/
Follow for more Ahsi Dev 💙