import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:provider/provider.dart'; import '../../../core/theme/app_color_scheme.dart'; import '../../../providers/auth_provider.dart'; import '../home/home_page.dart'; import '../market/market_page.dart'; import '../trade/trade_page.dart'; import '../asset/asset_page.dart'; import '../mine/mine_page.dart'; /// 底部导航项 class _NavItem { final String label; final IconData icon; final Widget page; const _NavItem({required this.label, required this.icon, required this.page}); } /// 主页面 - "The Kinetic Vault" 设计风格 class MainPage extends StatefulWidget { const MainPage({super.key}); @override State createState() => _MainPageState(); } class _MainPageState extends State { int _currentIndex = 0; final Set _loadedPages = {0}; static final _navItems = [ _NavItem(label: '首页', icon: LucideIcons.house, page: const HomePage()), _NavItem(label: '行情', icon: LucideIcons.trendingUp, page: const MarketPage()), _NavItem(label: '交易', icon: LucideIcons.arrowLeftRight, page: const TradePage()), _NavItem(label: '资产', icon: LucideIcons.wallet, page: const AssetPage()), _NavItem(label: '我的', icon: LucideIcons.user, page: const MinePage()), ]; void _onTabChanged(int index) { setState(() { _currentIndex = index; _loadedPages.add(index); }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColorScheme.darkBackground, body: Column( children: [ // 公共顶部导航栏 const _TopAppBar(), // 页面内容 Expanded( child: LazyIndexedStack( index: _currentIndex, loadedIndexes: _loadedPages, children: _navItems.map((item) => item.page).toList(), ), ), ], ), bottomNavigationBar: _BottomNavBar( items: _navItems, currentIndex: _currentIndex, onTap: _onTabChanged, ), ); } } /// 顶部导航栏 - 玻璃拟态效果 class _TopAppBar extends StatelessWidget { const _TopAppBar(); @override Widget build(BuildContext context) { return Container( height: 64, decoration: BoxDecoration( color: AppColorScheme.darkSurfaceBright.withValues(alpha: 0.4), boxShadow: [ BoxShadow( color: AppColorScheme.darkPrimary.withValues(alpha: 0.06), blurRadius: 64, offset: const Offset(0, 32), ), ], ), child: ClipRRect( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 24, sigmaY: 24), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // 左侧头像和标题 Consumer( builder: (context, auth, _) { return Row( children: [ // 头像 Container( width: 32, height: 32, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: AppColorScheme.darkOutlineVariant.withValues(alpha: 0.2), ), ), child: CircleAvatar( backgroundColor: AppColorScheme.darkSurfaceHigh, child: Text( auth.user?.avatarText ?? 'U', style: const TextStyle( color: AppColorScheme.darkPrimary, fontWeight: FontWeight.bold, fontSize: 12, ), ), ), ), const SizedBox(width: 12), // 标题 const Text( '模拟所', style: TextStyle( color: AppColorScheme.darkPrimary, fontWeight: FontWeight.w900, letterSpacing: -0.5, fontSize: 16, ), ), ], ); }, ), // 右侧操作按钮 Row( children: [ _IconBtn( icon: LucideIcons.search, onTap: () {}, ), const SizedBox(width: 8), _IconBtn( icon: LucideIcons.bell, onTap: () {}, ), ], ), ], ), ), ), ), ); } } /// 图标按钮 class _IconBtn extends StatelessWidget { final IconData icon; final VoidCallback onTap; const _IconBtn({required this.icon, required this.onTap}); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.all(8), child: Icon( icon, color: AppColorScheme.darkOnSurfaceVariant, size: 22, ), ), ); } } /// 底部导航栏 - "The Kinetic Vault" 设计风格 class _BottomNavBar extends StatelessWidget { final List<_NavItem> items; final int currentIndex; final ValueChanged onTap; const _BottomNavBar({ required this.items, required this.currentIndex, required this.onTap, }); @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: AppColorScheme.darkSurfaceLow.withValues(alpha: 0.8), borderRadius: const BorderRadius.vertical(top: Radius.circular(32)), border: Border( top: BorderSide( color: AppColorScheme.darkOutlineVariant.withValues(alpha: 0.15), ), ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.5), blurRadius: 40, offset: const Offset(0, -10), ), ], ), child: ClipRRect( borderRadius: const BorderRadius.vertical(top: Radius.circular(32)), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 16, sigmaY: 16), child: SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 24), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: items.asMap().entries.map((entry) { return _NavItemWidget( item: entry.value, isSelected: entry.key == currentIndex, onTap: () => onTap(entry.key), ); }).toList(), ), ), ), ), ), ); } } /// 导航项组件 class _NavItemWidget extends StatelessWidget { final _NavItem item; final bool isSelected; final VoidCallback onTap; const _NavItemWidget({ required this.item, required this.isSelected, required this.onTap, }); @override Widget build(BuildContext context) { final color = isSelected ? AppColorScheme.darkPrimary : AppColorScheme.darkOnSurfaceVariant; return GestureDetector( onTap: onTap, behavior: HitTestBehavior.opaque, child: AnimatedContainer( duration: const Duration(milliseconds: 300), padding: const EdgeInsets.all(12), decoration: isSelected ? BoxDecoration( color: AppColorScheme.darkPrimary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: AppColorScheme.darkPrimary.withValues(alpha: 0.3), blurRadius: 15, spreadRadius: 0, ), ], ) : null, child: Icon( item.icon, color: color, size: 24, ), ), ); } } /// 懒加载 IndexedStack - 只渲染已访问过的页面 class LazyIndexedStack extends StatefulWidget { final int index; final Set loadedIndexes; final List children; const LazyIndexedStack({ super.key, required this.index, required this.loadedIndexes, required this.children, }); @override State createState() => _LazyIndexedStackState(); } class _LazyIndexedStackState extends State { @override Widget build(BuildContext context) { return Stack( children: widget.children.asMap().entries.map((entry) { final isVisible = entry.key == widget.index; final isLoaded = widget.loadedIndexes.contains(entry.key); return Positioned.fill( child: Offstage( offstage: !isVisible, child: TickerMode( enabled: isVisible, child: isLoaded ? entry.value : const SizedBox.shrink(), ), ), ); }).toList(), ); } }