Micro Frontends in Flutter: Modularization Application -Part 2

TungND
6 min readMay 21, 2024

--

In the previous part, we’ve discussed about advantages, disadvantages and noted some ideas we need to implement. With some readers getting this article for the first time, please read part 1 first. Of course, I have to talk again, make sure that you have a complete checklist of everything have to do before starting.

Micro Frontend Application with Flutter

Implementation

In this part, (not sure is the last part 😀), we will try to modulize a project following the traditional template. I’ve created a simple project here. It just has 2 screens, login and home with a listview, and follows Clean Architecture.
For some hard-coder “Talk is cheap, show me the code”, this is the full source code of the project after modularization. You can check it first, or after reading this article.

1. Package Management
We need to have package management, to manage all packages inside the projects, For example: we can run pub get, build_runner or gen-l10n in each module just in one terminal. Luckily, I figured out melos, developed by invertase.

name: flutter-micro-frontend
packages:
- modules/*
command:
bootstrap:
runPubGetInParallel: false
scripts:
gen-l10n:
exec: flutter gen-l10n
ignoreErrors: true

Just need a config like this, we can run melos bootstrap for getting all dependencies inside all packages, and melos gen-l10n for generating language files. That’s very easy.

2. UI Module
* Note: You should read about creating Flutter package and Dart package first to learn about packages and the structure of a Dart package.

Structure of UI module

This is a small version of UI module, of course, with a big project or another business project, this structure will be different. Just make sure inside this module:

  • Just have UI element here, not too complicated logic
  • For each category (like dialogs, list_view,…) we should have an export file to export all concrete elements.
export 'grid_view_load_more.dart';
export 'list_view_load_more.dart';
  • We should place fonts using the whole app here and adding to ui_theme. Just make sure that we always need to have package/ui before the path, expackages/ui/assets/fonts/SFProText-Regular.ttf .
  • We have to export LocalizationsDelegate to adding inside localizationsDelegates of Main App

That’s all, no more too many things we need to talk about this package

3. Core Module

Structure of core module

This is the skeleton of the whole app and all other modules. So consider carefully when adding or modifying any code inside this module. I also have some notes:

  • In the traditional project, as you see, I’ve used Get for state management, navigation, and dependencies injection. But when modulizing it, I use the raw Flutter navigation and get_it. That’s because when making modules, we try not to depend on too many other libraries. To re-use the module in many places, even inside the partner app, we cannot make sure that the partner is also using Get. So when building a module or a package, try not to depend on too many other libraries.
  • I still used Get for stage management, so I have some base view, and base controller here. If you prefer BLoC, I also have some base code for BLoC. But ideally, we should use only one state management for all modules, for synchronous.
  • This module still follows Clean Architecture. I’ve used Dio and Retrofit for calling API. So I will create dio and some config for it from here
  • I have libs.dart , which exports all the other third-party packages I used inside the core. If we need to customize or use them inside another package, we just need to import the core module.
Sample of libs.dart

4. Main Application
We turn back to Main Application. As said in UI module, we need to add localizationsDelegates and theme for MaterialApp. That’s all for app config.
We temporarily create an empty splash screen and home screen. We will develop some function modules and add them to it.

Good preparation, now we start to implement the first module, which almost all systems need, is authentication.

5. Auth Module
The idea will be the dependencies just need to call Auth.start(context). then get the result for the next business. All logic or UI pages inside the authentication process, like login, register, forgot password, OTP,… will be implemented in this module. That was incredible, Main app didn’t know any logic about authentication.

auth module structure
  • As you see, we don’t need to add the prefix auth for each class/file such as repository, client, or use case.
  • Manage session, ex save access token, refresh token, I also implement only inside this module
  • I have AuthController to manage all logic for the whole auth module. It is something like a global stream, that all other classes can call. Let’s see some piece of code inside it
  late Completer completer;

Future<void> auth(BuildContext context) {
completer = Completer();
final result = CheckIsLoggedInUseCase(GetIt.instance.get<Repository>()).execute();
result.fold((left) {
if (left) {
completer.complete();
} else {
Navigator.pushAndRemoveUntil(context, Routes.checkPhone(), (route) => false);
}
}, (right) {
showError(context, right);
});
return completer.future;
}

void loginSuccessfully() {
if (!completer.isCompleted) {
completer.complete();
}
}

As you see, whenever other screens call to loginSuccessfully(), AuthController will return the result and finish the auth process.

Back to Splash screen (in Main Application), we just need to call auth function:

class SplashController extends BaseController {
@override
void onReady() async {
super.onReady();
await Auth.start(Get.context);
_goToHome();
}

void _goToHome() {
Get.offNamedUntil(MainRouteName.home, (route) => false);
}
}

Very clear, after the auth process is complete, we will open Home screen. Perfect!!!

6. Product Module
This is just a sample module that I want to demo. Almost normal modules have the same concepts:

product module structure

In this demo, I have to export HomeProductWidget, for Home screen. There is nothing more need to say in this module if you are familiar with some previous modules. Inside the home screen, just to call:

import 'package:product/product.dart';

class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return const HomeProductWidget();
}
}

We’ve completely modulized an app.

This is the full source code of the project after modularization

Conclusion

Modularization has gained popularity as an architectural pattern that promotes modularity, code reusability, and scalability in app development. Of course, depending on your business and resources, you can consider using traditional architecture or separating it into packaging.

If you learned something from this article, don’t forget to give it a thumbs up. If you have some confusion or have some problems when modularizing your app, please leave a comment, and we will learn together.

--

--