🔒 Guards
Os Guards no Modugo permitem controlar o acesso a rotas com base em condições lógicas, como autenticação, papéis de usuário ou verificações de sessão. Eles são executados antes do carregamento da rota e podem redirecionar o usuário conforme necessário.
🔹 Interface IGuard
Todo guard implementa a interface IGuard:
final class AuthGuard implements IGuard {
final AuthRepository _repository;
AuthGuard({required AuthRepository repository}) : _repository = repository;
@override
FutureOr<String?> call(BuildContext context, GoRouterState state) async {
final isAuthenticated = await _repository.isAuthenticated();
// Retorna null para permitir acesso
// Retorna um path para redirecionar
return isAuthenticated ? null : '/login';
}
}
Regras
- Retornar
null→ permite o acesso normalmente. - Retornar uma
String→ redireciona o usuário para esse caminho. - Guards suportam operações assíncronas (
FutureOr).
🔹 Aplicando Guards em Rotas
Guards podem ser aplicados diretamente em ChildRoute:
route(
'/dashboard',
child: (_, _) => const DashboardPage(),
guards: [AuthGuard(repository: i.get<AuthRepository>())],
);
Ou usando a sintaxe tradicional:
ChildRoute(
path: '/dashboard',
child: (_, _) => const DashboardPage(),
guards: [AuthGuard(repository: i.get<AuthRepository>())],
);
🔹 Propagando Guards com propagateGuards
Quando você deseja aplicar um guard a todas as rotas de um módulo (incluindo submódulos), use propagateGuards:
final class AppModule extends Module {
@override
List<IRoute> routes() => propagateGuards(
guards: [AuthGuard(repository: i.get<AuthRepository>())],
routes: [
module('/home', HomeModule()),
module('/profile', ProfileModule()),
],
);
}
Todas as rotas internas de HomeModule e ProfileModule herdam automaticamente o AuthGuard.
Como funciona internamente
O propagateGuards percorre recursivamente as rotas e:
- ChildRoute → os guards do pai são prepended aos guards da rota (executados primeiro).
- ModuleRoute → o módulo é envolvido em um
GuardModuleDecoratorque injeta os guards em todas as suas rotas. - ShellModuleRoute → os guards são propagados para todas as rotas filhas.
- StatefulShellModuleRoute → os guards são propagados para todas as branches.
🔹 Ordem de Execução
1. Guards propagados (do pai) → executados primeiro
2. Guards da rota atual → executados depois
3. Se todos retornarem null → navegação prossegue
4. Se algum retornar String → redirect para esse caminho
5. Redirect da rota (se definido) → avaliado após os guards
🔹 Tipos Comuns de Guard
Guard de Autenticação
final class AuthGuard implements IGuard {
@override
FutureOr<String?> call(BuildContext context, GoRouterState state) async {
final isLoggedIn = await checkAuth();
return isLoggedIn ? null : '/login';
}
}
Guard de Role/Permissão
final class AdminGuard implements IGuard {
@override
FutureOr<String?> call(BuildContext context, GoRouterState state) async {
final user = context.read<UserService>().currentUser;
return user.isAdmin ? null : '/unauthorized';
}
}
Guard com Side-Effect (sem redirect)
final class AnalyticsGuard implements IGuard {
@override
FutureOr<String?> call(BuildContext context, GoRouterState state) {
Analytics.logPageView(state.uri.path);
return null; // sempre permite acesso
}
}
🔹 Exemplo Completo
final class AppModule extends Module {
@override
void binds() {
i
..registerSingleton<AuthRepository>(AuthRepositoryImpl())
..registerSingleton<UserService>(UserService());
}
@override
List<IRoute> routes() => [
// Rota publica (sem guard)
route('/login', child: (_, _) => const LoginPage()),
// Rotas protegidas
...propagateGuards(
guards: [AuthGuard(repository: i.get<AuthRepository>())],
routes: [
route('/', child: (_, _) => const HomePage()),
module('/profile', ProfileModule()),
module('/admin', AdminModule()), // AdminModule pode ter seus proprios guards
],
),
];
}