import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/theme/app_colors.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_extension.dart'; import 'nav_bar_config.dart'; /// 应用级 Scaffold:NavBar + body + 可选 BottomTabBar /// /// 替代原来的 AppShell。每个页面自行包裹,无需 ShellRoute。 class AppScaffold extends ConsumerWidget { final Widget body; final bool showTabBar; const AppScaffold({super.key, required this.body, this.showTabBar = false}); bool _isRootTab(String location) { return location == '/' || location == '/messages' || location == '/profile'; } 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 colors = Theme.of(context).extension()!; final l10n = AppLocalizations.of(context); final location = GoRouterState.of(context).uri.toString(); final config = _isRootTab(location) ? _rootConfig(location, l10n) : ref.watch(navBarConfigProvider); SystemChrome.setSystemUIOverlayStyle( SystemUiOverlayStyle( statusBarColor: colors.bgCard, statusBarIconBrightness: Brightness.dark, statusBarBrightness: Brightness.light, ), ); return Scaffold( backgroundColor: colors.bgPage, body: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _NavBarView( config: config, location: location, onBack: () { if (_isRootTab(location)) { SystemNavigator.pop(); } else { GoRouter.of(context).pop(); } }, ), Expanded(child: body), if (showTabBar) Container( color: colors.bgCard, child: _AppTabBar(location: location), ), ], ), ); } } class _NavBarView extends StatelessWidget { final NavBarConfig config; final String location; final VoidCallback onBack; const _NavBarView({ required this.config, required this.location, required this.onBack, }); @override Widget build(BuildContext context) { final colors = Theme.of(context).extension()!; List? leftItems; if (config.showBack) { final icon = config.leadingIcon ?? TDIcons.chevron_left; leftItems = [ TDNavBarItem( icon: icon, iconSize: 22, iconColor: colors.textPrimary, action: config.onBack ?? onBack, ), ]; } List? rightItems; if (config.showRight && config.rightWidget != null) { rightItems = [TDNavBarItem(iconWidget: config.rightWidget, iconSize: 22)]; } return TDNavBar( title: config.title, titleColor: colors.textPrimary, titleFontWeight: FontWeight.w600, titleFont: Font(size: AppFontSizes.title.toInt(), lineHeight: 26), backgroundColor: colors.bgCard, height: 56, centerTitle: true, useDefaultBack: false, screenAdaptation: true, leftBarItems: leftItems, rightBarItems: rightItems, ); } } class _AppTabBar extends StatelessWidget { final String location; const _AppTabBar({required this.location}); static int tabIndex(String location) { if (location.startsWith('/messages')) return 0; if (location == '/' || (!location.startsWith('/messages') && !location.startsWith('/profile'))) { return 1; } return 2; } @override Widget build(BuildContext context) { final colors = Theme.of(context).extension()!; final l10n = AppLocalizations.of(context); 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: 0, bottom: 8 + bottomPadding), child: TDBottomTabBar( TDBottomTabBarBasicType.iconText, useSafeArea: false, componentType: TDBottomTabBarComponentType.label, outlineType: TDBottomTabBarOutlineType.filled, backgroundColor: colors.bgCard, dividerColor: Colors.transparent, selectedBgColor: colors.primaryLight, unselectedBgColor: Colors.transparent, currentIndex: tabIndex(location), navigationTabs: [ TDBottomTabBarTabConfig( tabText: l10n.get('tabMessages'), selectedIcon: Icon( Icons.notifications, size: 22, color: colors.primary, ), unselectedIcon: Icon( Icons.notifications_outlined, size: 22, color: colors.textSecondary, ), selectTabTextStyle: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: colors.primary, ), unselectTabTextStyle: TextStyle( fontSize: 10, color: colors.textSecondary, ), onTap: () => context.go('/messages'), ), TDBottomTabBarTabConfig( tabText: l10n.get('tabWorkbench'), selectedIcon: Icon( Icons.dashboard, size: 22, color: colors.primary, ), unselectedIcon: Icon( Icons.dashboard_outlined, size: 22, color: colors.textSecondary, ), selectTabTextStyle: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: colors.primary, ), unselectTabTextStyle: TextStyle( fontSize: 10, color: colors.textSecondary, ), onTap: () => context.go('/'), ), TDBottomTabBarTabConfig( tabText: l10n.get('tabProfile'), selectedIcon: Icon( Icons.person, size: 22, color: colors.primary, ), unselectedIcon: Icon( Icons.person_outline, size: 22, color: colors.textSecondary, ), selectTabTextStyle: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: colors.primary, ), unselectTabTextStyle: TextStyle( fontSize: 10, color: colors.textSecondary, ), onTap: () => context.go('/profile'), ), ], ), ); }, ); } }