go router added

This commit is contained in:
amirrezaghabeli
2025-08-05 08:23:05 +03:30
parent d5984b8bbf
commit 676124567f
14 changed files with 887 additions and 108 deletions
+136
View File
@@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../views/home/home_screen.dart';
import '../../views/login/login_screen.dart';
import '../../views/profile/profile_screen.dart';
import '../../views/tenders/tenders_screen.dart';
import 'app_shell_screen.dart';
part 'app_routes.g.dart';
// Global navigator keys
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> homeNavigatorKey = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> tendersNavigatorKey =
GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> profileNavigatorKey =
GlobalKey<NavigatorState>();
// Main app router
final GoRouter appRouter = GoRouter(
routes: $appRoutes,
initialLocation: '/login',
navigatorKey: rootNavigatorKey,
);
// Login route - outside the shell
@TypedGoRoute<LoginScreenRoute>(path: '/login')
class LoginScreenRoute extends GoRouteData with _$LoginScreenRoute {
const LoginScreenRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const LoginScreen();
}
}
// Shell route with bottom navigation using StatefulShellRoute.indexedStack
@TypedStatefulShellRoute<AppShellRouteData>(
branches: <TypedStatefulShellBranch<StatefulShellBranchData>>[
TypedStatefulShellBranch<HomeBranch>(
routes: <TypedRoute<RouteData>>[
TypedGoRoute<HomeRouteData>(path: '/home'),
],
),
TypedStatefulShellBranch<TendersBranch>(
routes: <TypedRoute<RouteData>>[
TypedGoRoute<TendersRouteData>(path: '/tenders'),
],
),
TypedStatefulShellBranch<ProfileBranch>(
routes: <TypedRoute<RouteData>>[
TypedGoRoute<ProfileRouteData>(path: '/profile'),
],
),
],
)
class AppShellRouteData extends StatefulShellRouteData {
const AppShellRouteData();
static final GlobalKey<NavigatorState> $parentNavigatorKey = rootNavigatorKey;
@override
Widget builder(
BuildContext context,
GoRouterState state,
StatefulNavigationShell navigationShell,
) {
return AppShellScreen(navigationShell: navigationShell);
}
}
// Branch data classes
class HomeBranch extends StatefulShellBranchData {
const HomeBranch();
static final GlobalKey<NavigatorState> $navigatorKey = homeNavigatorKey;
}
class TendersBranch extends StatefulShellBranchData {
const TendersBranch();
static final GlobalKey<NavigatorState> $navigatorKey = tendersNavigatorKey;
}
class ProfileBranch extends StatefulShellBranchData {
const ProfileBranch();
static final GlobalKey<NavigatorState> $navigatorKey = profileNavigatorKey;
}
// Route data classes
class HomeRouteData extends GoRouteData with _$HomeRouteData {
const HomeRouteData();
@override
Widget build(BuildContext context, GoRouterState state) {
return const HomeScreen();
}
// @override
// Page<void> buildPage(BuildContext context, GoRouterState state) {
// return CustomTransitionPage<void>(
// key: state.pageKey,
// child: const HomeScreen(),
// transitionDuration: const Duration(milliseconds: 500),
// transitionsBuilder: (context, animation, secondaryAnimation, child) {
// const begin = Offset(1.0, 0.0); // From right to left
// const end = Offset.zero;
// const curve = Curves.linearToEaseOut;
// var tween = Tween(
// begin: begin,
// end: end,
// ).chain(CurveTween(curve: curve));
// var offsetAnimation = animation.drive(tween);
// return SlideTransition(position: offsetAnimation, child: child);
// },
// );
// }
}
class TendersRouteData extends GoRouteData with _$TendersRouteData {
const TendersRouteData();
@override
Widget build(BuildContext context, GoRouterState state) {
return const TendersScreen();
}
}
class ProfileRouteData extends GoRouteData with _$ProfileRouteData {
const ProfileRouteData();
@override
Widget build(BuildContext context, GoRouterState state) {
return const ProfileScreen();
}
}
+136
View File
@@ -0,0 +1,136 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'app_routes.dart';
// **************************************************************************
// GoRouterGenerator
// **************************************************************************
List<RouteBase> get $appRoutes => [$loginScreenRoute, $appShellRouteData];
RouteBase get $loginScreenRoute =>
GoRouteData.$route(path: '/login', factory: _$LoginScreenRoute._fromState);
mixin _$LoginScreenRoute on GoRouteData {
static LoginScreenRoute _fromState(GoRouterState state) =>
const LoginScreenRoute();
@override
String get location => GoRouteData.$location('/login');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $appShellRouteData => StatefulShellRouteData.$route(
parentNavigatorKey: AppShellRouteData.$parentNavigatorKey,
factory: $AppShellRouteDataExtension._fromState,
branches: [
StatefulShellBranchData.$branch(
navigatorKey: HomeBranch.$navigatorKey,
routes: [
GoRouteData.$route(path: '/home', factory: _$HomeRouteData._fromState),
],
),
StatefulShellBranchData.$branch(
navigatorKey: TendersBranch.$navigatorKey,
routes: [
GoRouteData.$route(
path: '/tenders',
factory: _$TendersRouteData._fromState,
),
],
),
StatefulShellBranchData.$branch(
navigatorKey: ProfileBranch.$navigatorKey,
routes: [
GoRouteData.$route(
path: '/profile',
factory: _$ProfileRouteData._fromState,
),
],
),
],
);
extension $AppShellRouteDataExtension on AppShellRouteData {
static AppShellRouteData _fromState(GoRouterState state) =>
const AppShellRouteData();
}
mixin _$HomeRouteData on GoRouteData {
static HomeRouteData _fromState(GoRouterState state) => const HomeRouteData();
@override
String get location => GoRouteData.$location('/home');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$TendersRouteData on GoRouteData {
static TendersRouteData _fromState(GoRouterState state) =>
const TendersRouteData();
@override
String get location => GoRouteData.$location('/tenders');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$ProfileRouteData on GoRouteData {
static ProfileRouteData _fromState(GoRouterState state) =>
const ProfileRouteData();
@override
String get location => GoRouteData.$location('/profile');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
+101
View File
@@ -0,0 +1,101 @@
// Shell screen with bottom navigation
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import '../utils/size_config.dart';
class AppShellScreen extends StatelessWidget {
const AppShellScreen({required this.navigationShell, super.key});
final StatefulNavigationShell navigationShell;
void _gotoBranch(int index) {
navigationShell.goBranch(
index,
initialLocation: index == navigationShell.currentIndex,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: navigationShell,
bottomNavigationBar: Container(
height: 72.0.h(),
decoration: BoxDecoration(
color: const Color(0xFFE3E3E3),
border: Border.fromBorderSide(
BorderSide(color: const Color(0xFFE3E3E3)),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_bottomNavigationItem(
context: context,
text: 'Home',
isActive: navigationShell.currentIndex == 0,
onTap: () => _gotoBranch(0),
iconPath: 'assets/icons/home.svg',
activeIconPath: 'assets/icons/homeActive.svg',
),
_bottomNavigationItem(
context: context,
text: 'Tenders',
isActive: navigationShell.currentIndex == 1,
onTap: () => _gotoBranch(1),
iconPath: 'assets/icons/task_square.svg',
activeIconPath: 'assets/icons/task-square_active.svg',
),
_bottomNavigationItem(
context: context,
text: 'Profile',
isActive: navigationShell.currentIndex == 2,
onTap: () => _gotoBranch(2),
iconPath: 'assets/icons/profile-circle.svg',
activeIconPath: 'assets/icons/profile-circle_active.svg',
),
],
),
),
);
}
Widget _bottomNavigationItem({
required BuildContext context,
required String text,
required bool isActive,
required VoidCallback onTap,
required String iconPath,
required String activeIconPath,
}) {
return InkWell(
onTap: onTap,
child: SizedBox(
width: 64.0.w(),
height: 72.0.h(),
child: Padding(
padding: EdgeInsets.only(top: 12.0.h(), bottom: 14.0.h()),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SvgPicture.asset(isActive ? activeIconPath : iconPath),
Text(
text,
style: TextStyle(
fontSize: 12.0.sp(),
fontWeight: FontWeight.w400,
color:
isActive
? const Color(0xFF0164FF)
: const Color(0xFF777777),
),
),
],
),
),
),
);
}
}