flutter
/

GetX Middleware: Route Guards & Interceptors

Last Sync: Today

On this page

15
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

GetX Middleware: Route Guards & Interceptors

What is GetX Middleware?

Middleware in GetX allows you to intercept route navigation events. You can use it to redirect users (e.g., authentication checks), log page views, modify route data, or perform any action before a route is displayed. Middleware is defined per route and can be chained, with each middleware having a priority to control execution order.

Real-World Use Cases

  • Authentication Guards – Redirect unauthenticated users to the login page when they try to access protected routes.
  • Onboarding Flow – Show onboarding screens only to first-time users, then redirect to main app.
  • Role-Based Access – Restrict admin pages to users with admin role, redirect others to a forbidden page.
  • Analytics & Logging – Automatically log every navigation event for tracking user behavior.
  • Deep Link Handling – Intercept deep links and transform them into appropriate app routes.
  • A/B Testing – Redirect certain users to different versions of a page based on a feature flag.

Creating a Basic Middleware

To create a middleware, extend GetMiddleware and override one or more of its methods.

DARTRead-only
1
class MyMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    // Return a new RouteSettings to redirect, or null to continue
    return null;
  }

  @override
  List<GetPage> onPageCalled(GetPage page) {
    // Called when the page is about to be built
    print('Navigating to ${page.name}');
    return [page];
  }
}

Key Methods

  • redirect(String? route) – Called before the route is built. Return a RouteSettings to redirect to another route, or null to proceed. Use this for guards.
  • onPageCalled(GetPage page) – Called after redirect but before the page is built. You can modify the page or perform side effects.
  • getPriority() – Override to set the execution order (lower numbers run first).
  • onPageDispose() – Called when the page is disposed (optional).

Priority

If multiple middlewares are assigned to a route, they execute in order of priority (lower priority runs first).

DARTRead-only
1
class LoggingMiddleware extends GetMiddleware {
  @override
  int? get priority => 0; // runs first
}

class AuthMiddleware extends GetMiddleware {
  @override
  int? get priority => 1; // runs after LoggingMiddleware
}

Authentication Guard Example

A common use case is protecting routes that require authentication.

DARTRead-only
1
class AuthMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    final auth = Get.find<AuthController>();
    if (!auth.isLoggedIn.value && route != '/login') {
      // Redirect to login and show a message
      Get.snackbar('Access Denied', 'Please login first');
      return RouteSettings(name: '/login');
    }
    return null;
  }
}

Logging Middleware

DARTRead-only
1
class LoggingMiddleware extends GetMiddleware {
  @override
  List<GetPage> onPageCalled(GetPage page) {
    print('Navigating to: ${page.name}');
    return [page];
  }
}

Registering Middleware with GetPage

Attach middleware to a route using the middlewares parameter. Order matters: they execute in the order they appear, but priority can override.

DARTRead-only
1
GetPage(
  name: '/profile',
  page: () => ProfilePage(),
  middlewares: [
    LoggingMiddleware(),
    AuthMiddleware(),
  ],
);

Global Middleware via InitialBinding

You can also register a middleware globally by adding it to GetMaterialApp using routingCallback or by using a custom navigator observer. For per‑route middleware, use the GetPage approach.

DARTRead-only
1
GetMaterialApp(
  routingCallback: (routing) {
    print('Current route: ${routing?.current}');
    // global guard logic
  },
  // ...
);

Redirect Logic

The redirect method receives the current route name. You can inspect it and decide to redirect to another route. Always return null if no redirection is needed.

DARTRead-only
1
class OnboardingMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    final hasSeenOnboarding = Get.find<SettingsController>().hasSeenOnboarding;
    if (!hasSeenOnboarding && route != '/onboarding') {
      return RouteSettings(name: '/onboarding');
    }
    return null;
  }
}

Comparison: GetX Middleware vs Alternatives

FeatureGetX MiddlewareFlutter NavigatorObserverCustom Solution
Route-specific guardsYes, per GetPageGlobal onlyYes, with manual checks
Redirect capabilitiesBuilt-inNot directly (must manipulate navigator)Manual
Priority orderingYes, via `priority`NoManual
Tight integration with GetX DIYesNoNo
BoilerplateMinimalModerateHigh

When NOT to Use Middleware

  • Simple apps with few routes – The overhead may not be justified; use simple checks in the UI.
  • Deep nested navigation with complex state – Middleware is best for top‑level guards; for fine‑grained checks, consider using controller logic.
  • Real-time UI updates based on auth – Middleware runs on navigation, not when auth state changes. Use Obx inside widgets for that.

Best Practices

  • Keep middleware focused – One middleware per concern (auth, logging, analytics).
  • Use priority to control order – Ensure logging runs before auth, etc.
  • Avoid heavy operations – Middleware runs before the page loads; keep it fast.
  • Use Get.find to access services – Inject controllers/services via GetX DI.
  • Return null when no redirection – Always return null to proceed normally.
  • Test middleware in isolation – Verify that redirects happen under the right conditions.

Common Mistakes

  • ❌ Forgetting to return null – Causes the route to be blocked. ✅ Always return null unless you want to redirect.
  • ❌ Using middleware without registering in GetPage – Won't execute. ✅ Attach middleware to the route definition.
  • ❌ Calling Get.to inside middleware – Can cause infinite loops. ✅ Use return RouteSettings(...) for redirection.
  • ❌ Not handling async operations – Middleware methods are synchronous; use a controller to hold state and react.

Conclusion

GetX middleware provides a clean, powerful way to intercept and control navigation. Whether you're implementing authentication, analytics, or simple logging, middleware helps keep your code organized and your routes secure.

Try it yourself

import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Middleware Demo',
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => HomePage()),
        GetPage(
          name: '/protected',
          page: () => ProtectedPage(),
          middlewares: [AuthMiddleware()],
        ),
        GetPage(name: '/login', page: () => LoginPage()),
      ],
    );
  }
}

// Simple auth controller
class AuthController extends GetxController {
  var isLoggedIn = false.obs;
  void login() => isLoggedIn.value = true;
  void logout() => isLoggedIn.value = false;
}

// Middleware that protects routes
class AuthMiddleware extends GetMiddleware {
  @override
  RouteSettings? redirect(String? route) {
    final auth = Get.find<AuthController>();
    if (!auth.isLoggedIn.value) {
      Get.snackbar('Access Denied', 'Please login first');
      return RouteSettings(name: '/login');
    }
    return null;
  }
}

class HomePage extends StatelessWidget {
  final auth = Get.put(AuthController());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Obx(() => Text('Logged in: ${auth.isLoggedIn.value}')),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => auth.login(),
              child: Text('Login'),
            ),
            ElevatedButton(
              onPressed: () => auth.logout(),
              child: Text('Logout'),
            ),
            ElevatedButton(
              onPressed: () => Get.toNamed('/protected'),
              child: Text('Go to Protected Page'),
            ),
          ],
        ),
      ),
    );
  }
}

class ProtectedPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Protected')),
      body: Center(
        child: Text('You have access to this protected page'),
      ),
    );
  }
}

class LoginPage extends StatelessWidget {
  final auth = Get.find<AuthController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Login')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            auth.login();
            Get.offAllNamed('/');
          },
          child: Text('Login'),
        ),
      ),
    );
  }
}

Test Your Knowledge

Q1
of 3

Which method should you override in GetMiddleware to redirect a route?

A
onPageCalled
B
redirect
C
getPriority
D
onPageDispose
Q2
of 3

How do you attach middleware to a specific route?

A
Add it to GetMaterialApp
B
Use the middlewares parameter in GetPage
C
Call Get.useMiddleware()
D
Register it in a binding
Q3
of 3

What does a middleware's `redirect` method return to allow navigation to proceed?

A
true
B
false
C
null
D
RouteSettings.current

Frequently Asked Questions

Can I have multiple middlewares on one route?

Yes, they execute in the order of priority (lowest first). You can also set priorities to control the order.

How do I pass data from middleware to the page?

You can modify page.arguments or store data in a controller that the page can access via Get.find.

Does middleware run for every navigation?

Yes, for every navigation to a route that has that middleware attached. If a route does not have the middleware in its middlewares list, it won't be triggered.

Can I use middleware for deep links?

Yes, the middleware receives the route name and can redirect accordingly. It's a good place to transform deep links into your app's internal routing.

What's the difference between `redirect` and `onPageCalled`?

redirect is for changing the route before the page is built; onPageCalled is for side effects like logging, and you can also modify the page object.

Can middleware access the previous route?

Not directly in the redirect method. You can use a service that tracks navigation history or use Get.routing to get the current route.

Previous

getx bottomsheet

Next

getx services

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.