Files
xianyan/lib/core/layout/app_shell.dart
Developer fed86c0375 迁移
2026-05-21 04:27:21 +08:00

234 lines
8.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// 闲言APP — 应用布局壳
// 创建时间: 2026-04-20
// 更新时间: 2026-05-18
// 作用: ShellRoute 布局壳,包含底部 GlassBottomBar 导航 + 发现小红点
// 上次更新: 恢复鸿蒙端液态玻璃效果统一使用GlassBottomBar
// ============================================================
import 'package:badges/badges.dart' as badges;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:xianyan/core/utils/platform_utils.dart' as pu;
import 'package:go_router/go_router.dart';
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
import '../theme/app_theme.dart';
import '../utils/interaction_animations.dart';
import '../utils/logger.dart';
import '../../features/tool_center/inspiration/providers/chat_provider.dart';
import '../../features/mine/settings/providers/theme_settings_provider.dart';
import '../../l10n/translations.dart';
import '../../shared/widgets/tab_icon_sprite.dart';
import '../../main.dart' show liquidGlassReady;
import '../../shared/widgets/glass_bottom_nav_bar.dart';
class AppShell extends ConsumerWidget {
const AppShell({super.key, required this.child});
final StatefulNavigationShell child;
static bool get _isOhos => pu.isOhos;
@override
Widget build(BuildContext context, WidgetRef ref) {
final ext = AppTheme.ext(context);
final int currentIndex = child.currentIndex;
if (_isOhos) {
Log.i(
'🟢 [OHOS] AppShell.build() — currentIndex=$currentIndex isDark=${ext.isDark}',
);
}
final unreadCount = ref.watch(chatProvider).unreadCount;
final settings = ref.watch(themeSettingsProvider);
final expressionStyle = settings.tabExpressionStyle;
final characterId = settings.tabCharacterStyleId;
final animIntensity = settings.animationIntensity.durationMultiplier;
int adjacentFor(int index) {
if (index == currentIndex) return 0;
if (index < currentIndex) return -1;
return 1;
}
Widget buildSpriteIcon(TabSpriteType type, int index, String label) {
return TabIconSprite(
type: type,
label: label,
isSelected: index == currentIndex,
adjacentDirection: adjacentFor(index),
animationIntensity: animIntensity,
characterId: characterId,
eyeScale: expressionStyle.eyeScale,
mouthCurve: expressionStyle.mouthCurve,
bounceMultiplier: expressionStyle.bounceMultiplier,
);
}
final Widget bottomBar = (_isOhos && !liquidGlassReady)
? _buildFallbackNavBar(context, currentIndex, ext, settings, ref)
: GlassBottomBar(
tabs: [
GlassBottomBarTab(
label: '',
icon: buildSpriteIcon(TabSpriteType.home, 0, '闲言'),
activeIcon: buildSpriteIcon(TabSpriteType.home, 0, '闲言'),
glowColor: const Color(0xFFE8E8ED),
),
GlassBottomBarTab(
label: '',
icon: badges.Badge(
showBadge: unreadCount > 0,
badgeContent: const Text(
'',
style: TextStyle(
color: Colors.white,
fontSize: 9,
fontWeight: FontWeight.bold,
),
),
badgeStyle: const badges.BadgeStyle(
badgeColor: CupertinoColors.systemRed,
padding: EdgeInsets.all(3),
),
position: badges.BadgePosition.topEnd(top: -4, end: -6),
child: buildSpriteIcon(TabSpriteType.discover, 1, '发现'),
),
activeIcon: badges.Badge(
showBadge: unreadCount > 0,
badgeContent: const Text(
'',
style: TextStyle(
color: Colors.white,
fontSize: 9,
fontWeight: FontWeight.bold,
),
),
badgeStyle: const badges.BadgeStyle(
badgeColor: CupertinoColors.systemRed,
padding: EdgeInsets.all(3),
),
position: badges.BadgePosition.topEnd(top: -4, end: -6),
child: buildSpriteIcon(TabSpriteType.discover, 1, '发现'),
),
glowColor: const Color(0xFFE8E8ED),
),
GlassBottomBarTab(
label: '',
icon: buildSpriteIcon(TabSpriteType.profile, 2, '我的'),
activeIcon: buildSpriteIcon(TabSpriteType.profile, 2, '我的'),
glowColor: const Color(0xFFE8E8ED),
),
],
selectedIndex: currentIndex,
onTabSelected: (index) => _onTabTap(context, index),
quality: GlassQuality.premium,
selectedIconColor: ext.isDark ? Colors.white : ext.accent,
unselectedIconColor: ext.isDark
? Colors.white38
: const Color(0xFFAEAEB2),
barHeight: 68,
barBorderRadius: 34,
horizontalPadding: 16,
verticalPadding: 16,
indicatorColor: ext.isDark
? Colors.white.withValues(alpha: 0.08)
: Colors.black.withValues(alpha: 0.04),
indicatorSettings: LiquidGlassSettings(
thickness: 40,
blur: 25,
refractiveIndex: 1.8,
chromaticAberration: 1.2,
lightIntensity: 3.5,
ambientStrength: 1.2,
glassColor: ext.isDark
? const Color.from(alpha: 0.18, red: 1, green: 1, blue: 1)
: const Color.from(alpha: 0.12, red: 1, green: 1, blue: 1),
),
glassSettings: LiquidGlassSettings(
thickness: 30,
blur: 1.5,
refractiveIndex: 1.5,
chromaticAberration: 0.8,
lightIntensity: 1.2,
saturation: 1.0,
ambientStrength: 0.6,
glassColor: ext.isDark
? const Color.from(alpha: 0.08, red: 1, green: 1, blue: 1)
: const Color.from(alpha: 0.05, red: 1, green: 1, blue: 1),
),
magnification: 1.12,
innerBlur: 1.5,
glowOpacity: 0.4,
glowBlurRadius: 24,
glowSpreadRadius: 4,
);
return CelebrationOverlay(
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: ext.isDark
? Brightness.light
: Brightness.dark,
statusBarBrightness: ext.isDark ? Brightness.dark : Brightness.light,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarIconBrightness: ext.isDark
? Brightness.light
: Brightness.dark,
systemNavigationBarDividerColor: Colors.transparent,
),
child: PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, _) {
if (didPop) return;
},
child: Scaffold(
extendBody: true,
body: Stack(children: [child]),
bottomNavigationBar: bottomBar,
),
),
),
);
}
void _onTabTap(BuildContext context, int index) {
child.goBranch(index, initialLocation: index == child.currentIndex);
}
Widget _buildFallbackNavBar(
BuildContext context,
int currentIndex,
AppThemeExtension ext,
ThemeSettingsState settings,
WidgetRef ref,
) {
final expressionStyle = settings.tabExpressionStyle;
final characterId = settings.tabCharacterStyleId;
final animIntensity = settings.animationIntensity.durationMultiplier;
final unreadCount = ref.watch(chatProvider).unreadCount;
final t = ref.watch(translationsProvider);
return GlassBottomNavBar(
items: [
GlassBottomNavBarItem(spriteType: TabSpriteType.home, label: t.navHome),
GlassBottomNavBarItem(
spriteType: TabSpriteType.discover,
label: t.navDiscover,
badgeCount: unreadCount,
),
GlassBottomNavBarItem(spriteType: TabSpriteType.profile, label: t.navProfile),
],
selectedIndex: currentIndex,
onTabSelected: (index) => _onTabTap(context, index),
ext: ext,
animationIntensity: animIntensity,
expressionStyle: expressionStyle,
characterId: characterId,
);
}
}