gt_navigation 1.0.0
gt_navigation: ^1.0.0 copied to clipboard
A production-ready Flutter navigation package with middleware, deep linking, nested navigation, custom transitions, and type-safe routing.
import 'package:flutter/material.dart';
import 'package:gt_navigation/gt_navigation.dart';
void main() {
// Initialize the router with routes
AppRouter().initialize(
routes: AppPages.all,
deepLinkConfig: const DeepLinkConfig(
scheme: 'myapp',
host: 'example.com',
),
enableLogging: true,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GT Navigation Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
navigatorKey: AppRouter().navigatorKey,
onGenerateRoute: AppRouter().onGenerateRoute,
navigatorObservers: [AppRouter().routeObserver],
initialRoute: AppRoutes.home,
);
}
}
// --- Route Constants ---
class AppRoutes {
static const String home = '/home';
static const String detail = '/detail/:id';
static const String settings = '/settings';
static const String login = '/login';
}
// --- Route Configurations ---
class AppPages {
static final List<AppRoute> all = [
AppRoute(
path: AppRoutes.home,
builder: (context, args) => const HomeScreen(),
transition: const AppTransition(type: AppTransitionType.fade),
),
AppRoute(
path: AppRoutes.detail,
builder: (context, args) => DetailScreen(args: args),
transition: const AppTransition(
type: AppTransitionType.slide,
direction: AppTransitionDirection.bottomToTop,
),
),
AppRoute(
path: AppRoutes.settings,
builder: (context, args) => const SettingsScreen(),
middlewares: [AuthGuardMiddleware()],
),
AppRoute(
path: AppRoutes.login,
builder: (context, args) => const LoginScreen(),
transition: const AppTransition(type: AppTransitionType.material),
),
];
}
// --- Middleware ---
class AuthGuardMiddleware extends AppMiddleware {
@override
Future<bool> canNavigate(
BuildContext context,
String route,
AppRouteArguments? args,
) async {
return AuthService.isLoggedIn;
}
@override
String? getRedirectRoute() => AppRoutes.login;
}
// --- Simple Auth Service ---
class AuthService {
static bool isLoggedIn = false;
}
// --- Screens ---
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GT Navigation Demo'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildNavigationCard(
title: 'Basic Push Navigation',
subtitle: 'Push to detail screen with path parameter',
onTap: () => AppRouter().navigateTo('/detail/123'),
),
_buildNavigationCard(
title: 'Navigation with Query Args',
subtitle: 'Pass additional query parameters',
onTap: () => AppRouter().navigateTo(
'/detail/456',
arguments: const AppRouteArguments(
queryParameters: {'source': 'home'},
),
),
),
_buildNavigationCard(
title: 'Protected Route (Auth Guard)',
subtitle: 'Redirects to login if not authenticated',
onTap: () => AppRouter().navigateTo(AppRoutes.settings),
),
_buildNavigationCard(
title: 'Show Dialog (Context-Free)',
subtitle: 'Open dialog using AppRouter',
onTap: () => _showGlobalDialog(),
),
_buildNavigationCard(
title: 'Simulate Deep Link',
subtitle: 'Handle deep link: myapp://example.com/detail/999',
onTap: () => AppRouter().handleDeepLink(
Uri.parse('myapp://example.com/detail/999?ref=deeplink'),
),
),
],
),
);
}
Widget _buildNavigationCard({
required String title,
required String subtitle,
required VoidCallback onTap,
}) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: ListTile(
title: Text(title, style: const TextStyle(fontWeight: FontWeight.w600)),
subtitle: Text(subtitle, style: const TextStyle(fontSize: 12)),
trailing: const Icon(Icons.chevron_right),
onTap: onTap,
),
);
}
void _showGlobalDialog() {
AppRouter().openDialog(
builder: (_) => AlertDialog(
title: const Text('Context-Free Dialog'),
content: const Text(
'This dialog was opened without needing direct BuildContext access!',
),
actions: [
TextButton(
onPressed: () => AppRouter().goBack(),
child: const Text('Close'),
),
],
),
);
}
}
class DetailScreen extends StatelessWidget {
final AppRouteArguments? args;
const DetailScreen({super.key, this.args});
@override
Widget build(BuildContext context) {
final id = args?.getString('id') ?? 'Unknown';
final source = args?.getString('source') ?? 'Direct';
final ref = args?.getString('ref') ?? 'None';
return Scaffold(
appBar: AppBar(
title: const Text('Detail Screen'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.info_outline, size: 64, color: Colors.indigo),
const SizedBox(height: 20),
Text('ID: $id', style: Theme.of(context).textTheme.headlineMedium),
const SizedBox(height: 8),
Text('Source: $source'),
Text('Ref: $ref'),
const SizedBox(height: 32),
ElevatedButton(
onPressed: () => AppRouter().goBack(),
child: const Text('Go Back'),
),
],
),
),
);
}
}
class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Settings (Protected)'),
backgroundColor: Colors.green.shade100,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.security, size: 64, color: Colors.green),
const SizedBox(height: 20),
const Text('🎉 You are authenticated!'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
AuthService.isLoggedIn = false;
AppRouter().navigateTo(
AppRoutes.login,
mode: NavigationModes.pushAndRemoveAll,
);
},
child: const Text('Logout'),
),
],
),
),
);
}
}
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.lock_outline, size: 64, color: Colors.orange),
const SizedBox(height: 20),
const Text('Please login to access protected routes'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
AuthService.isLoggedIn = true;
AppRouter().navigateTo(
AppRoutes.home,
mode: NavigationModes.pushReplacement,
);
},
child: const Text('Login'),
),
],
),
),
);
}
}