[Flutter]: How to configure application in the cloud by Firestore “GetX + DIO + Firebase”

Ala Alhaj
9 min readOct 10, 2020
“That there’s some good in this world, Mr. Frodo… and it’s worth fighting for” , Sam

I personally do not like to re-publish my work to the store for tiny changes such as coloring, add/change text(s), or whatever you can name. I worked once with a customer where he kept asking over and over again to change some part of the design / coloring that he might made-up his mind for no reason, :/ there you go I must do it. I tried to keep myself and this customer with the requirements after agreeing, but NOPE! However, i came up with a nice idea:

What if we configure the application in the cloud, so we can avoid waste of time + energy for both of the customer and the developer.

Of course, I implemented the idea in JSON file where it contains List / keys for headers, padding, auto animation and many other things to name. In this article, I decided to share the same idea but by using Firestore as real-time service and another away beside remote_configuration_firebase package, since it is not delivering real-time service, even though it is possible to do it by changing the parameter value of expiration to 0 but still not efficient in way I want to share with you all ! No more talk, time to do coding but before this I will list down the used packages / API service that our application retrieve the data from. The figure down shows how the application designed.

Draw by using GoodNotes application , iOS.

Packges

I would list down the important parts and the reset will be given as screenshot from YAML file.

1- Dio package for API calls from https://the-one-api.dev/

2- GetX package for State Management, routing, and theme. everything i would say !

3- Cloud Firestore for real-time remote configuration.

The reset are not less important as they play their big-parts. Please make yourself familiar:

all the needed packages, pubspec.yaml

once you have everything setup make sure that you had your firebase setup and all of the required steps secured, then time to smash it buddy !

Oh, it’s quite simple. If you are a friend, you speak the password, and the doors will open.

Implementation

I would be honest from now before you go further with the article, I won’t explain the basic stuff such as setup Firestore package for android / iOS, the basic stuff would call. So, get these things done be yourself by reading the documentation of the package from HERE.

1- Initialize APP & Main.dart

To get the application run, there is a need to initial Firebase and also services that are attached to GetxService subclasses, under initServices() function.

It is quite nice to use GetMaterialApp class that provides handy and powerful options such as life-event log of every activity that shows any actions taken within GetX environment. Alright, there are two things nice to mention as you may know that GetX also provides navigation features other than State Management. AppPages class contains two variables: Initial page name and List of GetPage objects that helps GetX to trace when we Get.toName(‘/example’).

app.dart

For code base, please feel free and refer yourself to Route Folder contains: app_pages.dart and app_routes.dart. The code base link is attached at the bottom.

2- API

I would like to mention a thing before i go ahead with this part API by Rike developer, rike.dev. I had flutter interview where the tech interviewer did not like that I am writing most of API/Model services manually as it does not sound efficient to him. Well he has his point maybe, but what i want to point at is that you should do what it suits you. However, the api is easy and you can spend 10/15 minutes to master it: https://the-one-api.dev/documentation . Once you go through, you will need to register to get the bearer code to add up to your header when your application starts requesting. Create API class under api.dart under, the class should contain Dio object and one Future get function. it should look like this:

API.dart

3- Repositories

Since the API delivers sort of services, i had decided to take Movie and Book and additionally to this I added another one for remote configuration a repository.

BookRepostiory: it is a class that extends GetxService class to be a subclass as you should have the principle of OOP. It depends on an object from API class that gets through a constructor and if not, then define the default one. Three functions:

1- init: it is a future function that returns instance of define object by the called function by using this keyword. <- they are always used at initalService function called in main.dart.

2-getBooks: easy it is a future function that returns BooksModel object by calling a get function.

3- getBookChapters: returns BookChaptersModel defined by book’s id and used inside path parameter. This is passed inside of Chapter controller.

book_repository.dart

MovieRepostiory: it is 90% similar to book repository but only returns MoviesModel object.

movie_repository.dart

ConfigRepository: This is the game change i would call, before i go ahead with explanation. I had another approach for this way by using JSON file that contains the most important values, so the application would get the configuration from the server by using get function as stream. I will write another article how to configure the application in the cloud by using Server-side in Node.js and Aqueduct. The reason i choose Firestore is for the sake of simplicity, which there is one collection , one document “UI_application”, and 4 fields are given so far and you can create more fields and add them in configuration mode, lib/models/config_model.dart.

collection in Firestore
config_model.dart

This is an important part of our work , this model is used organize and get the fields one by one. Easy to change later if you want to add more options, only add more properties, add to constructor, and then modify the return value in fromDcoumentSnapshot method.

configStream: it is a stream that returns the defined model that we already mention. We first initiate Firestore instance, call the collection by its name, then call the document by its name as we only seek on document that contains what we need so far. then simply call out snapshots to open up the stream call(s). Map method used to take the event value and then pass it to fromDocumentSnapshot functions. The event value is DocumentSnapshot and that makes sense because the given event it is a map.

configuration_repository.dart

Well , Well ! Time for a break and hear out:

“THE WORLD IS NOT IN YOUR BOOKS AND MAPS. IT IS OUT THERE.”, Gandalf

4- Controllers & Bindings

Welcome my friend to the magic ! The trick behind this application are Controllers and Bindings classes where they are working as State Management for data. Controllers are providing Reactive State and Bindings are used to initial or i would love to call the triggers under dependency function by calling the needed classes/methods by using Get.put , Get.putLazy, or async one. However, you can simply refer yourself to GetX packages to read the documentation https://pub.dev/packages/get. Alright, we have 2 controllers and 2 bindings in our application and will discuss them in depth:

InitialController & InitialBindings

It does not mater which part we start as long as they both implemented and used together.

InitialController: it is subclass of GetxController that helps us to maintain the state of the UI and as well the data between the components / controllers. It starts with injecting the required repositories: book, movie(s), and configuration.

class InitialController extends GetxController {InitialController({this.bookRepostiory, this.movieRepostiory, this.configRepository});/// inject repo classes dependency
final BookRepostiory bookRepostiory;final MovieRepostiory movieRepostiory;final ConfigRepository configRepository;........
}

Then the main properties such as loading handlers that help(s) to trigger the indicator when API + Firestore is busy in the background. Last not least 3 variables that holds for the mentioned repositories: again book, movie(s), and configuration.

class InitialController extends GetxController {/// create a reactive status from request with initial value , loading by using ENUMfinal statusMovie = Status.loading.obs;final statusBook = Status.loading.obs;
/// Create a reactive / stream variablesfinal books = Rx<BooksModel>();final movies = Rx<MoviesModel>();Rx<ConfigurationModel> _configuration = Rx<ConfigurationModel>();ConfigurationModel get configurationModel => _configuration.value;
.....[methods]
}

Apologize in the base code on my repository may have extra variable(s) that I did not use and the reason why is i may keep going with this project and deliver more features ^<^. This is not everything, in this subclass we need to override onInit by calling it and write our statements or another method that it can be called by.

@overridevoid onInit() async => setUp();setUp() async {_configuration.bindStream(configRepository.configStream());configRepository.configStream().listen((event) {print('listening to coming data version = ${event.version} Dark Theme = ${event.darkTheme} Color code = ${event.colorHexa}');Get.changeTheme(event.darkTheme? ThemeData.dark().copyWith(primaryColor: Color(int.parse(event.colorHexa)),buttonColor: Color(int.parse(event.colorHexa),),):ThemeData.light().copyWith(primaryColor: Color(int.parse(event.colorHexa)),buttonColor: Color(int.parse(event.colorHexa),),));_configuration(event);update();});await bookRepostiory.getBooks().then((value) {books(value);statusBook(Status.success);}, onError: (error) {print(error.toString());statusBook(Status.error);Get.snackbar('Error Retrving books', error.toString(),duration: Duration(seconds: 5));});await movieRepostiory.getMovies().then((value) {movies(value);statusMovie(Status.success);}, onError: (error) {print(error.toString());statusMovie(Status.error);Get.snackbar('Error Retrving Movies', error.toString(),duration: Duration(seconds: 5));});}

it is so easy, we use _configuration variable to run bindStream that establishes a stream listener by configRepository.configStream(). Every event that happens from Firestore, it will pass immediately to our reactive variable. The other two function(s) are for API calls for movies and books. Each of them, returns a variable to the proper variable and then update our status tracker or shows the error if there is any error/exception that may occur.

initial_controller.dart

Alright, this is the controller and what about the binding class ? If you look closely that controller has repositories as contractors parameters. Bindings help to pass those before drawing the UI and plays the major-role by passing the injected variables by using Get.find() as it will automatically look up by the suitable variable or you can make lifer easier by informing it in this way: Get.find<BookRepository>();

initial_bindings.dart

This is all for both of them as they are going to be used in home screen by using GetView class instead of statelessWidget.

ChaptersController & ChaptersBinding:

It is almost the same in the pervious one instead of calling 3 repositories, we call one which is books since there is a method used for returning chapters. There is no need to explain bindings since there is no much to explain.

chapters_controller.dart

Hint: there is big deal by using onInit or onReady, but i wanted to try since it is good way to call async function after onInit method called.

5- Routing

The routing in our project was designed by having two separated Dart files which one for naming and the other one List for Routes that can be used in GetMaterialApp in app.dart.

app_routes.dart
app_pages.dart

6- Pages (UI)

Home screen with single Column grid

Looks pretty, isn’t it ? ❤ The design has been split into two parts: View + Widget. The come screen uses as we said GetView and also some part of the design is in the base code of the view but only cards are written as widget(s): movies and books. The reason is to keep our code as short as possible and clean. Please refer yourself to the code base for the design.

Final work ❤

The end

~ Moonlight drowns out all but the brightest stars.

Aye, this is not the end my dear reader as I am planning the following:

1- Implement Custom field in WooCommerce that hide(s) base on the shipping field method.

2- Topics about AngularDart from basic to advanced as series.

3- How to created your own Core that shared between Flutter / AngularDart.

4- Scorpio Server, in Dart for e-commerce<- this is my thesis i would share the first version of it and explain.

5- How to use Dio for stream calls.

This is the planned article(s)/Series I am working all by myself and you may notice that this is a hard work and it takes a lot of time and effort as well. I won’t ask for anything more you learn something and share this article/code to anyone.

However, this is the link to GitHub: https://github.com/scardeath/flutter-remote-configuration . It is public, ready to use, and it is written in Flutter version 1.22.0 and the Bearer code will stay from now till it gets expired, so if you may end-up with authentication error then you have create your own account.

At the end, your supporting will mean alot by reading this article by giving a clap and if you think that I am worth the support, then buy me a coffee over here

--

--