Flutter provides a wide variety of options for building apps for both Android and iOS. Popular frameworks like MVC and MVVM can be used. However, BLoC is generally considered the ideal Flutter architecture due to Flutter’s uniqueness and its focus on widgets. In this blog post, you’ll learn how the Flutter architecture works, how to implement it and get answers to frequently asked problems. To begin with, let’s start with the most important concept.

What exactly is a mobile application architecture?

Application architecture is a way of arranging the various components in a project that allows us to fully integrate the business logic in a structured way that other developers can understand. Choosing the exemplary architecture for our application will also help to maintain the project better in the future.

What to pay attention to when designing the Flutter architecture of an app?

In the case of Flutter mobile applications, we need to take care of the 3 most important layers of any project. These are 1) the elements through which the user interacts, 2) the business logic executed by the application, and 3) the area where the data used in the application is retrieved. And these are the three elements that make up the so-called “Three-Layer Architecture” used in many projects at Applover, among others.

A closer look at the three-layer architecture

The main idea is to introduce separation of the above-mentioned layers so that each area is responsible for only one thing.

The first layer is the so-called presentation layer, which is the area where all the graphical elements of the application are located, such as views of individual screens, and individual elements of the user interface.

The next layer is the domain layer. In this area, the entire business logic responsible for the operation of the application functions is written out. In addition, the domain layer reacts to input from the user interface and communicates with the repository in order, for example, to send form data to the API.

The last layer is the data layer, which is only responsible for fetching or sending data to the APIs or reading or writing data that we save locally on the user’s device.

BloC pattern – the way for state management of screens in the presentation layer

In applications that use the Flutter framework, it’s very good practice to use an architectural pattern called BloC, which stands for Business Logic Component. It makes it easy to manage the state of screens or individual UI elements by dividing each action into states that are sent out depending on what the user is doing.

@freezed
class ExampleState with _$ExampleState {
  const factory ExampleState.initial() = ExampleStateInitial;
  const factory ExampleState.loading() = ExampleStateLoading;
  const factory ExampleState.success(ExampleModel model) = ExampleStateSuccess;
  const factory ExampleState.error(String? message) = ExampleStateError;
}

By using the Bloc template for individual screens, we can have a clear overview of the individual states into which a given screen changes, as well as easily add new ones when the need arises.

When the user clicks and interacts with the UI, it triggers an action in the component. The main responsibility of the BLoC component is to connect to the domain layer through the use case and update the state of the UI component. It also allows us to somehow separate the business logic of the individual screens from the repositories.

Use cases – why are they so important in the architecture of a modern mobile application?

A use case, by definition, is a single call to the business logic of our application. It acts as a “bridge” between the domain layer and the data layer. Adding this idea to your project will help keep your application architecture clean and will make it easier to write unit tests that test the most important business logic calls in your project.

@injectable
class GetExampleFeatureDataUseCase {
  final ExampleRepository  _exampleRespository;
  GetExampleFeatureDataUseCase(this._exampleRespository);
  Future<ExampleModel> call() => _exampleRespository.getExampleData();
}

The example above communicates with the repository at the data layer to retrieve items and display them on the view of our application, and as you can see, it represents a single business logic call that we can easily use in any area of our application 😀

Entions and DTOs – what does it help us to avoid by introducing such a division?

We know that when we retrieve data from the web, we will most often get a response in the form of a JSON object, and directly operating on JSON files can lead to errors.

Most of the time, the problem comes up when we want to get data out of a JSON-type object by using the keys of its fields. This is where the DTO data model comes in handy.

Creating a DTO object will solve this problem by parsing the data directly from the “json” response to the object. Here we use the Freezed package known and loved by Flutter developers. Thus, mapped DTO object, then we can map to the entity we will use in our presentation layer.

Using DTO will avoid the situation when, for example, instead of accessing the data this way ‘json[“title”]’, you use ‘json[“titlee”]’ by mistake 🙂

@freezed
class ExampleDataDto with _$ExampleDataDto {
  const factoryExampleDataDto({
    required String exampleTitle,
  }) = _ExampleDataDto;
  factoryExampleDataDto.fromJson(Map<String, dynamic> json) => _$ExampleDataDtoFromJson(json);
}

In the DTO model, we can also implement a mapper in the form of an extension, which is a method that converts DTOs to entities. This helps us already in the data layer to map DTO models to entities that will go to our presentation layer and from it to the view.

extension ExampleDataDtoExtension on ExampleDataDto {
  ExampleData get toEntity => ExampleData(
       exampleTitle:exampleTitle
      );
}

Retrofit – a helpful tool for communicating with APIs at the data layer

If you know about Android application development, you have surely heard of Retrofit, a library that allows us to implement API calls more simply and cleanly. It can handle errors that can happen on the server side of an application.

When developing applications in Flutter technology, another very good practice will be to use the package Retrofit to improve the queries to the APIs it works very similarly to its Android counterpart, and the implementation should not cause any problems.

Contact

Do you want to find out more about Flutter app development?

Talk to us!

The best way for a good Flutter app architecture

Using pre-made solutions for your architecture, like the Google Bloc library, can speed up the development process significantly. It has a low maintenance cost and a high level of adaptability. You can get your architecture up and running quickly in your Flutter application, and you won’t have to worry a lot about it. If you’re interested in more tips about the development, check out an article by Michał on Flutter animations.