flutter_navkit 2.0.1
flutter_navkit: ^2.0.1 copied to clipboard
A powerful navigation toolkit for Flutter with type-safe routing, automatic route generation, and comprehensive navigation observability.
🧭 Flutter NavKit - Examples #
Simple examples demonstrating both routing approaches in NavKit.
🎨 Generated Routes Example #
This example shows how to use programmatic route generation with NavKitGeneratedRoute.
Complete Code: #
import 'package:flutter/material.dart';
import 'package:flutter_navkit/flutter_navkit.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return NavkitMaterialApp.onGenerateRoute(
title: 'NavKit Generated Routes Example',
debugShowCheckedModeBanner: false,
observeWithStack: true,
/// Define all routes with custom transitions
generatedRoutes: [
NavKitGeneratedRoute(
name: '/',
builder: (settings) => const HomeScreen(),
transitionType: NavKitGeneratedRouteTransitionType.fade,
),
NavKitGeneratedRoute(
name: '/profile',
builder: (settings) => const ProfileScreen(),
transitionType: NavKitGeneratedRouteTransitionType.slideRight,
duration: const Duration(milliseconds: 400),
),
NavKitGeneratedRoute(
name: '/details',
builder: (settings) {
// Extract arguments from settings
final args = settings.arguments as Map<String, dynamic>?;
final title = args?['title'] as String? ?? 'No Title';
final id = args?['id'] as int? ?? 0;
return DetailsScreen(title: title, id: id);
},
transitionType: NavKitGeneratedRouteTransitionType.scale,
),
],
/// Fallback route for unknown routes
generatedFailureRoute: NavKitGeneratedRoute(
builder: (settings) => const NotFoundScreen(),
transitionType: NavKitGeneratedRouteTransitionType.fade,
),
);
}
}
// ═══════════════════════════════════════════════════════════
// HOME SCREEN
// ═══════════════════════════════════════════════════════════
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Generated Routes Example',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 32),
// Navigate to profile
ElevatedButton(
onPressed: () => context.toNamed('/profile'),
child: const Text('Go to Profile'),
),
const SizedBox(height: 16),
// Navigate with arguments
ElevatedButton(
onPressed: () => context.toNamed(
'/details',
args: {'title': 'Product A', 'id': 42},
),
child: const Text('View Details'),
),
const SizedBox(height: 16),
// Test 404 page
ElevatedButton(
onPressed: () => context.toNamed('/nonexistent'),
child: const Text('Go to Unknown Route (404)'),
),
],
),
),
);
}
}
// ═══════════════════════════════════════════════════════════
// PROFILE SCREEN
// ═══════════════════════════════════════════════════════════
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Profile')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircleAvatar(
radius: 50,
child: Icon(Icons.person, size: 50),
),
const SizedBox(height: 16),
const Text(
'John Doe',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: () => context.back(),
child: const Text('Go Back'),
),
],
),
),
);
}
}
// ═══════════════════════════════════════════════════════════
// DETAILS SCREEN (With Arguments)
// ═══════════════════════════════════════════════════════════
class DetailsScreen extends StatelessWidget {
final String title;
final int id;
const DetailsScreen({
super.key,
required this.title,
required this.id,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
title,
style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
'ID: $id',
style: const TextStyle(fontSize: 16, color: Colors.grey),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: () => context.back(),
child: const Text('Go Back'),
),
],
),
),
);
}
}
// ═══════════════════════════════════════════════════════════
// NOT FOUND SCREEN (404)
// ═══════════════════════════════════════════════════════════
class NotFoundScreen extends StatelessWidget {
const NotFoundScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('404 - Not Found')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 80, color: Colors.red),
const SizedBox(height: 16),
const Text(
'Page Not Found',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text('The page you are looking for does not exist.'),
const SizedBox(height: 32),
ElevatedButton(
onPressed: () => context.toNamed('/'),
child: const Text('Go Home'),
),
],
),
),
);
}
}
Key Features Demonstrated: #
✅ Route definition with NavKitGeneratedRoute
✅ Custom transitions per route (fade, slideRight, scale)
✅ Route arguments using RouteSettings in the builder
✅ Custom 404 page with generatedFailureRoute
✅ Navigation methods (toNamed, back)
✅ Stack logging with observeWithStack: true
📝 Annotated Routes Example #
This example shows how to use annotation-based automatic route generation with @NavkitRoute.
Step 1: Define Your Screens with Annotations #
import 'package:flutter/material.dart';
import 'package:flutter_navkit/flutter_navkit.dart';
// Import the generated routes file
import 'main.navkit.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return NavkitMaterialApp.annotatedRoute(
title: 'NavKit Annotated Routes Example',
debugShowCheckedModeBanner: false,
observeWithStack: true,
/// List all annotated screens
/// The initial route MUST be first!
navkitRoutes: const [
HomeScreen(), // Must be first (marked as initial)
ProfileScreen(),
DetailsScreen(),
],
);
}
}
// ═══════════════════════════════════════════════════════════
// HOME SCREEN (Initial Route)
// ═══════════════════════════════════════════════════════════
@NavkitRoute(isInitial: true)
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Annotated Routes Example',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 32),
// Named navigation using generated constants
ElevatedButton(
onPressed: () => context.toNamed(NavkitRoutes.profileScreen),
child: const Text('Go to Profile (Named)'),
),
const SizedBox(height: 16),
// Direct widget navigation
ElevatedButton(
onPressed: () => context.to(const ProfileScreen()),
child: const Text('Go to Profile (Direct)'),
),
const SizedBox(height: 16),
// Navigate with arguments
ElevatedButton(
onPressed: () => context.toNamed(
NavkitRoutes.detailsScreen,
args: {'title': 'Product A', 'id': 42},
),
child: const Text('View Details'),
),
const SizedBox(height: 16),
// With fade animation
ElevatedButton(
onPressed: () => context.toWithFade(const ProfileScreen()),
child: const Text('Go to Profile (Fade)'),
),
],
),
),
);
}
}
// ═══════════════════════════════════════════════════════════
// PROFILE SCREEN
// ═══════════════════════════════════════════════════════════
@NavkitRoute()
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Profile')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircleAvatar(
radius: 50,
child: Icon(Icons.person, size: 50),
),
const SizedBox(height: 16),
const Text(
'John Doe',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 32),
// Navigate to details
ElevatedButton(
onPressed: () => context.toNamed(NavkitRoutes.detailsScreen),
child: const Text('View Details'),
),
const SizedBox(height: 16),
// Go back
ElevatedButton(
onPressed: () => context.back(),
child: const Text('Go Back'),
),
const SizedBox(height: 16),
// Back to home
ElevatedButton(
onPressed: () => context.backToNamed(NavkitRoutes.homeScreen),
child: const Text('Back to Home'),
),
],
),
),
);
}
}
// ═══════════════════════════════════════════════════════════
// DETAILS SCREEN (Receives Arguments)
// ═══════════════════════════════════════════════════════════
@NavkitRoute()
class DetailsScreen extends StatelessWidget {
const DetailsScreen({super.key});
@override
Widget build(BuildContext context) {
// Extract arguments
final args = context.arguments<Map<String, dynamic>>();
final title = args?['title'] ?? 'No Title';
final id = args?['id'] ?? 0;
return Scaffold(
appBar: AppBar(title: const Text('Details')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
title,
style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
'ID: $id',
style: const TextStyle(fontSize: 16, color: Colors.grey),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: () => context.back(),
child: const Text('Go Back'),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => context.backToFirst(),
child: const Text('Back to First'),
),
],
),
),
);
}
}
Step 2: Create build.yaml #
Create a build.yaml file in your project root:
targets:
$default:
builders:
flutter_navkit|navkit_routes_builder:
enabled: true
Step 3: Generate Routes #
Run this command to generate the route constants:
dart run build_runner build --delete-conflicting-outputs
This generates lib/main.navkit.dart:
class NavkitRoutes {
NavkitRoutes._();
/// Route name for HomeScreen
static const String homeScreen = '/';
/// Route name for ProfileScreen
static const String profileScreen = '/profileScreenRoute';
/// Route name for DetailsScreen
static const String detailsScreen = '/detailsScreenRoute';
/// All registered routes
static const Map<String, String> all = {
'HomeScreen': '/',
'ProfileScreen': '/profileScreenRoute',
'DetailsScreen': '/detailsScreenRoute',
};
}
Key Features Demonstrated: #
✅ Annotation-based routing with @NavkitRoute()
✅ Auto-generated route constants (NavkitRoutes.screenName)
✅ Initial route marking with isInitial: true
✅ Type-safe navigation using generated constants
✅ Multiple navigation methods (named, direct, animated)
✅ Argument handling with context.arguments<T>()
✅ Stack management (back, backToNamed, backToFirst)
🎯 Comparison #
| Feature | Generated Routes | Annotated Routes |
|---|---|---|
| Setup | Define routes in list | Annotate classes + run build_runner |
| Route Names | Manual ('/', '/profile') |
Auto-generated (NavkitRoutes.profileScreen) |
| Type Safety | String-based | Constant-based |
| Arguments | In builder function | In widget build method |
| Transitions | Per-route config | Per-navigation call |
| 404 Handling | Custom generatedFailureRoute |
Default screen |
| Build Runner | ❌ Not needed | ✅ Required |
📚 Next Steps #
- Generated Routes: See Generated Routes Guide
- Annotated Routes: See Annotated Routes Guide
- Navigation Features: See Navigation Fundamentals