Clean architecture in Flutter — kick-off
This article shows an approach to Clean Architecture by Uncle Bob in Flutter. Demo app fetches posts from public API and displays them inside ListView. I’ve decided to use GetIt as a service locator and Chopper for API calls.
Main file initialises the service locator and opens the list screen.
Presentation layer
Screens
This package is divided by screens. Every has its own main widget (here called screen), optional subdirectory with widgets used on that screen and bloc (Business Logic Component) implementation.
Screen widgets most of the time are Scaffolds which makes it easy to implement material design page components.
If the screen does some business logic it has to communicate with bloc. To do that use BlocBuilder inside the widgets hierarchy. Builder method refreshes views based on received state. See below how refresh indicator appears on LoadingState and is replaced by ListView when data comes.
Bloc
The core function in Bloc is the mapEventToState
method. It receives events sent by widgets, handles longer operations calling usecases and “returns” data by yielding state to all listening widgets. That means you can listen to the same bloc in more than one widget.
Usecases are provided in constructor by service locator.
Events and States could be extracted to separate files, but for sure they clearly describe all business actions — what user can do on that screen and how app responds to that actions on UI.
Last but not least is initial state. Either it’s EmptyState or LoadingState it’s important to display some info on UI rather than a blank white page.
Domain layer
Entities
Business object models of application. They may reference each other. They may contain methods. But they CAN’T know about other packages outside entities.
Repositories definitions
Abstract repositories, which have implementations in the data layer.
Usecases
Make one or more operations using repositories.
Data layer
Network
Data source handles integration between network and domain objects. It converts from network response to domain entity object.
Chopper annotated services make API calls using HttpClient, but definitions of request are easily readable.
Mappers
JSON to/from Entity functions. Unfortunately Dart doesn’t support static methods implementation inside extension :/
Repositories
Implementation of previously defined interfaces from domain layer. They can make use of one of more data sources. It’s a good place to fetch data from the network and store it locally to minimize network calls.
Summary
Final result should look like below. I believe that one was easy to understand and the next one will take care about caching multiple data sources and streams in usecases.