flutter_modular 1.2.7+1

  • Readme
  • Changelog
  • Example
  • Installing
  • 98

CI & Coverage pub package Coverage Status [![Join the chat at https://discord.gg/ZbdsWA4](https://img.shields.io/badge/Chat-on%20Discord-lightgrey?style=flat&logo=discord)](https://discord.gg/ZbdsWA4)

All Contributors

Flutter Modular #

flutter_modular

This README is also available in Brazilian Portuguese.

What is Flutter Modular? #

As an application project grows and becomes complex, it's hard to keep your code and project structure mantainable and reusable. Modular provides a bunch of Flutter-suiting solutions to deal with this problem, like dependency injection, routing system and the "disposable singleton" system (that is, Modular disposes the injected module automatically as it is out of scope).

Modular's dependency injection system has out-of-the-box support for any state management system, managing your application memory usage.

Modular Structure #

Modular structure consists in decoupled and independent modules that will represent the features of the application. Each module is located in its own directory, and controls its own dependencies, routes, pages, widgets and business logic. Consequently, you can easily detach one module from your project and use it wherever you want.

Modular Pillars #

These are the main aspects that Modular focus on:

  • Automatic Memory Management.
  • Dependency Injection.
  • Dynamic Routing.
  • Code Modularization.

Examples #

Getting started with Modular #

Installation #

Open your project's pubspec.yaml and add flutter_modular as a dependency:

dependencies:
  flutter_modular: any

You can also provide the git repository as source instead, to try out the newest features and fixes:

dependencies:
  flutter_modular:
    git:
      url: https://github.com/Flutterando/modular

Using in a new project #

To use Modular in a new project, you will have to make some initial setup:

  1. Create your main widget with a MaterialApp and set its initialRoute. On onGenerateroute, you will have to provide Modular's routing system (Modular.generateRoute), so it can manage your routes.
//  app_widget.dart
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';

class AppWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // set your initial route
      initialRoute: "/",
      navigatorKey: Modular.navigatorKey,
      // add Modular to manage the routing system
      onGenerateRoute: Modular.generateRoute,
    );
  }
}
  1. Create your project's main module file extending MainModule:
// app_module.dart
class AppModule extends MainModule {

  // Provide a list of dependencies to inject into your project
  @override
  List<Bind> get binds => [];

  // Provide all the routes for your module
  @override
  List<Router> get routers => [];

  // Provide the root widget associated with your module
  // In this case, it's the widget you created in the first step
  @override
  Widget get bootstrap => AppWidget();
}
  1. In your main.dart, wrap your main module in ModularApp to initialize it with Modular:
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';

import 'app/app_module.dart';

void main() => runApp(ModularApp(module: AppModule()));
  1. Done! Your app is set and ready to work with Modular!

Adding routes #

Your module's routes are provided by overriding the routers getter:

// app_module.dart
class AppModule extends MainModule {

  // Provide a list of dependencies to inject into your project
  @override
  List<Bind> get binds => [];

  // Provide all the routes for your module
  @override
  List<Router> get routers => [
      Router('/', child: (_, __) => HomePage()),
      Router('/login', child: (_, __) => LoginPage()),
  ];

  // Provide the root widget associated with your module
  @override
  Widget get bootstrap => AppWidget();
}

To push your route to your app, you can use Navigator.pushNamed:

Navigator.pushNamed(context, '/login');

Alternatively, you can use Modular.to.pushNamed, in which you don't have to provide a BuildContext:

Modular.to.pushNamed('/login');

Use Modular.to for literal paths or Modular.link for routes in current module:

// Modules Home → Product
Modular.to.pushNamed('/home/product/list');
Modular.to.pushNamed('/home/product/detail/:id');

// Inside Product module, use Modular.link and navigate between Product module routes
Modular.link.pushNamed('/list');
Modular.link.pushNamed('/detail/:id');

Dynamic routes #

You can use the dynamic routing system to provide parameters to your Router:

// Use :parameter_name syntax to provide a parameter in your route.
// Route arguments will be available through `args`, and may be accessed in `params` property,
// using square brackets notation (['parameter_name']).

@override
List<Router> get routers => [
  Router(
    '/product/:id',
    child: (_, args) => Product(id: args.params['id']),
  ),
];

The parameter will, then, be pattern-matched when calling the given route. For example:

// In this case, `args.params['id']` will have the value `1`.
Modular.to.pushNamed('/product/1');

This notation, however, is only valid for simple literals. If you want to pass a complex object to your route, provide it in arguments parameter:

Modular.to.pushNamed('/product', arguments: ProductModel());

And it will be available in the args.data property instead of args.params:

@override
List<Router> get routers => [
  Router(
    '/product',
    child: (_, args) => Product(model: args.data),
  ),
];

Route guard #

Route guards are middleware-like objects that allow you to control the access of a given route from other route. You can implement a route guard by making a class that implements RouteGuard.

For example, the following class will only allow a redirection from /admin route:

class MyGuard implements RouteGuard {
  @override
  bool canActivate(String url) {
    if (url != '/admin'){
      // Return `true` to allow access
      return true;
    } else {
      // Return `false` to disallow access
      return false
    }
  }
}

To use your RouteGuard in a route, pass it to the guards parameter:

@override
List<Router> get routers => [
  Router('/', module: HomeModule()),
  Router(
    '/admin',
    module: AdminModule(),
    guards: [MyGuard()],
  ),
];

If placed on a module route, RouterGuard will be global to that route.

Route transition animation #

You can choose which type of animation you want to be used on your pages transition by setting the Router's transition parameter, providing a TransitionType.

Router('/product',
  module: AdminModule(),
  transition: TransitionType.fadeIn,
), //use for change transition

If you use transition in a module, all routes in that module will inherit this transition animation.

Custom transition animation route #

You can also use a custom transition animation by setting the Router parameters transition and customTransition with TransitionType.custom and your CustomTransition, respectively:

Router('/product',
  module: AdminModule(),
  transition: TransitionType.custom,
  customTransition: myCustomTransition,
),

For example, this is a custom transition that could be declared in a separated file and used in the customTransition parameter:

import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';

CustomTransition get myCustomTransition => CustomTransition(
    transitionDuration: Duration(milliseconds: 500),
    transitionBuilder: (context, animation, secondaryAnimation, child){
      return RotationTransition(turns: animation,
        child: SlideTransition(
          position: Tween<Offset>(
            begin: const Offset(-1.0, 0.0),
            end: Offset.zero,
          ).animate(animation),
          child: ScaleTransition(
            scale: Tween<double>(
              begin: 0.0,
              end: 1.0,
            ).animate(CurvedAnimation(
              parent: animation,
              curve: Interval(
                0.00,
                0.50,
                curve: Curves.linear,
              ),
            ),
            ),
            child: child,
          ),
        ),
      )
      ;
    },
  );

Grouping routes #

You can group routes that contains one or more common properties. Properties like guards, transition and customTransition can be provided both for single routes and groups of routes:

List<Router> get routers => [
  Router('/', module: HomeModule()),
  Router.group(
    guards: [MyGuard()],
    routes: [
      Router("/admin", module: AdminModule()),
      Router("/profile", module: ProfileModule()),
    ],
  ),
);

Router generic types #

You can return values from navigation, just like .pop. To achieve this, pass the type you expect to return as type parameter to Router:

@override
List<Router> get routers => [
  // This router expects to receive a `String` when popped.
  Router<String>('/event', child: (_, __) => EventPage()),
]

Now, use .pop as you would with Navigator.pop:

// Push route
String name = await Modular.to.pushNamed<String>();

// And pass the value when popping
Modular.to.pop('Jacob Moura');

The routing system can recognize what is in the URL and navigate to a specific part of the application. Dynamic routes apply here as well. The following URL, for instance, will open the Product view, with args.params['id'] set to 1.

https://flutter-website.com/#/product/1

Dependency Injection #

You can inject any class into your module by overriding the binds getter of your module. Typical examples to inject are BLoCs, ChangeNotifier classes or stores.

A Bind object is responsible for configuring the object injection.

class AppModule extends MainModule {

  // Provide a list of dependencies to inject into your project
  @override
  List<Bind> get binds => [
    Bind((_) => AppBloc()), // Injecting a BLoC
    Bind((_) => Counter()), // Injecting a ChangeNotifier class
  ];

  // Provide all the routes for your module
  @override
  List<Router> get routers => [
    Router('/', child: (_, args) => HomePage()),
    Router('/login', child: (_, args) => LoginPage()),
  ];

  // Provide the root widget associated with your module
  @override
  Widget get bootstrap => AppWidget();
}

Retrieving your injected dependencies in the view #

Let's assume the following BLoC has been defined and injected in our module (as in the previous example):

import 'package:flutter_modular/flutter_modular.dart' show Disposable;

// In Modular, `Disposable` classes are automatically disposed when out of the module scope.

class AppBloc extends Disposable {
  final controller = StreamController();

  @override
  void dispose() {
    controller.close();
  }
}

There are several ways to retrieve our injected AppBloc.

class HomePage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    // You can use the object Inject to retrieve..

    final appBloc = Modular.get<AppBloc>();
    //...
  }
}

By default, objects in Bind are singletons and lazy. When Bind is lazy, the object will only be instantiated when it is called for the first time. You can use 'lazy: false' if you want your object to be instantiated immediately (eager-loaded).

Bind((i) => OtherWidgetNotLazy(), lazy: false),

If you want the injected object to be instantiated every time it is called (instead of being a singleton instance), you may simple pass false to the singleton parameter:

Bind((i) => OtherWidgetNotLazy(), singleton: false),

Using Modular widgets to retrieve your classes #

ModularState #

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends ModularState<MyWidget, HomeController> {

  // Variable controller
  // Automatic dispose of HomeController

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Modular"),
      ),
      body: Center(child: Text("${controller.counter}"),),
    );
  }
}

Consuming a ChangeNotifier class #

Example of a ChangeNotifier class:

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  int counter = 0;

  increment() {
    counter++;
    notifyListeners();
  }
}

you can use the Consumer to manage the state of a widget block.

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(title: Text("Home")),
      body: Center(
        // By passing your ChangeNotifier class as type parameter, the `builder` will be called every time `notifyListeners` is called
        child: Consumer<Counter>(
          builder: (context, value) {
            return Text('Counter ${value.counter}');
          }
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          // You can retrive the class directly with `get` and execute the increment method
          get<Counter>().increment();
        },
      ),
    );
  }
}

Creating child modules #

You can create as many modules in your project as you wish, but they will be dependent of the main module. To do so, instead of inheriting from MainModule, you should inherit from ChildModule:

class HomeModule extends ChildModule {
  @override
  List<Bind> get binds => [
    Bind((i) => HomeBloc()),
  ];

  @override
  List<Router> get routers => [
    Router('/', child: (_, args) => HomeWidget()),
    Router('/list', child: (_, args) => ListWidget()),
  ];

  static Inject get to => Inject<HomeModule>.of();
}

You may then pass the submodule to a Router in your main module through the module parameter:

class AppModule extends MainModule {

  @override
  List<Router> get routers => [
    Router('/home', module: HomeModule()),
  ];
}

We recommend that you split your code in various modules, such as LoginModule, and place all the routes related to this module within it. By doing so, it will much easier to maintain and share your code with other projects.

WidgetModule #

WidgetModule has the same structure as MainModule/ChildModule. It is very useful if you want to have a TabBar with modular pages.

class TabModule extends WidgetModule {

    @override
  List<Bind> get binds => [
    Bind((i) => TabBloc(repository: i())),
    Bind((i) => TabRepository()),
  ];

  Widget get view => TabPage();

}

RouterOutlet #

A RouterOutlet may be used if you need a routing system that is totally detached from the main routing system. This is useful, for example, when you need an element to have its own set of routes, even though it is inside a page on the main route.

A practical example of this is its use in a TabBar or Drawer:

PageView(
  controller: controller
  children: [
    RouterOutlet(
      module: Tab1Module()
    ),
    RouterOutlet(
      module: Tab2Module()
    ),
    RouterOutlet(
      module: Tab3Module()
    ),
  ]
),

NOTE: Navigation within these modules are only supported through Navigator.of(context) using literal routes paths.

Lazy loading #

Another benefit you get when working with modules is that they are (by default) lazily-loaded. This means that your dependency injection will only be available when you navigate to a module, and when you exit that module, Modular will manage the resources disposal by removing all injections and executing dispose() (if available) on each injected dependency.

Unit test #

You can use the dependency injection system to replace a Bind with a mocked Bind, like, for example, a mocked repository. You can also do it using "Inversion of Control" (IoC).

For example, you can make a repository interface (ILocalStorage) that satisfies your repository contract requirement and pass it as a paramter type to Bind.

@override
List<Bind> get binds => [
  Bind<ILocalStorage>((i) => LocalStorageSharePreferences()),
];

Then, on your test file, you import flutter_modular_test and provide your mocked repository in the initModule as a replacement of your concrete repository:

import 'package:flutter_modular/flutter_modular_test.dart';
import 'package:flutter_test/flutter_test.dart';

main() {
  test('change bind', () {
    initModule(AppModule(), changeBinds: [
      Bind<ILocalStorage>((i) => LocalMock()),
    ]);
    expect(Modular.get<ILocalStorage>(), isA<LocalMock>());
  });
}

Modular test helper #

Before write in your test file, if you want to improve readability you might to import flutter_modular_test and define your mocked module using IModularTest and override his methods to create a mock, similar as ChildModule, when writing your tests:

The first step is write a class like that:


import 'package:flutter_modular/flutter_modular.dart';
import 'package:flutter_modular/flutter_modular_test.dart';

class InitAppModuleHelper extends IModularTest {

  final ModularTestType modularTestType;
  IModularTest({this.modularTestType: ModularTestType.resetModule});

  @override
  List<Bind> get binds => [
        Bind<ILocalStorage>((i) => LocalStorageSharePreference()),
      ];

  @override
  ChildModule get module => AppModule();
  

  @override
  IModularTest get modulardependency => null;

}

The right way to use is writing as least one of that per module, its important to remember to put the modular dependecies in modularDependency. its useful because when you load this module for testing, all related modules will be load together. In this case the AppModule is the root module and it hasn`t dependency.

Load Modular helper on tests #

  1. By default when use IModularTest each InitAppModuleHelper().load() will clean and rebuid the modular and his injects, this is fine to do each test block independent and make more easy to write modular tests without noise.
import 'package:flutter_modular/flutter_modular_test.dart';
import 'package:flutter_test/flutter_test.dart';

main() {
  test('change bind', () {
    InitAppModuleHelper().load();
    //do something
  });
  test('change bind', () {
    InitAppModuleHelper().load();
    //do something
  });
}
  1. To keep previous modular and its injects you can pass the param modularTestType.

NOTE: With modularTestType.keepModulesOnMemory, it won't clean the modules that already have been loaded. (It doesn't call Modular.removeModule())

import 'package:flutter_modular/flutter_modular_test.dart';
import 'package:flutter_test/flutter_test.dart';

main() {
  
  test('test1', () {
    InitAppModuleHelper().load();
  });

  test('test2', () {
    InitAppModuleHelper(
      modularTestType: ModularTestType.keepModulesOnMemory
      ).load();
      // Keep the same injects loaded by test1
  });
}
  1. Changing the binds when load() the module like initModule().

NOTE: It also can change binds of another modules that are its dependencies until find the MainModule.

Ex: When you have a tree like InitAppModuleHelper <- InitHomeModuleHelper, when you call InitHomeModuleHelper.load(changeBinds:[<newBinds>]) it will be able to change binds on HomeModule and AppModule. Because of that you only need one changeBinds array and it can make all the changes for you, see it on section: Create helper for a child module.

import 'package:flutter_modular/flutter_modular_test.dart';
import 'package:flutter_test/flutter_test.dart';

main() {
  
  test('test1', () {
    InitAppModuleHelper().load(changeBinds:[
      Bind<ILocalStorage>((i) => LocalStorageHive())

    ]);
  });
  
}

Create helper for a child module #

Remember you only need to call the most deeper IModularTest and it can load all dependency modules you have added on your mock definition, like the next example:

The first step is define a IModularTest to another module, pay attention that the HomeModule is a child of AppModule, because of that you need to put the AppModule on modularDependency.

import 'package:flutter_modular/flutter_modular_test.dart';
import 'package:flutter_modular/src/interfaces/child_module.dart';
import 'package:flutter_modular/src/inject/bind.dart';
import 'package:flutter_modular/flutter_modular.dart';

import '../../app_module_test_modular.dart';
import 'home_module.dart';

class InitHomeModuleHelper extends IModularTest {

  @override
  List<Bind> get binds => [];

  @override
  ChildModule get module => HomeModule();
  
  @override
  IModularTest get modulardependency => InitAppModuleHelper();

}

Now we can init the HomeModule and all his dependencies just by typing InitHomeModuleHelper().load() on your test_file. It doesn't matter how deep is your module, all dependencies are recursively loaded in a batch, you only need to create a IModuleTest for each one and put your dependencies correctly and it will work fine.

import 'package:flutter_modular/flutter_modular_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'app/modules/home/home_module_test_modular.dart';
main() {
  test('change bind', () {
    InitHomeModuleHelper().load();
    //do something
  });
  test('change bind', () {
    InitHomeModuleHelper().load();
    //do something
  });
}

Mocking with mockito #

  1. Add the mock into the binds list on your IModularTest helper, if you dont need to change during the tests.
import 'package:flutter_modular/flutter_modular_test.dart';
import 'package:flutter_modular/src/interfaces/child_module.dart';
import 'package:flutter_modular/src/inject/bind.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:mockito/mockito.dart';

import '../../app_module_test_modular.dart';
import 'home_module.dart';

class LocalStorageMock extends Mock implements ILocalStorage {}

class InitHomeModuleHelper extends IModularTest {

  @override
  List<Bind> get binds => [
    Bind<ILocalStorage>((i) => LocalStorageMock()),
  ];

  @override
  ChildModule get module => HomeModule();
  
  @override
  IModularTest get modulardependency => InitAppModuleHelper();
  
}


  1. Get the instance using Modular.get() and change the behavior as you need in the middle of the test:
import 'package:flutter_modular/flutter_modular.dart';
import 'package:flutter_modular/flutter_modular_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

import 'app/modules/home/home_module_test_modular.dart';

class LocalStorageMock extends Mock implements ILocalStorage {}

main() {

  LocalStorageMock localStorageMock = LocalStorageMock();

  group("IModuleTest", () {
    setUp(() {
      InitAppModuleHelper().load(changeBinds:[
        
        Bind<ILocalStorage>((i) => localStorageMock),

      ]);
      ILocalStorage iLocalStorage = Modular.get<ILocalStorage>();
    });

    test('change bind', () {
      when(localStorageMock.doSomething()).thenReturn("Hello");
      iLocalStorage.doSomething();
      //return Hello

      when(localStorageMock.doSomething()).thenReturn("World");
      iLocalStorage.doSomething();
      //return World
    });

  });

}

Mock the navigation system #

We though it would be interesting to provide a native way to mock the navigation system when used with Modular.to and Modular.link. To do this, you may just implement IModularNavigator and pass your implementation to Modular.navigatorDelegate.

// Modular.to and Modular.link will be called MyNavigatorMock implements!
Modular.navigatorDelegate = MyNavigatorMock();

DebugMode #

By default, Modular prints a lot of debug info in the console. You may disable this by disabling debugMode:

Modular.debugMode = false;

Roadmap #

This is our current roadmap. Please, feel free to request additions/changes.

FeatureProgress
DI by Module
Routes by Module
Widget Consume for ChangeNotifier
Auto-dispose
Integration with flutter_bloc
Integration with mobx
Multiple routes
Pass arguments by route
Pass url parameters per route
Route Transition Animation

Features and bugs #

Please send feature requests and bugs at the issue tracker.

This README was created based on templates made available by Stagehand under a BSD-style license.

Contributors ✨ #

Our thanks goes out to all these wonderful people (emoji key):


Jacob Moura

🚧 💻 👀

Vilson Blanco Dauinheimer

💻 📖 👀

Pedro Massango

💻 📖 🤔

Kelven I. B. Galvão

📖 🌍

David Araujo

💻

Alvaro Vasconcelos

💻

André de Almeida

💻 📖

Alberto Monteiro

💻 ⚠️

Guilherme Nepomuceno de Carvalho

💻

Eduardo Lúcio

💻

Ascênio

💻 📖

Wemerson Couto Guimarães

💻

Alan D. Cabrera

💻

Jean Lucas

💻

Moacir Schmidt

📖

Felipe Marra

📖 🌍

Adem Kouki

📖

Gabriel Sávio - Flutterando

💻

Tiagosito

💻

Mateus Felipe C. C. Pinto

🌍 📖

Pierre Grimaud

📖

Toshi Ossada

📖 💻

Allan L.

💻 📖 ⚠️

This project follows the all-contributors specification. Contributions of any kind are welcome!

[1.2.7+1] - 26 Jun 2020 #

  • Fix route error issue #118
  • Added WillPopScope in RouterOutlet

[1.2.6+1] - 23 Jun 2020 #

  • Direct call Inject
@override
  List<Bind> get binds => [
        Bind((i) => HomeBloc(repository: i(), appBloc: i())),
        Bind((i) => HomeRepository(dio: i())),
        Bind((i) => Dio()),
      ];

Use i() instead i.get()

[1.2.5+1] - 26 May 2020 #

  • Fix Modular.link bug
  • Smooth Animation Navigator: 56% faster navigation animations

[1.2.4] - 23 May 2020 #

  • Welcome Navigator API 2.0!!!
  • Added push, pushReplacement in Modular.to and Modular.link;
  • Added Modular.navigatorDelegate for tests mocks. Just implements IModularNavigator.
//Modular.to and Modular.link will be called MyNavigatorMock implements!
Modular.navigatorDelegate = MyNavigatorMock();

[1.2.3] - 19 May 2020 #

  • Health suggestions
  • Added Contributors in README
  • Fix RouterOutlet
  • Fix Modular.link

[1.2.1] - 15 May 2020 #

  • Fix bugs
  • new Modular.link for Navigation in Current Module;
//Modules home>product
Modular.to.pushNamed('/home/product/list');
Modular.to.pushNamed('/home/product/detail/:id');

//into product module, use Modular.link and navigate between routes of current Module (Product)

Modular.link.pushNamed('/list');
Modular.link.pushNamed('/detail/:id');

Use Modular.to for literal paths or Modular.link for routes in current module.

  • Finally, use Modular.to.path (or Modular.link.path) if you want see the "Current Route Path".

[1.1.2] - 13 Apr 2020 #

  • Fix bugs

[1.1.1] - 07 Apr 2020 #

  • Added showDialog
Modular.to.showDialog(
  barrierDismissible: false,
  builder: (_) => AlertDialog(),
);

[1.0.0] - 24 Mar 2020 #

  • Release!!!

[0.5.6] - 13 Mar 2020 #

  • Added keepAlive flag in RouterOutlet.

[0.5.5] - 08 Mar 2020 #

  • Fix StackOverflow error
  • Fix RouteGuard
  • Fix Transitions Animation
  • PREVIEW: RouterOutlet Widget Use Navigation in BottomBarTab or Drawer
PageView(
  controller: controller
  children: [
    RouterOutlet(
      module: Tab1Module()
    ),
    RouterOutlet(
      module: Tab2Module()
    ),
    RouterOutlet(
      module: Tab3Module()
    ),
  ]
),

NOTE: Navigation is only Navigator.of (context) and only uses the module's literal route path.

[0.5.3] - 05 Mar 2020 #

  • Prevent StackOverflow

[0.5.2] - 20 Feb 2020 #

  • Prevent StackOverflow

[0.5.1] - 15 Feb 2020 #

  • fix #52

[0.5.0] - 13 Feb 2020 #

  • Added router generic type
 @override
  List<Router> get routers => [
    //type router with return type
    Router<String>('/event', child: (_, args) => EventPage()),
  ]

Now you can type your pushNamed and pop

 String value = await Modular.to.pushNamed<String>();
 //and
 Modular.to.pop('My String');

[0.4.7] - 9 Feb 2020 #

  • Added Custom Transition.
  • Added Modular.args (get route params in Controller).
  • (PREVIEW) RouterGuard in child routes.
  • Fix error in WidgetTests
  • Added Print routers in debugMode

[0.4.5] - 7 Feb 2020 #

  • Added not lazy Objects
@override
  List<Bind> get binds => [
        Bind((i) => OtherWidgetNotLazy(), lazy: false),
      ];

[0.4.4] - 6 Feb 2020 #

  • fix RouterGuards
  • Added Modular.debugMode = false;
  • Improve documentations
  • Fix Error in initalRoute

[0.4.3] - 1 Feb 2020 #

  • fix RouterGuards
  • Added Modular.debugMode = false;

[0.4.2] - 1 Feb 2020 #

  • fix routerGuards
  • fix tests

[0.4.1] - 30 Jan 2020 #

  • Internal Inject Interface reference

[0.4.0] - 28 Jan 2020 #

  • added Modular.dispose();
  • ModularState
  • Removed InjectMixin

[0.3.5+1] - 26 Jan 2020 #

  • fix module widget
  • fix inject error

[0.3.3+1] - 18 Jan 2020 #

  • Modular is BETA!!!
  • You can now control navigation without the context!
  • Added Modular.to and replace Navigator.of(context)
  • Added Modular.get and replace AppModule.to.get
  • Added flag "singleton" in Bind injection
  • Fix Router Guard

[0.1.8] - 08 Jan 2020 #

  • fix test errors (initModule)
  • Added modularException

[0.1.4] - 24 Dec 2019 #

  • fix #7 and more

[0.1.3] - 17 Dec 2019 #

  • Route Settings, RemoveUntil fix #11

[0.1.1] - 17 Dec 2019 #

  • Fix tests

[0.1.0+1] - 16 Dec 2019 #

  • Added Route Transitions.
  • Change ModularWidget to ModularApp.

[0.0.10] - 14 Dec 2019 #

  • Added logo

[0.0.8] - 13 Dec 2019 #

  • Route Guard

[0.0.7] - 10 Dec 2019 #

  • Dynamic Router
  • Added Doc Translation
  • Change BrowserModule to MainModule
  • Change CommonModule to ChildModule
  • Corrigido erro de blink na primeira rota
  • fix routes param

[0.0.1] - 8 Dec 2019 #

  • First Release

example/lib/main.dart

import 'package:example/app/app_module.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_modular/flutter_modular.dart';

void main() => runApp(ModularApp(module: AppModule()));

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_modular: ^1.2.7+1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_modular/flutter_modular.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
96
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
98
Learn more about scoring.

We analyzed this package on Jul 11, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

Because:

  • flutter_modular that is a package requiring null.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.3.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8 1.2.2
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
effective_dart 1.2.2
flutter_test
mockito ^4.1.1