import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../core/i18n/app_localizations.dart'; import '../../core/theme/app_colors.dart'; import 'nav_bar_config.dart'; class AppShell extends ConsumerWidget { final Widget child; const AppShell({super.key, required this.child}); bool _showBottomBar(String location) { return location == '/' || location == '/messages' || location == '/profile'; } int _tabIndex(String location) { if (location.startsWith('/messages')) return 0; if (location == '/') return 1; if (location.startsWith('/profile')) return 2; return 1; } /// 根页面 NavBar 配置直接从路由推导,避免 Timer 延迟更新的竞态问题 NavBarConfig _rootConfig(String location, AppLocalizations l10n) { if (location.startsWith('/messages')) { return NavBarConfig( title: l10n.get('tabMessages'), showBack: true, leadingIcon: Icons.close, ); } if (location == '/') { return NavBarConfig( title: l10n.get('appName'), showBack: true, leadingIcon: Icons.close, ); } if (location.startsWith('/profile')) { return NavBarConfig( title: l10n.get('tabProfile'), showBack: true, leadingIcon: Icons.close, ); } return NavBarConfig.home; } @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); final location = GoRouterState.of(context).uri.toString(); final showBottomBar = _showBottomBar(location); // 根页面从 location 同步推导,子页面走 provider(由页面 build 时更新) final config = showBottomBar ? _rootConfig(location, l10n) : ref.watch(navBarConfigProvider); SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarColor: AppColors.bgCard, statusBarIconBrightness: Brightness.dark, statusBarBrightness: Brightness.light, ), ); return Scaffold( backgroundColor: AppColors.bgPage, body: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildNavBar(config, location, context), Expanded(child: child), if (showBottomBar) _buildBottomBar(location, context, l10n), ], ), ); } Widget _buildNavBar( NavBarConfig config, String location, BuildContext context, ) { final isRoot = _showBottomBar(location); List? leftItems; if (config.showBack) { final icon = config.leadingIcon ?? TDIcons.chevron_left; leftItems = [ TDNavBarItem( icon: icon, iconSize: 22, iconColor: AppColors.textPrimary, action: config.onBack ?? (isRoot ? () => SystemNavigator.pop() : () => GoRouter.of(context).pop()), ), ]; } List? rightItems; if (config.showRight && config.rightWidget != null) { rightItems = [TDNavBarItem(iconWidget: config.rightWidget, iconSize: 22)]; } return TDNavBar( title: config.title, titleColor: AppColors.textPrimary, titleFontWeight: FontWeight.w600, titleFont: Font(size: AppFontSizes.title.toInt(), lineHeight: 26), backgroundColor: AppColors.bgCard, height: 56, centerTitle: true, useDefaultBack: false, screenAdaptation: true, leftBarItems: leftItems, rightBarItems: rightItems, ); } Widget _buildBottomBar( String location, BuildContext context, AppLocalizations l10n, ) { return LayoutBuilder( builder: (ctx, constraints) { if (constraints.maxWidth <= 0) { return const SizedBox.shrink(); } final bottomPadding = MediaQuery.of(ctx).padding.bottom; return Padding( padding: EdgeInsets.only(top: 8, bottom: 8 + bottomPadding), child: TDBottomTabBar( TDBottomTabBarBasicType.iconText, useSafeArea: false, componentType: TDBottomTabBarComponentType.label, outlineType: TDBottomTabBarOutlineType.capsule, currentIndex: _tabIndex(location), navigationTabs: [ TDBottomTabBarTabConfig( tabText: l10n.get('tabMessages'), selectedIcon: const Icon( Icons.notifications, size: 22, color: AppColors.primary, ), unselectedIcon: const Icon( Icons.notifications_outlined, size: 22, color: AppColors.textSecondary, ), selectTabTextStyle: const TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: AppColors.primary, ), unselectTabTextStyle: const TextStyle( fontSize: 10, color: AppColors.textSecondary, ), onTap: () => context.go('/messages'), ), TDBottomTabBarTabConfig( tabText: l10n.get('tabWorkbench'), selectedIcon: const Icon( Icons.dashboard, size: 22, color: AppColors.primary, ), unselectedIcon: const Icon( Icons.dashboard_outlined, size: 22, color: AppColors.textSecondary, ), selectTabTextStyle: const TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: AppColors.primary, ), unselectTabTextStyle: const TextStyle( fontSize: 10, color: AppColors.textSecondary, ), onTap: () => context.go('/'), ), TDBottomTabBarTabConfig( tabText: l10n.get('tabProfile'), selectedIcon: const Icon( Icons.person, size: 22, color: AppColors.primary, ), unselectedIcon: const Icon( Icons.person_outline, size: 22, color: AppColors.textSecondary, ), selectTabTextStyle: const TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: AppColors.primary, ), unselectTabTextStyle: const TextStyle( fontSize: 10, color: AppColors.textSecondary, ), onTap: () => context.go('/profile'), ), ], ), ); }, ); } }