Flutter with clean architecture and BLOC state management
The objetive of this architecture and most of the other ideas regarding the architecture of systems is the separations of concerns, that means specialized sections like a vehicle production plant or any other mass production assembly line. We achieve this separation by dividing the system into layers (Specialized sections).
The most important benefits of this architecture are:
- Independence of frameworks: Our systems doesn’t depend on external libraries and can changes easily from one to other without that implying a big effort. Imagine a new module to be included in the Internation Space Station (ISS), we don’t need to disassemble the entire ISS to include the new module instead we have “small structures” (adapters) that facilitate the assemble of whatever new module or disasseble it later when be obsolete “deprecated”.
2. Testability: Continuing the analogy of the ISS the new module can be tested in isolation, we don’t need to test the entire ISS to verify it was correctly coupled and supply all the spected functionalities.
BLOC state management
BLOC (Business Logic Component), it is a class that keeps the state of our app receiving input events and reacting, notifying its changes through streams.
This is how the packages structure looks at the end.
Let’s start from back to front, from data source to presentation.
Before we start implementing the network operations to get the required data we have to create a bridge/adapter to connect Business(Data) with Frameworks(Data source),on lib folder create the following structure:
Into the abstraction package add a class to represent an interface like we do for Kotlin or Java.
On RecipeNetworkDataSourceImpl we are implementing the methods defined on RecipeNetworkDataSource.
- On line five we are creating an instance of RecipeServiceImpl which is the bridge to connect Business(Data) with Framework(Data source).
RecipeServiceImpl implements an interface with the same methods defined on RecipeNetworkDataSource.
On line 10 where we specify a token you have to go to Food2Fork and get a new token that works for you, it’s easy to do.
In this class we are doing the calls to the API, and each method returns a json object or a exception, this is the last point in the flow, now the response has to navigate back to the view passing through Business(Data, Interactors), Frameworks(Presentation, View).
Now is time to create the interactor package with the use cases which will create an instance of RecipeNetworkDataSourceImpl and with that the bridge between Business(Data) and Business(Interactors) be established.
For practical reasons let’s omitted the use case GetRecipe and just focus on SearchRecipes
- On line 7 we are creating an instance of RecipeNetworkDataSourceImpl that allows the comunication with Business(Data), Frameworks(DataSource).
- On line 11 we are getting the response of search the recipes on RecipeNetworkDataSourceImpl in form of Json object.
- On line 13 we are maping the Json response to a list of RecipeDto that is a DTO .
- On line 15 we are maping the list of RecipeDto to a list of Recipe that is a business model.
Now it’s time to include the presentation package that contains the Bloc classes and stablish the bridge between Business(Interactors) and Frameworks(Presentation).
- On line 9 we are defining an instance of SearchRecipes in order to stablish the bridge between Business(Interactors) and Frameworks(Presentation).
- On line 14 we are defining an instance of StreamController to handle the stream produced by the action to get recipes.
- On line 22 we are defining the method RecipeBloc where the instances of StreamController and SearchRecipes are initialized.
- On line 28 we are defining the method SearchRecipes to comunicate with the use case and get the recipes to feed the stream.
- On line 39 we are defining the method dispose to set free the StreamController when the consumers no longer require it.
Now it’s time to render the data into views(Widgets). RecipeListScreen is a StateFulWidget (A widget that has mutable state) because the UI we are building can change dynamically.
Into the RecipeListScreen we are adding subwidgets to handle the different states in the request of recipes (Loading, Completed, Error) and also we are defining methods to handle the action to search recipes on RecipeSelectorWidget and navigate to the recipe detail on RecipeDetailWidget.
- On line 22 we are defining an instance of RecipeBloc and in line 27 we are initializing it.
- On Line 46 we are creating a widget StreamBuilder that will listen to the events flowing from the stream, for every new event it will rebuild its descendents given them the latest event to work with.
- On line 89 we are set free the StreamController of RecipeBloc when the screen is no longer available.
That’s it, as usual if you need dig in detail into the code feel free to go and check on this repo , and of course if you have any question or concern just let me know, and finally but not least clap if this information taught you something new.