Flutter Clean Architecture Layers
Structure Flutter applications with domain, data, and presentation layers for maintainability.
Overview
Clean Architecture in Flutter establishes clear boundaries between business logic, data handling, and UI code. This separation enables independent development, easier testing, and better maintainability as applications grow in complexity. The three-layer model—domain, data, and presentation—each serves a distinct purpose. The domain layer contains pure Dart code with no Flutter or external dependencies. Entities represent core business objects, use cases encapsulate single business operations, and repository interfaces define data access contracts. This layer is entirely testable without mocking Flutter components. Entities should be simple data classes, while use cases follow the single responsibility principle—one use case per business operation. The data layer implements repository interfaces defined in the domain layer. Data sources handle actual data retrieval from APIs, databases, or local storage. Repository implementations coordinate between multiple data sources if needed, transform data between entity and model representations, and handle caching strategies. Models extend entities with serialization capabilities for network or local storage. The presentation layer contains widgets, state management, and view models. It depends only on the domain layer, never directly accessing data sources. This dependency flow ensures that business rules remain consistent regardless of UI changes. State management solutions like Riverpod or Bloc sit here, connecting domain use cases to the UI while managing application state reactively.
Code Example
// Domain Layer - Entities
class Article {
final String id;
final String title;
final String content;
final String authorId;
final DateTime publishedAt;
final List<String> tags;
const Article({
required this.id,
required this.title,
required this.content,
required this.authorId,
required this.publishedAt,
required this.tags,
});
}
// Domain Layer - Repository Interface
abstract class ArticleRepository {
Future<List<Article>> getArticles();
Future<Article?> getArticleById(String id);
Future<void> createArticle(Article article);
Future<void> deleteArticle(String id);
}
// Domain Layer - Use Case
class GetArticlesUseCase {
final ArticleRepository repository;
GetArticlesUseCase(this.repository);
Future<List<Article>> call() async {
return repository.getArticles();
}
}
// Data Layer - Model
class ArticleModel extends Article {
const ArticleModel({
required super.id,
required super.title,
required super.content,
required super.authorId,
required super.publishedAt,
required super.tags,
});
factory ArticleModel.fromJson(Map<String, dynamic> json) {
return ArticleModel(
id: json['id'],
title: json['title'],
content: json['content'],
authorId: json['author_id'],
publishedAt: DateTime.parse(json['published_at']),
tags: List<String>.from(json['tags']),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'content': content,
'author_id': authorId,
'published_at': publishedAt.toIso8601String(),
'tags': tags,
};
}
}
// Data Layer - Repository Implementation
class ArticleRepositoryImpl implements ArticleRepository {
final ArticleRemoteDataSource remoteDataSource;
ArticleRepositoryImpl({required this.remoteDataSource});
@override
Future<List<Article>> getArticles() async {
final models = await remoteDataSource.fetchArticles();
return models;
}
}
// Presentation Layer - Riverpod Provider
final getArticlesUseCaseProvider = Provider<GetArticlesUseCase>((ref) {
return GetArticlesUseCase(ref.watch(articleRepositoryProvider));
});
final articlesProvider = FutureProvider<List<Article>>((ref) async {
final useCase = ref.watch(getArticlesUseCaseProvider);
return useCase();
});More Flutter Rules
Flutter State Management with Riverpod
Use Riverpod for reactive state management with compile-time safety and testable provider architecture.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../models/todo.dart';
im...Flutter BLoC Pattern Implementation
Implement BLoC for predictable state management with clear event-state separation.
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:equatable/equatable.dart...Flutter Navigation with GoRouter
Implement declarative routing with GoRouter for deep linking and nested navigation.
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '....