Flutter development often involves designing clean, maintainable, and reusable code. One of the key tools in achieving that is abstract classes. In this article, we’ll explore how abstract classes work in Dart using a brand-new example focused on different modes of transport, with added clarity and use-case driven explanations.
🔍 What Are Abstract Classes?
An abstract class is a class that cannot be instantiated on its own. It acts as a template for other classes, defining method signatures (but not implementations) that must be implemented by its subclasses.
📘 Why Abstract Classes Matter
- They enforce a consistent interface across multiple related classes.
- They help implement polymorphism, allowing interchangeable object behavior.
- They’re useful for building scalable and testable architecture.
🚗 Example: Transport Modes App
Let’s build an example that simulates different types of transport like Car, Bicycle, and Train. Each transport method will implement a shared interface defined in an abstract class.
Step 1: Define the Abstract Class
abstract class TransportMode {
void start();
void stop();
double estimatedTime(double distance); // in hours
}
This defines a contract for all transport modes — they must define start()
, stop()
, and a way to calculate time based on distance.
Step 2: Create Subclasses
class Car extends TransportMode {
@override
void start() => print("Car engine started");
@override
void stop() => print("Car parked");
@override
double estimatedTime(double distance) => distance / 60; // avg 60 km/h
}
class Bicycle extends TransportMode {
@override
void start() => print("Pedaling started");
@override
void stop() => print("Bike stopped");
@override
double estimatedTime(double distance) => distance / 15; // avg 15 km/h
}
class Train extends TransportMode {
@override
void start() => print("Train departed");
@override
void stop() => print("Train arrived at station");
@override
double estimatedTime(double distance) => distance / 100; // avg 100 km/h
}
Step 3: Use in a Flutter App
import 'package:flutter/material.dart';
class TransportApp extends StatelessWidget {
final List<TransportMode> transports = [Car(), Bicycle(), Train()];
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("Transport Modes")),
body: ListView.builder(
itemCount: transports.length,
itemBuilder: (context, index) {
final mode = transports[index];
final distance = 120.0; // km
return ListTile(
title: Text(mode.runtimeType.toString()),
subtitle: Text(
"Time for \$distance km: \${mode.estimatedTime(distance).toStringAsFixed(2)} hrs",
),
trailing: IconButton(
icon: Icon(Icons.directions),
onPressed: () {
mode.start();
Future.delayed(Duration(seconds: 1), () => mode.stop());
},
),
);
},
),
),
);
}
}
void main() => runApp(TransportApp());
💡 Real-World Use Cases
Abstract classes shine in larger projects:
- Creating base services or repositories for APIs
- Defining widgets contracts for custom UI components
- Structuring state management logic
For example:
abstract class AuthService {
Future<void> login(String email, String password);
Future<void> logout();
}
Then you can implement this with Firebase, Supabase, or mock versions for testing.
🧠 Summary
Abstract classes in Dart allow you to define clean, reusable interfaces and promote consistency across your Flutter app’s codebase. In this transport example, we saw how three different modes of transport could share the same interface and behave polymorphically.
Use abstract classes when you want to:
- Enforce method structure
- Create flexible yet structured class hierarchies
- Build architecture-ready code
Thanks for reading and happy Fluttering! Let me know if you’d like a diagram or banner to go with this post.