Table of Contents
Entities
Entities are business object of your application. Entities are extended with equatable package which override the ==
operator as well as hashCode
so that we don’t have to waste our time writing lots of boilerplate code.
import 'package:equatable/equatable.dart';
class Todo extends Equatable {
final int userId;
final int id;
final String title;
final bool completed;
const Todo({
required this.userId,
required this.id,
required this.title,
required this.completed,
});
@override
List
Here Todo class is extended with Equatable. Equality and hash code are based on fields provided in props
property.
You can also use freezed package for your Entity.
This will create a file named todo.freezed.dart
with all the required methods for your Todo
class, including copyWith
, hashCode
, ==
, and more.
Your Todo
class will now leverage the features provided by freezed
, making it easier to manage immutable data classes in your Flutter app.
dependencies:
freezed_annotation: ^2.5.7
dev_dependencies:
build_runner: ^2.4.13
freezed: ^2.4.4
import 'package:freezed_annotation/freezed_annotation.dart';
part 'todo.freezed.dart';
@freezed
class Todo with _$Todo {
const factory Todo({
required int userId,
required int id,
required String title,
required bool completed,
}) = _Todo;
}
This will create a file named todo.freezed.dart
with all the required methods for your Todo
class, including copyWith
, hashCode
, ==
, and more.
Your Todo
class will now leverage the features provided by freezed
, making it easier to manage immutable data classes in your Flutter app.
Repositories Interface
Repositories in domain layer provides abstraction layer between domain layer and data layer. They have different functions that can be used to retrieve and manipulate data. The implementation of these functions are done in data layer.
import 'package:flutter_clean_architecture/feature/todo/domain/entity/todo.dart';
abstract class TodoRepository {
Future> getTodoList();
}
Here we create abstract class TodoRepository
where we provide all necessary functions.
Use Cases
Use Cases are actions that takes certain input and gives us output based on the input. Each use case represents a single action or functionality within the application. Use case should be free of any external dependencies.
import 'package:flutter_clean_architecture/feature/todo/domain/entity/todo.dart';
import 'package:flutter_clean_architecture/feature/todo/domain/repository/todo_repository.dart';
import 'package:injectable/injectable.dart';
@LazySingleton()
class GetTodoListUsecase {
final TodoRepository _repository;
GetTodoListUsecase(this._repository);
Future> call() => _repository.getTodoList();
}
Here we import TodoRepository
and use it’s object to return value to call method of this use case class. The call()
method allows an instance of any class that defines it to emulate a function.
Real project implementation
You can find all of the implementations in the following GitHub link: Github Repository
We are going to create a simple app that fetches list of todos with api provided by https://jsonplaceholder.typicode.com which will show us the use of remote data source. We will also change theme of the app between light and dark theme which will show us the use of local data source. Here we are going to have two features todo
and setting
. So let us create respective folders in lib/feature
folder as.
- lib/feature/todo/
- lib/feature/setting/
Step 1: Creating required entities
We will create todo.dart
file in lib/feature/todo/domain/entity
directory.
import 'package:equatable/equatable.dart';
class Todo extends Equatable {
final int userId;
final int id;
final String title;
final bool completed;
const Todo({
required this.userId,
required this.id,
required this.title,
required this.completed,
});
@override
List
For setting feature we are going to only store bool value so we will not make any entity.
Step 2: Creating required repositories and functions
We will create todo_repository.dart
file in lib/feature/todo/domain/repository
directory.
import 'package:flutter_clean_architecture/feature/todo/domain/entity/todo.dart';
abstract class TodoRepository {
Future> getTodoList();
}
Here we use getTodayList()
function to fetch list of todos.
Then we create setting_repository.dart
in lib/feature/setting/domain/repository
directory.
abstract class SettingRepository {
bool getDarkMode();
Future setDarkMode(bool darkMode);
}
Here we use getDarkMode()
to get dark mode status and setDarkMode(bool darkMode)
to set dark mode on or off.
Step3: Creating required use cases
Each use case represents a single action or functionality.
Create get_todo_list_usecase.dart
file in lib/feature/todo/domain/usecase
directory.
import 'package:flutter_clean_architecture/feature/todo/domain/entity/todo.dart';
import 'package:flutter_clean_architecture/feature/todo/domain/repository/todo_repository.dart';
import 'package:injectable/injectable.dart';
@LazySingleton()
class GetTodoListUsecase {
final TodoRepository _repository;
GetTodoListUsecase(this._repository);
Future> call() => _repository.getTodoList();
}
This use case will be used to get list of todos.
Then we will create get_dark_mode_usecase.dart
file in lib/feature/setting/domain/usecase
directory.
import 'package:flutter_clean_architecture/feature/setting/domain/repository/setting_respository.dart';
import 'package:injectable/injectable.dart';
@LazySingleton()
class GetDarkModeUsecase {
final SettingRepository _respository;
const GetDarkModeUsecase(this._respository);
bool call() => _respository.getDarkMode();
}
This use case will be used to get dark mode status for the app.
Then we will create set_dark_mode_usecase.dart
file in lib/feature/setting/domain/usecase
directory.
import 'package:flutter_clean_architecture/feature/setting/domain/repository/setting_respository.dart';
import 'package:injectable/injectable.dart';
@LazySingleton()
class SetDarkModeUsecase {
final SettingRepository _respository;
const SetDarkModeUsecase(this._respository);
Future call(bool darkMode) async => _respository.setDarkMode(darkMode);
}
This use case will be used to set dark mode on or off for the app.
This completes our domain layer. In upcoming chapter we will deep dive into other specific implementation.
You can find all of the implementations in following GitHub link: Github Repository
Read more in upcoming chapters to learn more about all of the layers of Clean Architecture.