How to Implement Clean Architecture in Flutter (Beginner Step-by-Step Guide)

April 20, 2026 | Al Saeed
Flutter Clean Architecture showing Domain, Data, and Presentation layers for scalable apps

This image is generated by AI

If you want to write clean, scalable, and professional Flutter code, understanding Clean Architecture is essential.In this guide, we will focus only on the core concept of Clean Architecture:

  • ❌ No State Management
  • ❌ No Dependency Injection
  • βœ… Only core architecture (Domain + Data + Presentation)

 

🧠 What is Clean Architecture?

Clean Architecture is a design approach where an application is divided into layers to achieve:

  • Well-organized code
  • Easier maintenance and updates
  • Better testability
  • Scalability for large applications

 

πŸ”‘ The Golden Rule

  • Dependencies always point inward
  • Inner layers are independent and do not know about outer layers

Presentation β†’ Domain β†’ Data

 

🧱 The 3 Core Layers

 

1. Domain Layer (Core Logic)

  • The brain of the application
  • Contains business rules
  • Written in pure Dart (no Flutter, no APIs)

2. Data Layer (Implementation)

  • Handles API calls or local database
  • Implements contracts defined in the Domain layer

3. Presentation Layer (UI)

  • Displays UI
  • Handles user interactions

πŸ“ Recommended Folder Structure

lib/
β”œβ”€β”€ features/
β”‚ └── user/
β”‚ β”œβ”€β”€ domain/
β”‚ β”œβ”€β”€ data/
β”‚ └── presentation/

 

πŸš€ Step-by-Step Implementation (API Example)

Let’s walk through a simple example:

πŸ‘‰ Fetch User from API

 

🧠 Step 1: Domain Layer (Start Here)

πŸ“ lib/features/user/domain/

 

βœ… 1.1 Entity (First File)

πŸ“ entities/user.dart

class User {
  final int id;
  final String name;

  User({required this.id, required this.name});
}

βœ” Represents core data
βœ” Has no dependency on external layers

 

βœ… 1.2 Repository (Abstract)

πŸ“ repositories/user_repository.dart

abstract class UserRepository {
  Future<User> getUser();
}

βœ” Defines what data is needed
βœ” Does not include implementation

 

βœ… 1.3 Use Case

πŸ“ usecases/get_user.dart

class GetUser {
  final UserRepository repository;

  GetUser(this.repository);

  Future<User> call() {
    return repository.getUser();
  }
}

βœ” Represents a business action
βœ” This is what the UI will call

 

πŸ’Ύ Step 2: Data Layer

πŸ“ lib/features/user/data/

 

βœ… 2.1 Model

πŸ“ models/user_model.dart

import '../../domain/entities/user.dart';

class UserModel extends User {
  UserModel({required int id, required String name})
      : super(id: id, name: name);

  factory UserModel.fromJson(Map<String, dynamic> json) {
    return UserModel(
      id: json['id'],
      name: json['name'],
    );
  }
}

βœ” Maps API response to your app structure

 

βœ… 2.2 Data Source (API Call)

πŸ“ datasources/user_remote_data_source.dart


class UserRemoteDataSource {
  Future<UserModel> getUser() async {
    // Fake API call
    await Future.delayed(Duration(seconds: 1));

    return UserModel(id: 1, name: "Ali");
  }
}

βœ” Contains actual API logic

 

βœ… 2.3 Repository Implementation

πŸ“ repositories/user_repository_impl.dart


class UserRepositoryImpl implements UserRepository {
  final UserRemoteDataSource remoteDataSource;

  UserRepositoryImpl(this.remoteDataSource);

  @override
  Future<User> getUser() async {
    return await remoteDataSource.getUser();
  }
}

βœ” Implements the Domain contract

 

🎨 Step 3: Presentation Layer (Simple UI)

πŸ“ lib/features/user/presentation/

 

βœ… Simple UI Page (Without State Management)

πŸ“ pages/user_page.dart


class UserPage extends StatefulWidget {
  @override
  _UserPageState createState() => _UserPageState();
}

class _UserPageState extends State<UserPage> {
  String text = "Press Button";

  void fetchUser() async {
    final dataSource = UserRemoteDataSource();
    final repository = UserRepositoryImpl(dataSource);
    final usecase = GetUser(repository);

    final user = await usecase();

    setState(() {
      text = user.name;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: fetchUser,
          child: Text(text),
        ),
      ),
    );
  }
}

βœ” Direct and simple connection
βœ” Easy for beginners (no Bloc, no Provider)

 

πŸ”„ Data Flow Explained

UI β†’ UseCase β†’ Repository β†’ DataSource β†’ API
API β†’ DataSource β†’ Repository β†’ UseCase β†’ UI

 

⚑ Recommended Development Order

  1. Entity
  2. Repository (abstract)
  3. Use Case
  4. Model
  5. Data Source
  6. Repository Implementation
  7. UI

 

❌ Common Mistakes to Avoid

  • Calling APIs directly from UI ❌
  • Mixing Models and Entities ❌
  • Importing Flutter into Domain layer ❌

 

🎯 Final Summary

  • Domain β†’ Defines business logic
  • Data β†’ Implements logic
  • Presentation β†’ Displays results

πŸ‘‰ β€œAlways start with the Domain β€” never with the UI.”

 

πŸš€ What’s Next?

  • State Management (Bloc, Riverpod)
  • Dependency Injection (e.g., get_it)

Clean Architecture is the foundation everything else builds on.


πŸ“š Explore More Blogs

If you found this guide helpful, explore more mobile app development articles on our blog.