| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- 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<TDNavBarItem>? 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<TDNavBarItem>? 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'),
- ),
- ],
- ),
- );
- },
- );
- }
- }
|