Modugo
Modugo é um sistema modular para Flutter inspirado em Flutter Modular e Go Router Modular. Ele organiza sua aplicação em módulos, rotas e injeção de dependências de forma clara e escalável. Diferente de outros frameworks, o Modugo não gerencia descarte automático de dependências.
📖 Sumário
-
ChildRouteModuleRouteShellModuleRouteStatefulShellModuleRouteAliasRoute
-
📜 Licença
🚀 Visão Geral
- Usa GoRouter para navegação.
- Usa GetIt para injeção de dependências.
- Dependências são registradas uma única vez ao iniciar.
- Não há descarte automático — dependências vivem até o app encerrar.
- Projetado para fornecer arquitetura modular desacoplada.
⚠️ Atenção: Diferente das versões <3.x, o Modugo não descarta dependências automaticamente.
📦 Instalação
dependencies:
modugo: ^x.x.x
🏗️ Estrutura de Projeto
/lib
/modules
/home
home_page.dart
home_module.dart
/profile
profile_page.dart
profile_module.dart
/chat
chat_page.dart
chat_module.dart
app_module.dart
app_widget.dart
main.dart
▶️ Primeiros Passos
main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Modugo.configure(module: AppModule(), initialRoute: '/');
runApp(
ModugoLoaderWidget(
loading: const CircularProgressIndicator(),
builder: (_) => const AppWidget(),
),
);
}
🧭 Navegação
🔹 ChildRoute
ChildRoute(
path: '/home',
child: (_, _) => const HomePage(),
)
🔹 ModuleRoute
ModuleRoute(
path: '/profile',
module: ProfileModule(),
)
🔹 ShellModuleRoute
Útil para criar áreas de navegação em parte da tela, como menus ou abas.
ShellModuleRoute(
builder: (context, state, child) => Scaffold(body: child),
routes: [
ChildRoute(path: '/user', child: (_, _) => const UserPage()),
ChildRoute(path: '/config', child: (_, _) => const ConfigPage()),
],
)
🔹 StatefulShellModuleRoute
Ideal para apps com BottomNavigationBar ou abas preservando estado.
StatefulShellModuleRoute(
builder: (context, state, shell) => BottomBarWidget(shell: shell),
routes: [
ModuleRoute(path: '/', module: HomeModule()),
ModuleRoute(path: '/profile', module: ProfileModule()),
],
)
🔹 AliasRoute
O AliasRoute é um tipo especial de rota que funciona como um apelido (alias) para uma ChildRoute já existente. Ele resolve o problema de URLs alternativas para a mesma tela, sem precisar duplicar lógica ou cair nos loops comuns de RedirectRoute.
📌 Quando usar?
- Para manter compatibilidade retroativa com URLs antigas.
- Para expor uma mesma tela em múltiplos caminhos semânticos (ex:
/carte/order).
✅ Exemplo simples
ChildRoute(
path: '/order/:id',
child: (_, state) => OrderPage(id: state.pathParameters['id']!),
),
AliasRoute(
from: '/cart/:id',
to: '/order/:id',
),
➡️ Nesse caso, tanto /order/123 quanto /cart/123 vão renderizar a mesma tela OrderPage.
⚠️ Limitações
-
O
AliasRoutesó funciona paraChildRoute.- Ele não pode apontar para
ModuleRouteouShellModuleRoute. - Essa limitação é intencional, pois módulos inteiros ou shells representam estruturas de navegação maiores e complexas.
- Ele não pode apontar para
-
O alias precisa apontar para uma
ChildRouteexistente dentro do mesmo módulo.-
Caso contrário, será lançado um erro em tempo de configuração:
Alias Route points to /cart/:id, but there is no corresponding Child Route.
-
-
Não há suporte a alias encadeados (ex: um alias apontando para outro alias).
🎯 Exemplo prático
final class ShopModule extends Module {
@override
List<IRoute> routes() => [
// rota canônica
ChildRoute(
path: '/product/:id',
child: (_, state) => ProductPage(id: state.pathParameters['id']!),
),
// rota alternativa (alias)
AliasRoute(
from: '/item/:id',
to: '/product/:id',
),
];
}
📊 Fluxo de matching:
graph TD
A[/item/42] --> B[AliasRoute /item/:id]
B --> C[ChildRoute /product/:id]
C --> D[ProductPage]
➡️ O usuário acessa /item/42, mas internamente o Modugo entrega o mesmo ProductPage de /product/42.
💡 Vantagens sobre RedirectRoute
- Evita loops infinitos comuns em redirecionamentos.
- Mantém o histórico de navegação intacto (não "teleporta" o usuário para outra URL, apenas resolve a rota).
🔒 Resumo: Use AliasRoute para apelidos de ChildRoute. Se precisar de comportamento mais avançado (como autenticação ou lógica condicional), continue usando guards (IGuard) ou ChildRoute com cuidado.
🔒 Guards e propagateGuards
Você pode proteger rotas com IGuard ou aplicar guardas de forma recursiva usando propagateGuards.
List<IRoute> routes() => propagateGuards(
guards: [AuthGuard()],
routes: [
ModuleRoute(path: '/', module: HomeModule()),
],
);
✅ Com isso, todos os filhos de HomeModule herdam automaticamente o guard.
📊 Fluxo de execução:
graph TD
A[ModuleRoute Pai] --> B[ChildRoute 1]
A --> C[ChildRoute 2]
A --> D[ModuleRoute Filho]
style A fill:#f96
style B fill:#bbf
style C fill:#bbf
style D fill:#bbf
🛠️ Injeção de Dependência
final class HomeModule extends Module {
@override
void binds() {
i
..registerSingleton<ServiceRepository>(ServiceRepository())
..registerLazySingleton<ApiClient>(ApiClient.new);
}
}
Acesse com:
final repo = i.get<ServiceRepository>();
Ou via contexto:
final repo = context.read<ServiceRepository>();
⏳ AfterLayoutMixin
Mixin para executar código após o primeiro layout do widget.
class MyScreen extends StatefulWidget {
const MyScreen({super.key});
@override
State<MyScreen> createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> with AfterLayoutMixin {
@override
Widget build(BuildContext context) {
return const Scaffold(body: Center(child: Text('Hello World')));
}
@override
Future<void> afterFirstLayout(BuildContext context) async {
debugPrint('Tela pronta!');
}
}
💡 Útil para:
- Carregar dados iniciais.
- Disparar animações.
- Abrir dialogs/snackbars com
BuildContextválido.
🔎 Regex e Matching
Use CompilerRoute para validar e extrair parâmetros:
final route = CompilerRoute('/user/:id');
route.match('/user/42'); // true
route.extract('/user/42'); // { id: "42" }
📡 Sistema de Eventos
Permite comunicação desacoplada entre módulos.
final class MyEvent {
final String message;
MyEvent(this.message);
}
EventChannel.on<MyEvent>((event) {
print(event.message);
});
EventChannel.emit(MyEvent('Olá Modugo!'));
📝 Logging e Diagnóstico
Modugo.configure(
module: AppModule(),
debugLogDiagnostics: true,
);
Exibe logs de injeção, navegação e erros.
📚 Documentação MkDocs Em desenvolvimento
Toda a documentação também está disponível em MkDocs para navegação mais amigável: 👉 Modugo Docs
Estrutura baseada em múltiplos tópicos (Rotas, Injeção, Guards, Eventos), permitindo leitura incremental.
🤝 Contribuições
Pull requests e sugestões são bem-vindos! 💜
📜 Licença
MIT ©