dio_auth_guard 0.0.1
dio_auth_guard: ^0.0.1 copied to clipboard
Automatically handles 401/403 responses in Dio — cancels all in-flight requests and navigates to a custom unauthenticated screen. Router-agnostic.
example/lib/main.dart
import 'package:dio/dio.dart';
import 'package:dio_auth_guard/dio_auth_guard.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
// ---------------------------------------------------------------------------
// Navigator key — shared between AuthGuardConfig and GoRouter so the guard
// can resolve the current context when navigation is needed.
// ---------------------------------------------------------------------------
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
// ---------------------------------------------------------------------------
// Dio instance — register AuthGuardModule after creation.
// ---------------------------------------------------------------------------
final Dio dio = Dio(BaseOptions(baseUrl: 'https://api.example.com'));
// ---------------------------------------------------------------------------
// GoRouter
// ---------------------------------------------------------------------------
final GoRouter router = GoRouter(
navigatorKey: navigatorKey,
initialLocation: '/home',
routes: [
GoRoute(
path: '/login',
builder: (_, __) => const LoginScreen(),
),
GoRoute(
path: '/home',
builder: (_, __) => const HomeScreen(),
),
],
);
// ---------------------------------------------------------------------------
// main()
// ---------------------------------------------------------------------------
void main() {
// ── Option A: GoRouter (active) ──────────────────────────────────────────
// Use onUnauthenticated when your app uses a declarative router.
// The callback lets the router manage the navigation stack itself.
AuthGuardConfig.init(
navigatorKey: navigatorKey,
onUnauthenticated: () {
navigatorKey.currentContext?.go('/login');
},
);
// ── Option B: Plain Navigator (commented out) ────────────────────────────
// Use unauthWidget when your app uses Flutter's built-in Navigator.
// AuthGuard will call pushAndRemoveUntil, clearing the entire back-stack.
//
// AuthGuardConfig.init(
// navigatorKey: navigatorKey,
// unauthWidget: const LoginScreen(),
// );
// Register the interceptor on the Dio instance.
AuthGuardModule.register(dio);
runApp(const ExampleApp());
}
// ---------------------------------------------------------------------------
// App
// ---------------------------------------------------------------------------
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'dio_auth_guard example',
routerConfig: router,
debugShowCheckedModeBanner: false,
);
}
}
// ---------------------------------------------------------------------------
// HomeScreen
// ---------------------------------------------------------------------------
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('dio_auth_guard example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Home Screen — authenticated',
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: () {
// Simulates receiving a 401 response — in a real app this is
// triggered automatically by AuthInterceptor. Useful for
// manually testing the guard without a real backend.
AuthGuard.instance.handleUnauthenticated();
},
child: const Text('Simulate 401 — trigger guard'),
),
],
),
),
);
}
}
// ---------------------------------------------------------------------------
// LoginScreen
// ---------------------------------------------------------------------------
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
void _onLogin() {
// In a real app: await your auth API call here, then on success:
// 1. Store tokens.
// 2. Call AuthGuard.onLoginSuccess() to re-arm the guard.
// 3. Navigate to the authenticated area.
AuthGuard.onLoginSuccess();
context.go('/home');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
obscureText: true,
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _onLogin,
child: const Text('Login'),
),
),
],
),
),
);
}
}