Start
flutter_modular was built using the engine of modular_core that's responsible for the dependency injection system and route management. The routing system emulates a tree of modules, just like Flutter does in it's widget trees. Therefore we can add one module inside another one by creating links to the parent module.
Inspirations from the Angular
The entire flutter_modular system came from studies carried out in Angular (another Google framework) and adapted to the Flutter world. Therefore, there are many similarities between the flutter_modular and Angular Routes and Dependency Injection System.
Routes are reflected in the Application using the the new Navigator 2.0 features alongside the use of multiple nested browsers. We call this feature RouterOutlet, just like in Angular.
Each module can be completely independent, so the same module can be used in multiple products. By dividing modules into packages, we can approach a micro-frontend application structure.
Starting a project
Our first goal will be the creation of a simple app with no defined structure or architecture yet, so that we can study the initial components of flutter_modular
Create a new Flutter project:
flutter create my_smart_app
Now add the flutter_modular to pubspec.yaml:
dependencies:
flutter_modular: 6.x.x
If that succeeded, we are ready to move on!
Flutter's CLI has a tool that makes package installation easier in the project. Use the command:
flutter pub add flutter_modular
The ModularApp
We need to add a ModularApp Widget in the root of our project. MainModule and MainWidget will be created in the next steps, but for now let's change our main.dart file:
import 'package:flutter/material.dart';
void main(){
return runApp(ModularApp(module: /*<MainModule>*/, child: /*<MainWidget>*/));
}
ModularApp forces us to add a main Module and main Widget. What are we going to do next? This Widget does the initial setup so everything can work as expected. For more details go to ModularApp doc.
It's important that ModularApp is the first widget in your app!
Creating the Main Module
We can have several modules, but for now, let's just create a main module called AppModule:
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
void main(){
return runApp(ModularApp(module: AppModule(), child: <MainWidget>));
}
class AppModule extends Module {
void binds(i) {}
void routes(r) {}
}
Note that the module is just a class that inherits from the Module class, overriding the binds and routes method. With this we have a route and injection mechanism separate from the application and can be both applied in a global context (as we are doing) or in a local context, for example, creating a module that contains only binds and routes only for a specific feature!
We've added AppModule to ModularApp. Now we need an initial route, so let's create a StatelessWidget to serve as the home page.
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
void main(){
return runApp(ModularApp(module: AppModule(), child: <MainWidget>));
}
class AppModule extends Module {
void binds(i) {}
void routes(r) {
r.child('/', child: (context) => HomePage()),
}
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(title: Text('Home Page')),
body: Center(
child: Text('This is initial page'),
),
);
}
}
We've created a Widget called HomePage and added its instances in a route called ChildRoute.
There are some ModularRoute types: ChildRoute, ModuleRoute, RedirectRoute and WildcardRoute.
ChildRoute: Serves to build a Widget.
ModuleRoute: Concatenates another module.
RedirectRoute: Redirect to other route.
WildcardRoute: Default route in Module.
Inside the routes(r);
method we can use:
r.child => ChildRoute.
r.module => ModuleRoute.
r.redirect => RedirectRoute.
r.wildcard => WildcardRoute.
Creating the Main Widget
The main Widget's function is to instantiate the MaterialApp or CupertinoApp.
In these main Widgets it's also necessary to set the custom route system. For this next snippet we'll use MaterialApp, but the process is exactly the same for CupertinoApp.
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
void main(){
return runApp(ModularApp(module: AppModule(), child: AppWidget()));
}
class AppWidget extends StatelessWidget {
Widget build(BuildContext context){
return MaterialApp.router(
title: 'My Smart App',
theme: ThemeData(primarySwatch: Colors.blue),
routerConfig: Modular.routerConfig,
); //added by extension
}
}
class AppModule extends Module {
void binds(i) {}
void routes(r) {
r.child('/', child: (context) => HomePage()),
}
}
class HomePage extends StatelessWidget {
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(title: Text('Home Page')),
body: Center(
child: Text('This is initial page'),
),
);
}
}
Here we create a Widget called AppWidget containing an instance of MaterialApp.router.
Support methods
Navigator 2.0 made Flutter's routing system more dynamic, but some information, previously passed in MaterialApp or CupertinoApp, has been removed, and it will be necessary to configure it using Modular's own support methods.
Modular.setNavigatorKey(myNavigatorKey);
Modular.setObservers([myObserver]);
Modular.setInitialRoute('/home');
You can change the prints
with function setPrintResolver
:
setPrintResolver((text) => print(text));
// deactivate
setPrintResolver((text){});
That's enough to run a Modular app. In the next steps let's explore navigation.