modugo 3.3.3
modugo: ^3.3.3 copied to clipboard
Modular Routing and Dependency Injection for Flutter with GetIt and GoRouter.
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 #
-
ChildRoute
ModuleRoute
ShellModuleRoute
StatefulShellModuleRoute
AliasRoute
-
📜 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:
/cart
e/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
AliasRoute
só funciona paraChildRoute
.- Ele não pode apontar para
ModuleRoute
ouShellModuleRoute
. - 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
ChildRoute
existente 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
BuildContext
vá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 ©