import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../screens/auth/login_screen.dart'; import '../../screens/auth/register_screen.dart'; import '../../screens/shell/app_shell.dart'; import '../../screens/stock_in/stock_in_list_screen.dart'; import '../../screens/stock_in/stock_in_form_screen.dart'; import '../../screens/stock_out/stock_out_list_screen.dart'; import '../../screens/stock_out/stock_out_form_screen.dart'; import '../../screens/inventory/inventory_list_screen.dart'; import '../../screens/inventory/inventory_check_screen.dart'; import '../../screens/partners/partners_screen.dart'; import '../../screens/finance/finance_screen.dart'; import '../../screens/products/products_screen.dart'; import '../../screens/products/product_detail_screen.dart'; import '../../screens/public/public_product_screen.dart'; import '../../screens/public/public_shop_products_screen.dart'; import '../../screens/settings/settings_screen.dart'; import '../../screens/settings/users_screen.dart'; import '../../screens/about/about_screen.dart'; import '../../screens/devices/device_management_screen.dart'; import '../auth/auth_state.dart'; Page _noTransition(Widget child) => NoTransitionPage(child: child); /// ChangeNotifier that bridges Riverpod auth state → GoRouter refreshListenable. class _RouterNotifier extends ChangeNotifier { final Ref _ref; _RouterNotifier(this._ref) { _ref.listen(authStateProvider, (prev, next) { debugPrint('[Router] authState changed:' ' initialized=${next.initialized}' ' isLoggedIn=${next.isLoggedIn}' ' user=${next.user?.username}'); notifyListeners(); }); } String? redirect(BuildContext context, GoRouterState state) { final authState = _ref.read(authStateProvider); final isLoggedIn = authState.isLoggedIn; final loc = state.matchedLocation; final isPublicRoute = loc == '/login' || loc == '/register' || loc.startsWith('/product/') || loc.startsWith('/shop/'); final result = !authState.initialized ? null : (!isLoggedIn && !isPublicRoute) ? '/login' : (isLoggedIn && loc == '/login') ? '/stock-in' : null; debugPrint('[Router] redirect: location=$loc' ' initialized=${authState.initialized}' ' isLoggedIn=$isLoggedIn' ' → ${result ?? "null (no redirect)"}'); return result; } } /// Separate provider so that appRouterProvider has NO dependencies and /// is never rebuilt when auth state changes (prevents router reset to /login). final _routerNotifierProvider = Provider<_RouterNotifier>((ref) { final notifier = _RouterNotifier(ref); ref.onDispose(notifier.dispose); return notifier; }); final appRouterProvider = Provider((ref) { // Use ref.read (not ref.watch) so appRouterProvider never rebuilds on auth change. final notifier = ref.read(_routerNotifierProvider); final router = GoRouter( initialLocation: '/login', refreshListenable: notifier, redirect: notifier.redirect, routes: [ // Public product scan — no auth, no shell nav bar GoRoute( path: '/product/:public_id', builder: (context, state) => PublicProductScreen(publicId: state.pathParameters['public_id']!), ), // Public shop product list — no auth, no shell nav bar GoRoute( path: '/shop/:shop_code', builder: (context, state) => PublicShopProductsScreen( shopCode: state.pathParameters['shop_code']!, shopName: state.uri.queryParameters['shopName'] ?? '', ), ), GoRoute( path: '/login', builder: (context, state) => const LoginScreen(), ), GoRoute( path: '/register', builder: (context, state) => const RegisterScreen(), ), // 各栏目拆为独立分支:StatefulShellRoute.indexedStack 让每个分支的 Navigator // 及其页面 State 常驻,跨栏目切换不再销毁上一页(半填表单/内部 tab/滚动位置保活)。 // 分支顺序必须与 AppShell._navItems 一致(navigationShell.currentIndex 据此高亮)。 StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) => AppShell(navigationShell: navigationShell), branches: [ // 0 入库管理 StatefulShellBranch(routes: [ GoRoute( path: '/stock-in', pageBuilder: (_, __) => _noTransition(const StockInListScreen())), GoRoute( path: '/stock-in/new', pageBuilder: (_, __) => _noTransition(const StockInFormScreen())), GoRoute( path: '/stock-in/edit/:id', pageBuilder: (_, state) => _noTransition(StockInFormScreen( editOrderId: int.parse(state.pathParameters['id']!)))), ]), // 1 出库管理 StatefulShellBranch(routes: [ GoRoute( path: '/stock-out', pageBuilder: (_, __) => _noTransition(const StockOutListScreen())), GoRoute( path: '/stock-out/new', pageBuilder: (_, __) => _noTransition(const StockOutFormScreen())), GoRoute( path: '/stock-out/edit/:id', pageBuilder: (_, state) => _noTransition(StockOutFormScreen( editOrderId: int.parse(state.pathParameters['id']!)))), ]), // 2 库存管理 StatefulShellBranch(routes: [ GoRoute( path: '/inventory', pageBuilder: (_, __) => _noTransition(const InventoryListScreen())), GoRoute( path: '/inventory/check', pageBuilder: (_, __) => _noTransition(const InventoryCheckScreen())), ]), // 3 财务管理 StatefulShellBranch(routes: [ GoRoute( path: '/finance', pageBuilder: (_, __) => _noTransition(const FinanceScreen())), ]), // 4 往来单位 StatefulShellBranch(routes: [ GoRoute( path: '/partners', pageBuilder: (_, __) => _noTransition(const PartnersScreen())), ]), // 5 基础数据 StatefulShellBranch(routes: [ GoRoute( path: '/products', // ?tab=intro → 商品介绍 tab(对齐原型 products.html?tab=intro) pageBuilder: (_, state) => _noTransition(ProductsScreen( initialTab: state.uri.queryParameters['tab'] == 'intro' ? 1 : 0))), GoRoute( path: '/products/:id', pageBuilder: (_, state) => _noTransition(ProductDetailScreen( productId: int.parse(state.pathParameters['id']!)))), ]), // 6 设备管理 StatefulShellBranch(routes: [ GoRoute( path: '/devices', pageBuilder: (_, __) => _noTransition(const DeviceManagementScreen())), ]), // 7 系统设置(面板:门店信息/用户管理/编号规则/授权兑换券/偏好设置) StatefulShellBranch(routes: [ GoRoute( path: '/settings', pageBuilder: (_, state) { const tabIndex = { 'shop': 0, 'users': 1, 'number': 2, 'license': 3, 'pref': 4, }; final tab = tabIndex[state.uri.queryParameters['tab']] ?? 0; return _noTransition(SettingsScreen(initialTab: tab)); }), // 用户管理独立页(照原型 users.html) GoRoute( path: '/settings/users', pageBuilder: (_, __) => _noTransition(const UsersScreen())), ]), // 8 关于我们 StatefulShellBranch(routes: [ GoRoute( path: '/about', pageBuilder: (_, __) => _noTransition(const AboutScreen())), ]), ], ), ], ); ref.onDispose(router.dispose); return router; });