go_router_guards 0.1.0+1 copy "go_router_guards: ^0.1.0+1" to clipboard
go_router_guards: ^0.1.0+1 copied to clipboard

A flexible and extensible guard system for Go Router that allows you to chain multiple guards together for route protection.

go_router_guards #

A flexible and extensible guard system for Go Router that enables type-safe route protection with chainable guards.

ci coverage pub package style: very good analysis License: MIT

Quick Start #

Installation #

Add go_router_guards to your pubspec.yaml:

dependencies:
  go_router_guards: ^0.1.0+1

Type-Safe Routes with Guards #

Following VGV's routing best practices, use type-safe routes with guards:

import 'package:go_router_guards/go_router_guards.dart';

// Define type-safe routes
@TypedGoRoute<LoginRoute>(path: '/login')
class LoginRoute extends GoRouteData {
  const LoginRoute();

  @override
  Widget build(BuildContext context, GoRouterState state) {
    return const LoginScreen();
  }
}

@TypedGoRoute<ProtectedRoute>(path: '/protected')
class ProtectedRoute extends GoRouteData with GuardedRoute {
  const ProtectedRoute();

  @override
  GuardChain get guards => GuardChain()..add(AuthenticationGuard());

  @override
  Widget build(BuildContext context, GoRouterState state) {
    return const ProtectedScreen();
  }
}

// Create type-safe guards
class AuthenticationGuard implements RouteGuard {
  @override
  FutureOr<String?> redirect(BuildContext context, GoRouterState state) async {
    final authState = context.read<AuthCubit>().state;
    if (!authState.isAuthenticated) {
      return LoginRoute().location; // Type-safe navigation
    }
    return null;
  }
}

// Navigate using type-safe routes
ElevatedButton(
  onPressed: () => ProtectedRoute().go(context),
  child: const Text('Go to Protected Route'),
)

Core Features #

RouteGuard Interface #

Implement guards to protect your routes:

mixin RouteGuard {
  FutureOr<String?> redirect(BuildContext context, GoRouterState state);
}
  • Return null to allow access
  • Return a route location (e.g., LoginRoute().location) to redirect

GuardChain #

Chain multiple guards together:

GuardChain()
  ..add(AuthenticationGuard())
  ..add(RoleBasedGuard(['admin']))
  ..add(SubscriptionGuard())

GuardedRoute Mixin #

Add guard functionality to your route classes:

class AdminRoute extends GoRouteData with GuardedRoute {
  const AdminRoute();

  @override
  GuardChain get guards => GuardChain()
    ..add(AuthenticationGuard())
    ..add(RoleBasedGuard(['admin']));

  @override
  Widget build(BuildContext context, GoRouterState state) {
    return const AdminScreen();
  }
}

Best Practices #

1. Use Type-Safe Navigation #

Always use type-safe routes for navigation:

// ✅ Good - Type-safe
context.go(ProtectedRoute().location);
ProtectedRoute().go(context);

// ❌ Bad - Hardcoded paths
context.go('/protected');

2. Chain Guards by Performance #

Order guards from fastest to slowest:

GuardChain()
  ..add(AppInitializationGuard()) // Fast check
  ..add(AuthenticationGuard())    // Medium check
  ..add(AsyncGuard())             // Slow async check

3. Create Reusable Guards #

Extract common guard logic:

class PremiumFeatureGuard implements RouteGuard {
  @override
  FutureOr<String?> redirect(BuildContext context, GoRouterState state) async {
    final userState = context.read<UserCubit>().state;
    if (!userState.hasPremiumAccess) {
      return UpgradeRoute().location;
    }
    return null;
  }
}

4. Handle Guard Failures Gracefully #

class RobustGuard implements RouteGuard {
  @override
  FutureOr<String?> redirect(BuildContext context, GoRouterState state) async {
    try {
      final userState = context.read<UserCubit>().state;
      if (!userState.isAuthenticated) {
        return LoginRoute().location;
      }
      return null;
    } catch (e) {
      return ErrorRoute().location;
    }
  }
}

Testing #

Unit Testing Guards #

test('AuthenticationGuard should redirect when not authenticated', () async {
  final guard = AuthenticationGuard();
  final mockContext = MockBuildContext();
  final mockState = MockGoRouterState();
  
  when(mockContext.read<AuthCubit>()).thenReturn(mockAuthCubit);
  when(mockAuthCubit.state).thenReturn(UnauthenticatedState());
  
  final result = await guard.redirect(mockContext, mockState);
  
  expect(result, LoginRoute().location);
});

Integration Testing #

testWidgets('Protected route should redirect to login', (tester) async {
  await tester.pumpWidget(MyApp());
  
  await tester.tap(find.text('Protected Route'));
  await tester.pumpAndSettle();
  
  expect(find.text('Login'), findsOneWidget);
});

Migration from Manual Guards #

Before (Manual Implementation) #

class ProtectedRoute extends GoRouteData {
  @override
  FutureOr<String?> redirect(BuildContext context, GoRouterState state) async {
    final authState = context.read<AuthCubit>().state;
    if (!authState.isAuthenticated) {
      return '/login'; // Hardcoded path
    }
    return null;
  }
}

After (Type-Safe Guards) #

class ProtectedRoute extends GoRouteData with GuardedRoute {
  @override
  GuardChain get guards => GuardChain()..add(AuthenticationGuard());

  @override
  Widget build(BuildContext context, GoRouterState state) {
    return const ProtectedScreen();
  }
}

class AuthenticationGuard implements RouteGuard {
  @override
  FutureOr<String?> redirect(BuildContext context, GoRouterState state) async {
    final authState = context.read<AuthCubit>().state;
    if (!authState.isAuthenticated) {
      return LoginRoute().location; // Type-safe navigation
    }
    return null;
  }
}

Contributing #

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass
  6. Submit a pull request

License #

This project is licensed under the MIT License - see the LICENSE file for details.

4
likes
150
points
2
downloads

Documentation

API reference

Publisher

verified publisheraquiles.dev

Weekly Downloads

A flexible and extensible guard system for Go Router that allows you to chain multiple guards together for route protection.

Repository (GitHub)
View/report issues
Contributing

License

MIT (license)

Dependencies

flutter, go_router

More

Packages that depend on go_router_guards