[go: up one dir, main page]

modugo 3.3.3 copy "modugo: ^3.3.3" to clipboard
modugo: ^3.3.3 copied to clipboard

Modular Routing and Dependency Injection for Flutter with GetIt and GoRouter.

Modugo Logo

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 #


🚀 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

  1. O AliasRoute só funciona para ChildRoute.

    • Ele não pode apontar para ModuleRoute ou ShellModuleRoute.
    • Essa limitação é intencional, pois módulos inteiros ou shells representam estruturas de navegação maiores e complexas.
  2. 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.
      
  3. 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 ©

11
likes
160
points
1.25k
downloads
screenshot

Publisher

unverified uploader

Weekly Downloads

Modular Routing and Dependency Injection for Flutter with GetIt and GoRouter.

Repository (GitHub)
View/report issues

Topics

#modugo #routing #injection #navigation #deep-linking

Documentation

API reference

License

MIT (license)

Dependencies

event_bus, flutter, get_it, go_router, talker_logger

More

Packages that depend on modugo