feat: Refactor routing and navigation structure with StatefulShellRoute
Test Suite / test (24.15.0) (push) Has been cancelled

- Introduced a new function `_shellBranchIndexForPath` to determine the index of the shell branch based on the path.
- Replaced `ShellRoute` with `StatefulShellRoute.indexedStack` for better state management during navigation.
- Updated `AppShell` to handle navigation path changes and integrate with the new routing structure.
- Organized routes into `StatefulShellBranch` for better modularity and clarity.
- Enhanced admin panel functionality with improved alias management and UI updates.
- Added new methods in `ReceiptImportSessionNotifier` for managing selected items and edits more efficiently.
- Improved UI components in receipt import and admin panels for better performance and user experience.
- Added PageStorageKeys to various ListViews to maintain scroll positions across navigation.
- Documented performance goals and profiling strategies in a new PERFORMANCE.md file.
This commit is contained in:
Nils-Johan Gynther
2026-05-08 12:51:38 +02:00
parent 73309cb110
commit 0873fa42bb
12 changed files with 625 additions and 285 deletions
+94 -35
View File
@@ -24,6 +24,17 @@ import '../../features/pantry/presentation/pantry_screen.dart';
import '../../features/import/presentation/import_screen.dart';
import '../../features/admin/presentation/admin_screen.dart';
int? _shellBranchIndexForPath(String path) {
if (path.startsWith('/recipes')) return 0;
if (path.startsWith('/inventory')) return 1;
if (path.startsWith('/matsedel')) return 2;
if (path.startsWith('/baslager')) return 3;
if (path.startsWith('/import')) return 4;
if (path.startsWith('/profile')) return 5;
if (path.startsWith('/admin')) return 6;
return null;
}
final appRouterProvider = Provider<GoRouter>((ref) {
final authState = ref.watch(authStateProvider);
@@ -175,42 +186,90 @@ final appRouterProvider = Provider<GoRouter>((ref) {
},
),
// Shell routes — shared AppShell with navigation bar.
ShellRoute(
builder: (context, state, child) {
return AppShell(location: state.uri.path, child: child);
},
routes: [
GoRoute(
path: '/recipes',
builder: (context, state) => const RecipesScreen(),
),
GoRoute(
path: '/inventory',
builder: (context, state) => const InventoryScreen(),
),
GoRoute(
path: '/matsedel',
builder: (context, state) => const MealPlanScreen(),
),
GoRoute(
path: '/baslager',
builder: (context, state) => const PantryScreen(),
),
GoRoute(
path: '/import',
builder: (context, state) => const ImportScreen(),
),
GoRoute(
path: '/profile',
builder: (context, state) => const ProfileScreen(),
),
GoRoute(
path: '/admin',
redirect: (context, state) {
final token = ref.read(authStateProvider).maybeWhen(data: (t) => t, orElse: () => null);
return jwtIsAdmin(token) ? null : '/recipes';
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) {
return AppShell(
location: state.uri.path,
onNavigateToPath: (path) {
final index = _shellBranchIndexForPath(path);
if (index == null) {
context.go(path);
return;
}
if (index == navigationShell.currentIndex) {
if (state.uri.path != path) {
context.go(path);
}
return;
}
navigationShell.goBranch(index);
},
builder: (context, state) => const AdminScreen(),
child: navigationShell,
);
},
branches: [
StatefulShellBranch(
routes: [
GoRoute(
path: '/recipes',
builder: (context, state) => const RecipesScreen(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/inventory',
builder: (context, state) => const InventoryScreen(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/matsedel',
builder: (context, state) => const MealPlanScreen(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/baslager',
builder: (context, state) => const PantryScreen(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/import',
builder: (context, state) => const ImportScreen(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/profile',
builder: (context, state) => const ProfileScreen(),
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/admin',
redirect: (context, state) {
final token = ref.read(authStateProvider)
.maybeWhen(data: (t) => t, orElse: () => null);
return jwtIsAdmin(token) ? null : '/recipes';
},
builder: (context, state) => const AdminScreen(),
),
],
),
],
),