Flutter: take the first step

Bernardo Iribarne
Nerd For Tech
Published in
5 min readSep 11, 2023

--

To take the first step sometimes is hard, I would like to give you some guidelines and tips which help you.

I’m sure you know about Clean Architecture, but I don’t want to talk about it, there are several blogs and articles that explain in detail what it means.

I will show you how to apply Clean Architecture to build a Flutter project. Using this design principle you will have your code modular, scalable, testable, and my favorite concept: simple.

Layers

I divide my project in 3 layers:

App Layers
  1. Presentation where we are going to have all related to views. Flutter widgets will be here with all its related presentation logic, the navigation flow, etc.
  2. Domain where the Business logic will be implemented.
  3. Data where we concerned about data sources, here we will implement how to get our data, how to save it, how to interact with external data sources, APIs.

Project template

Ok, lets go and create a Flutter project, I will call it “template_flutter”:

flutter create template_flutter

Then we are going to create one folder for each layer: data, domain and presentation. Also, I always add 2 additional folders:

  1. utils: for utility purposes, I add format classes, styles classes and more.
  2. config: on this folder I add the classes to configure my app, like environment, dependencies
Project structure

Domain layer

Let start for the domain layer. Remember that this is the place where we are going to have our business entities and its business logic.

Suppose that we have our business entity called “Bull”. So, on this layer we are going to define the model, the repository and the service to manage that entity.

Domain layer folder

I always have a GenericModel and all my business entity classes extends from it.

import '/src/domain/generic_model.dart';

class Bull extends GenericModel{

String name="";
String rp="";
String breed="";
String lot="";

Bull(id,
{ this.name="",
this.rp="",
this.breed="",
this.lot=""}):super(id);

Bull.empty():super(-1);

}
import '/src/domain/bull/bull.dart';

/**
* Interface to manage a repository for Bull entity
*/
abstract class BullRepository {

/// Get all bulls.
Future<List<Bull>> getAll();

/// Add a new bull.
Future<Bull> add(Bull bull);

}
import '/src/domain/bull/bull_repository.dart';
import '/src/config/dependency_manager.dart';
import '/src/domain/bull/bull.dart';

/**
* Provides method to manage Bulls.
* Implements the business logic for Bull entities
*/
class BullService {

/// Bull repository.
final BullRepository bullRepository = DependencyManager().get<BullRepository>();

/// Return all bulls.
Future<List<Bull>> getBulls() async{
List<Bull> bulls = List.empty(growable: true);
await bullRepository.getAll().then((dtos){
bulls = dtos;
}).catchError((error, stackTrace){
_errorHandler( error, stackTrace );
});
return bulls;
}

/// Add a new bull.
Future<Bull> addBul(Bull bull) async{

//TODO core validation.

await bullRepository.add(bull).then((dto){
bull = dto;
}).catchError((error, stackTrace){
_errorHandler( error, stackTrace );
});
return bull;
}

void _errorHandler(error, stackTrace) {
//TODO error handler
}
}

Other thing that I use to have is a FacadeService, it is a class which implements the Facade pattern, where I define all the services methods and it become in the portal of our domain layer. The presentation layer talk to domain layer through this class.

import '/src/config/dependency_manager.dart';
import '/src/domain/bull/bull.dart';
import '../bull/bull_service.dart';

/**
* Facade where we get all our services methods
*/
class FacadeService {


final BullService bullService = BullService();

Future<List<Bull>> getBulls() async{
return bullService.getBulls();
}


Future<Bull> addBull(Bull bull) async{
return bullService.addBul(bull);
}

}
Domain & Presentation layers

Presentation layer

As you know, Flutter is a declarative framework, so the key to get your code cleaned is how you manage the states. There are a few approach you can use, I recomend the BLoC pattern. I will talk about it in a separate article, but at least you have to know that it resolves the presentation logic, it manages the user interactions and its related events, a Bloc is between the views and the domain.

BLoC pattern

The presentation folder will look like this:

Presentation layer folder

For each business entity I create a folder, in this example you can see “bull” folder and it contains:

  1. bloc: where we have all our bloc pattern classes.
  2. widget: specific widgets for bull entity.
  3. On the root folder are the pages for bull.

Talk about the others folder:

  1. generic/bloc: I have generic classes for BLOC pattern
  2. nav: classes to manage the navigation. I use Go Route but here I have helpers classes to get it simple
  3. styles: classes to manage styles for icons, fonts, buttons.
  4. widgets: common widgets of the app, for example, message boxes, dialogs, inputs.

Data layer

On this layer we implement the data sources. There are two types of datasources: local and remote.

  1. Local: local database, file system, cache.
  2. Remote: external services, APIs.

Bellow is the data folder:

Data layer folder

I define one folder for each Business Entity where I implement the entity repository. In this example, to get it simple, the bull repository had been implemented to use memory as data source.

import '/src/domain/bull/bull.dart';
import '/src/domain/bull/bull_repository.dart';

/**
* It manages bull entities in memory.
*/
class BullInMemory implements BullRepository{

/// list of bulls in memory.
List<Bull> bulls = List.empty(growable: true);

@override
Future<List<Bull>> getAll() async{
return bulls;
}

@override
Future<Bull> add(Bull bull) async{
bulls.add(bull);
return bull;
}

}
Domain & Data layers

Conclusion

I hope you can take the first step with this tips. Don’t forget:

  1. Create folders for each layer.
  2. Create folders for each business entity.
  3. Respect layers flow.
  4. Use a state management.
  5. Be simple.
App Layers Flow

Topics on my TODO list:

  1. BLoCs.
  2. How to manage configuration files.
  3. Local persistence using a database

Thanks for reading, clap if you like it !

Let me know your comments bellow.

--

--

Bernardo Iribarne
Nerd For Tech

Passionate about design patterns, frameworks and well practices. Becoming a Flutter expert