go_router_guards 2.0.0+2
go_router_guards: ^2.0.0+2 copied to clipboard
A flexible and extensible guard system for Go Router that allows you to chain multiple guards together for route protection.
Changelog #
2.0.0+2 - 2025-10-20 #
Changed #
- Updated the README.md file to reflect the new API.
- Updated the CHANGELOG.md file to reflect the new changes.
- Updated the example code to reflect the new API.
- Updated the test code to reflect the new API.
- Updated the documentation to reflect the new API.
2.0.0+1 - 2025-10-19 #
Added #
-
Middleware-Style Navigation Control: Complete rewrite with resolver pattern
- NavigationResolver: New class providing
next(),redirect(path), andblock()methods - GuardResult: Sealed class with
AllowResultandRedirectResultfor type-safe guard outcomes - Clearer mental model aligned with Express.js-style middleware
- NavigationResolver: New class providing
-
Guard Composition Functions: New top-level functions for combining guards
guardAll(guards)- Requires all guards to pass (AND logic)guardAnyOf(guards, fallbackRedirect)- Requires at least one guard to pass (OR logic)guardOneOf(guards, fallbackRedirect)- Requires exactly one guard to pass (XOR logic)
-
Ergonomic List Extensions: Fluent API for guard composition
[guard1, guard2].all()- Combine guards with AND logic[guard1, guard2].anyOf(fallbackRedirect: '/login')- Combine with OR logic[guard1, guard2].oneOf(fallbackRedirect: '/error')- Combine with XOR logic
-
Direct Redirect Extensions: Convert guard collections to GoRouter redirects
guards.redirectAll()- Build redirect function from multiple guardsguards.redirectAnyOf(fallbackRedirect: '/login')- Build OR redirectguards.redirectOneOf(fallbackRedirect: '/error')- Build XOR redirect
-
Conditional Guards: Simple condition-based guards without boilerplate
guardWhen(condition, redirect)- Guard when condition is trueguardUnless(condition, redirect)- Guard when condition is false- Async condition support with futures
-
Factory Constructors: Convenient guard creation methods
RouteGuard.allow()- Always allows navigationRouteGuard.redirectTo(path)- Always redirects to pathRouteGuard.from(callback)- Create guard from callback function
-
Enhanced Error Handling
- Empty guard list validation for
anyOfandoneOf(throwsArgumentError) RouterNotMountedExceptionfor better error messages- Defensive null-safety throughout
- Empty guard list validation for
-
Route Guard Utils: Helper functions for common patterns
- Utility functions for creating reusable guard compositions
Changed #
-
API Redesign: Expression-based system replaced with middleware-style resolver pattern
- Guards now implement
onNavigation(resolver, context, state)instead ofredirect(context, state) - More intuitive control flow with explicit
resolver.next()calls - Better separation of concerns between guard logic and navigation control
- Guards now implement
-
Simplified Mental Model: Removed complex expression tree in favor of straightforward function composition
- No more
Guards.guard()wrapper - use guards directly - Clearer semantics: "all must pass" vs "at least one must pass"
- Reduced API surface area for easier learning
- No more
-
Improved Type Safety: Sealed
GuardResultclass ensures exhaustive pattern matching- Compile-time guarantees that all result types are handled
- No more nullable string returns
-
Better Short-Circuit Logic: Optimized execution with early returns
guardAllstops at first redirectguardAnyOfstops at first allowguardOneOfoptimally handles exactly-one semantics
Removed #
- Expression-Based System: Removed
GuardExpressionclass hierarchyGuards.guard()- No longer needed, use guards directlyGuards.all(),Guards.anyOf(),Guards.oneOf()- Replaced with top-level functionsExecutionOrderenum - Simplified to always use optimal execution order
Breaking Changes #
-
RouteGuard Interface Changed
// Before (v2.0) class MyGuard implements RouteGuard { @override FutureOr<String?> redirect(BuildContext context, GoRouterState state) { if (condition) return '/redirect'; return null; } } // After (v2.1) class MyGuard extends RouteGuard { @override FutureOr<void> onNavigation( NavigationResolver resolver, BuildContext context, GoRouterState state, ) { if (condition) { resolver.redirect('/redirect'); } else { resolver.next(); } } } -
Guard Composition Syntax
// Before (v2.0) Guards.all([ Guards.guard(AuthGuard()), Guards.guard(RoleGuard(['admin'])), ]) // After (v2.1) guardAll([ AuthGuard(), RoleGuard(['admin']), ]) // or [AuthGuard(), RoleGuard(['admin'])].all() -
GuardedRoute Mixin
// Before (v2.0) @override GuardExpression get guards => Guards.all([...]); // After (v2.1) @override RouteGuard get guard => guardAll([...]);
Migration Guide #
-
Update Guard Implementation
- Change from
redirect()returningString?toonNavigation()usingNavigationResolver - Replace
return nullwithresolver.next() - Replace
return '/path'withresolver.redirect('/path')
- Change from
-
Update Guard Composition
- Replace
Guards.all([Guards.guard(g1), ...])withguardAll([g1, ...])or[g1, ...].all() - Replace
Guards.anyOf([Guards.guard(g1), ...])withguardAnyOf([g1, ...])or[g1, ...].anyOf() - Replace
Guards.oneOf([Guards.guard(g1), ...])withguardOneOf([g1, ...])or[g1, ...].oneOf()
- Replace
-
Update Route Mixins
- Change
GuardExpression get guardstoRouteGuard get guard(singular) - Update guard composition to use new functions
- Change
-
Update Redirect Builders
- Use
.toRedirect()on anyRouteGuardinstance - Or use
.redirectAll(),.redirectAnyOf(),.redirectOneOf()on guard collections
- Use
Examples #
// Middleware-style guard
class AuthGuard extends RouteGuard {
@override
void onNavigation(
NavigationResolver resolver,
BuildContext context,
GoRouterState state,
) async {
final isAuth = await checkAuth();
if (isAuth) {
resolver.next();
} else {
resolver.redirect('/login');
}
}
}
// Ergonomic composition with extensions
final adminGuard = [
AuthGuard(),
RoleGuard(['admin']),
].all();
// Direct redirect builder
GoRoute(
path: '/admin',
redirect: [AuthGuard(), RoleGuard(['admin'])].redirectAll(),
builder: (context, state) => AdminScreen(),
)
// Conditional guards
final featureGuard = guardWhen(
() => featureFlags.isEnabled('premium'),
'/upgrade',
);
// Type-safe routes with guards
@TypedGoRoute<AdminRoute>(path: '/admin')
class AdminRoute extends GoRouteData with GuardedRoute {
@override
RouteGuard get guard => [
AuthGuard(),
RoleGuard(['admin']),
].all();
@override
Widget build(BuildContext context, GoRouterState state) {
return AdminScreen();
}
}
1.0.0+1 - 2025-07-28 #
Added #
-
Expression-Based Guard System: Complete rewrite from chain-based to expression-based architecture
- GuardExpression: Abstract base class for all guard expressions
- Multi-Expression Operators:
all,anyOf,oneOffor handling multiple guards efficiently - Complex Boolean Logic: Support for arbitrary boolean expressions like
(a & b) || (c & d) || e - Short-circuit Evaluation: Performance optimization that stops execution early when possible
-
ExecutionOrder Enum: Fine-grained control over guard execution
- leftToRight: Execute expressions in the order they are provided (default)
- rightToLeft: Execute expressions in reverse order
- parallel: Execute expressions simultaneously for better performance
-
Guards Utility Class: Fluent API for building guard expressions
Guards.guard()- Create guard expressions from RouteGuardGuards.all()- Create AND expressions for multiple guardsGuards.anyOf()- Create OR expressions for multiple guardsGuards.oneOf()- Create XOR expressions for multiple guardsGuards.allow()- Always allow access (for testing)
-
GuardedRoute Mixin: Seamless integration with Go Router type-safe routes
- Override
guardsgetter to define route protection - Automatic guard execution on route access
- Type-safe navigation throughout
- Override
-
Modular Package Architecture: Organized code into focused, maintainable files
core.dart- Fundamental interfaces and enumsexpressions.dart- Basic guard expression wrappermulti_operators.dart- Multi-expression logical operatorsutilities.dart- Fluent API and utility guardsroute.dart- Go Router integration
-
Robust Error Handling: Comprehensive input validation and safe parallel execution
- Empty list validation for multi-expression operators
- Safe parallel execution with
eagerError: false - BuildContext.mounted checks to prevent async context issues
-
Performance Optimizations: Multiple performance improvements
- Short-circuit evaluation in
And,Or,AndAll,OrAll, andXorAll - Parallel execution option for better performance
- Immutable design for better safety and performance
- Short-circuit evaluation in
Changed #
-
API Simplification: Removed confusing
Notoperator for cleaner mental model- Eliminated double-negative logic that was confusing for navigation
- Focused API on positive assertions that align with routing conventions
- Improved developer experience with more intuitive guard expressions
-
Package Structure: Completely reorganized from monolithic to modular architecture
- Split single large file into 6 focused, logical files
- Improved maintainability and code organization
- Better separation of concerns
Removed #
- GuardChain API: Completely removed the old chain-based API
GuardChainclass no longer exists- Simplified API by removing backward compatibility complexity
- Migration to expression-based system required
Breaking Changes #
- GuardChain API Removed: The old chain API has been completely removed
GuardChainclass no longer exists- Use the expression-based system with
Guardsutility instead - This simplifies the API and removes backward compatibility complexity
Migration Guide #
-
GuardChain Migration: Replace chain API with expression-based system
// Before (removed) GuardChain() .add(AuthenticationGuard()) .add(RoleGuard(['admin'])) // After (recommended) Guards.all([ Guards.guard(AuthenticationGuard()), Guards.guard(RoleGuard(['admin'])), ]) -
New Expression System: Use the new expression-based system for complex logic
// Complex logic: (auth & role) || admin Guards.anyOf([ Guards.all([ Guards.guard(AuthenticationGuard()), Guards.guard(RoleGuard(['admin'])), ]), Guards.guard(SuperAdminGuard()), ])
Examples #
// Type-safe route with complex guard logic
@TypedGoRoute<ProtectedRoute>(path: '/protected')
class ProtectedRoute extends GoRouteData with GuardedRoute {
const ProtectedRoute();
@override
GuardExpression get guards => Guards.anyOf([
Guards.all([
Guards.guard(AuthenticationGuard()),
Guards.guard(RoleGuard(['admin'])),
]),
Guards.guard(SuperAdminGuard()),
]);
@override
Widget build(BuildContext context, GoRouterState state) {
return const ProtectedScreen();
}
}
// Performance-optimized guards with parallel execution
class PerformanceOptimizedGuard extends GuardExpression {
const PerformanceOptimizedGuard() : super(executionOrder: ExecutionOrder.parallel);
@override
FutureOr<String?> execute(BuildContext context, GoRouterState state) async {
// Parallel execution for better performance
return null;
}
}
0.1.0+1 - 2025-07-28 #
Added #
-
RouteGuard Interface: Abstract base class for creating custom route guards
- Supports both synchronous and asynchronous guard operations
- Returns
nullto allow access or a redirect path to deny access - Full type safety with Go Router integration
-
GuardChain System: Chainable guard system for executing multiple guards in sequence
- Add guards individually with
add()method - Add multiple guards at once with
addAll()method - Execute guards in order with early termination on first redirect
- Chain management with
clear(),length,isEmpty, andisNotEmptyproperties
- Add guards individually with
-
GuardedRoute Mixin: Mixin for applying guards to GoRouteData classes
- Easy integration with existing Go Router type-safe routes
- Override
guardsgetter to define route protection - Automatic guard execution on route access
-
Type-Safe Navigation: Full integration with Go Router's type-safe routing
- Use
Route().locationfor type-safe redirects - Compile-time safety for route paths
- Integration with
@TypedGoRouteannotations
- Use
-
Comprehensive Testing: Complete test coverage for all components
- Unit tests for RouteGuard interface
- GuardChain execution and management tests
- GuardedRoute integration tests
- Mock implementations for testing
-
Documentation: Extensive documentation and examples
- Quick start guide with type-safe route examples
- Best practices for guard implementation
- Migration guide from manual guards
- Testing examples for guards and routes
Features #
- Flexible Guard System: Create guards for authentication, permissions, app state, or any custom conditions
- Chainable Guards: Combine multiple guards in a single route for complex protection logic
- Performance Optimized: Guards execute in order with early termination for optimal performance
- Error Handling: Robust error handling with graceful fallbacks
- Extensible Architecture: Easy to extend and customize for specific use cases
Technical Details #
- SDK Requirements: Flutter >=3.19.0, Dart SDK ^3.5.0
- Dependencies: go_router ^16.0.0
- Development Tools: build_runner, go_router_builder, mocktail, very_good_analysis
- License: MIT License