Abstract Class vs Concrete Class and Abstract Method vs Concrete Method
Answer
Overview
In Dart, classes and methods can be abstract or concrete. Understanding the difference is fundamental to writing clean, extensible object-oriented code in Flutter.
- Abstract Class — a blueprint that cannot be instantiated directly
- Concrete Class — a fully implemented class that can be instantiated
- Abstract Method — a method with no body that subclasses must implement
- Concrete Method — a method with a full implementation
Abstract Class
An abstract class is declared with the
abstractnew AbstractClass()dartabstract class Animal { String name; Animal(this.name); // Abstract method — no body, subclass must implement void makeSound(); // Concrete method — has a body, inherited as-is void breathe() { print('$name is breathing'); } } // ❌ Cannot instantiate an abstract class // final a = Animal('Lion'); // Compile-time error
Key characteristics:
- Declared with keywordtext
abstract - Cannot be instantiated directly
- Can have both abstract and concrete methods
- Can have constructors, fields, and getters
- Subclasses use to inherittext
extends
Concrete Class
A concrete class is a regular, fully implemented class. It can be instantiated directly. If it extends an abstract class, it must implement all abstract methods.
dart// Concrete class — extends abstract Animal class Dog extends Animal { Dog(String name) : super(name); // Must implement the abstract method void makeSound() { print('$name says: Woof!'); } } class Cat extends Animal { Cat(String name) : super(name); void makeSound() { print('$name says: Meow!'); } } void main() { final dog = Dog('Buddy'); dog.makeSound(); // Buddy says: Woof! dog.breathe(); // Buddy is breathing (inherited concrete method) final cat = Cat('Whiskers'); cat.makeSound(); // Whiskers says: Meow! }
Key characteristics:
- No keywordtext
abstract - Can be instantiated with / directlytext
new - Must override all abstract methods if extending an abstract class
- Can override concrete methods optionally
Abstract Method
An abstract method has no body — just a signature. It declares what must be done, not how.
dartabstract class Shape { // Abstract methods — no body, no implementation double area(); double perimeter(); // Concrete method — has implementation void describe() { print('Area: ${area()}, Perimeter: ${perimeter()}'); } }
Rules for abstract methods:
- Must be inside an text
abstract class - No curly braces and no bodytext
{} - Subclasses using must override themtext
extends - Classes using must also provide implementationstext
implements
dartclass Circle extends Shape { final double radius; Circle(this.radius); double area() => 3.14159 * radius * radius; double perimeter() => 2 * 3.14159 * radius; } void main() { final c = Circle(5); c.describe(); // Area: 78.53975, Perimeter: 31.4159 }
Concrete Method
A concrete method has a full implementation — a body with logic. It can live in any class (abstract or concrete) and can be used or overridden by subclasses.
dartabstract class Logger { // Concrete method in an abstract class void log(String message) { final timestamp = DateTime.now().toIso8601String(); print('[$timestamp] $message'); } // Abstract method — each subclass defines its own behavior void handleError(String error); } class FileLogger extends Logger { void handleError(String error) { // Subclass-specific implementation log('ERROR: $error'); // Reuses inherited concrete method print('Writing error to file...'); } } class ConsoleLogger extends Logger { void handleError(String error) { log('CONSOLE ERROR: $error'); } }
Key characteristics:
- Has a body with implementationtext
{ ... } - Can live in abstract or concrete classes
- Subclasses may optionally ittext
@override - Inherited automatically via text
extends
Comparison Tables
Abstract Class vs Concrete Class
| Feature | Abstract Class | Concrete Class |
|---|---|---|
| Keyword | text | text |
| Instantiation | ❌ Cannot instantiate | ✅ Can instantiate |
| Abstract methods | ✅ Allowed | ❌ Not allowed |
| Concrete methods | ✅ Allowed | ✅ Allowed |
| Constructor | ✅ Can have (used by subclasses) | ✅ Yes |
| Purpose | Blueprint / contract | Ready-to-use implementation |
| Usage | text text | Direct instantiation |
Abstract Method vs Concrete Method
| Feature | Abstract Method | Concrete Method |
|---|---|---|
| Body | ❌ No body | ✅ Has body text |
| Where defined | Only in text | Any class |
| Must override? | ✅ Yes (via text | ❌ Optional |
| Purpose | Forces subclass behavior | Provides default behavior |
| Keyword | None (just no body) | None |
Real-World Flutter Example
dart// Abstract class — defines the payment contract abstract class PaymentService { final String currency; PaymentService(this.currency); // Abstract methods — each gateway implements differently Future<bool> charge(double amount); Future<bool> refund(String transactionId); // Concrete method — shared logging logic void logTransaction(String type, double amount) { print('[$type] $currency ${amount.toStringAsFixed(2)}'); } } // Concrete class — Stripe implementation class StripeService extends PaymentService { final String apiKey; StripeService({required this.apiKey}) : super('USD'); Future<bool> charge(double amount) async { logTransaction('CHARGE', amount); // Uses inherited concrete method // Stripe-specific charge logic return true; } Future<bool> refund(String transactionId) async { logTransaction('REFUND', 0); // Stripe-specific refund logic return true; } } // Concrete class — Razorpay implementation class RazorpayService extends PaymentService { RazorpayService() : super('INR'); Future<bool> charge(double amount) async { logTransaction('CHARGE', amount); // Razorpay-specific charge logic return true; } Future<bool> refund(String transactionId) async { logTransaction('REFUND', 0); return true; } } void main() async { // Polymorphism — use any concrete class via the abstract type PaymentService service = StripeService(apiKey: 'sk_test_xxx'); await service.charge(99.99); // [CHARGE] USD 99.99 service = RazorpayService(); await service.charge(1999); // [CHARGE] INR 1999.00 }
When to Use
Use an abstract class when:
- You want to share common logic (concrete methods) across subclasses
- You need a base contract with some default implementation
- Classes are closely related (e.g., )text
Animal → Dog, Cat
Use a concrete class when:
- The class is a complete, standalone implementation
- No subclassing is needed
- You need to create instances directly
Use abstract methods when:
- Every subclass must provide its own behavior
- There is no sensible default implementation
- You want to enforce a contract at compile time
Use concrete methods when:
- The default behavior is shared and reusable
- You want to provide fallback logic
- Only some subclasses need to override
Key Rule: An abstract class can have both abstract and concrete methods. A concrete class cannot have abstract methods — every method must have a body.