import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; import 'package:maibu_satabot_v2/features/main_container/presentation/cubit/tab_config_cubit.dart'; import 'package:maibu_satabot_v2/features/main_container/domain/tab_config.dart'; import 'package:maibu_satabot_v2/features/home/presentation/pages/home_page.dart'; import 'package:maibu_satabot_v2/features/v2/home/presentation/pages/home_v2_page.dart'; import 'package:maibu_satabot_v2/features/v2/home/presentation/bloc/home_v2_bloc.dart'; import 'package:maibu_satabot_v2/features/v2/device_list/presentation/pages/device_status_page.dart'; import 'package:maibu_satabot_v2/features/ai/presentation/pages/ai_page.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/pages/alarm_center_page.dart'; import 'package:maibu_satabot_v2/features/v2/waring_center/presentation/bloc/alarm_cubit.dart'; import 'package:maibu_satabot_v2/features/my/presentation/pages/my_page.dart'; import 'package:maibu_satabot_v2/features/v2/my/presentation/pages/my_page.dart' as my_v2; import 'package:maibu_satabot_v2/features/v2/workorder/presentation/pages/workorder_page.dart'; import 'package:maibu_satabot_v2/features/v2/report/presentation/pages/report_page.dart'; import 'package:maibu_satabot_v2/features/v2/device_list/presentation/float_bar/view/float_bar_widget.dart'; import 'package:maibu_satabot_v2/features/v2/device_list/presentation/float_bar/cubit/float_bar_setting_cubit.dart'; import 'package:maibu_satabot_v2/core/di/injection.dart'; import 'package:maibu_satabot_v2/core/theme/AppTheme.dart'; import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart'; class MainWrapper extends StatefulWidget { const MainWrapper({super.key}); @override State createState() => _MainWrapperState(); } class _MainWrapperState extends State { int _currentIndex = 0; final PageController _pageController = PageController(); @override void initState() { super.initState(); // 🔥 确保 FloatBarSettingService 已初始化 GetIt.I(); } @override void dispose() { _pageController.dispose(); super.dispose(); } void _onTabChanged(int index) { setState(() { _currentIndex = index; }); _pageController.jumpToPage(index); } /// Tab 图标:选中用 filled(实心),未选用 outlined(描边),对比更清晰 IconData _navIcon(String iconName, bool selected) { switch (iconName) { case 'home_rounded': return selected ? Icons.home_rounded : Icons.home_outlined; case 'grid_view_rounded': return selected ? Icons.grid_view_rounded : Icons.grid_view_outlined; case 'devices_rounded': return selected ? Icons.devices_rounded : Icons.devices_outlined; case 'auto_awesome_rounded': return selected ? Icons.auto_awesome_rounded : Icons.auto_awesome_outlined; case 'warning_amber_rounded': return selected ? Icons.warning_amber_rounded : Icons.warning_amber_outlined; case 'assignment_rounded': return selected ? Icons.assignment_rounded : Icons.assignment_outlined; case 'post_add_rounded': return selected ? Icons.post_add_rounded : Icons.post_add_outlined; case 'person_rounded': return selected ? Icons.person_rounded : Icons.person_outlined; default: return selected ? Icons.home_rounded : Icons.home_outlined; } } Widget _buildCurrentPage(TabConfigItem tab) { switch (tab.id) { case 'home_v2': return BlocProvider( create: (_) => sl(), child: const HomeV2Page(), ); case 'home': return const HomePage(); case 'device': return const DeviceStatusPage(); case 'ai': return const AiPage(); case 'warning': return BlocProvider( create: (_) => sl(), child: const AlarmCenterPage(), ); case 'workorder': return const WorkOrderPage(); case 'report': return const ReportPage(); case 'my': return const MyPage(); case 'me': return const my_v2.MyPage(); default: return const HomePage(); } } @override Widget build(BuildContext context) { // 🔥 个人角色(roleKey == 'personal')底部导航强制只显示「设备 + 我的」 final roleKey = context.watch().state.user?.roleKey; return BlocBuilder( builder: (context, state) { if (state is! TabConfigLoaded) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } final config = state.config; final enabledItems = config.navItemsForRole(roleKey); if (enabledItems.isEmpty) { return const Scaffold(body: Center(child: Text('请至少启用一个 Tab'))); } if (_currentIndex >= enabledItems.length) { _currentIndex = 0; } return Scaffold( extendBody: false, // 底部导航预留区(SafeArea 那条)与页面同色,消除突兀的底色带 backgroundColor: context.appColors.pageBackground, body: Stack( children: [ PageView( controller: _pageController, // 🔥 启用左右滑动切换 Tab physics: const BouncingScrollPhysics(), onPageChanged: (index) { setState(() { _currentIndex = index; }); }, children: enabledItems .map((tab) => _buildCurrentPage(tab)) .toList(), ), // 悬浮条 - 可拖动的悬浮按钮 ValueListenableBuilder( valueListenable: FloatBarSettingService.settingNotifier, builder: (context, isEnabled, child) { return Visibility( visible: isEnabled, child: const FloatBarWidget(), ); }, ), ], ), bottomNavigationBar: SafeArea( top: false, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), child: LayoutBuilder( builder: (context, constraints) { final itemCount = enabledItems.length; // Tab 较少时收缩整体宽度并居中,Tab 多时铺满可用宽度 const double preferredTabWidth = 84; const double barPaddingH = 6; final double maxBarWidth = constraints.maxWidth; final double barWidth = itemCount * preferredTabWidth < maxBarWidth ? itemCount * preferredTabWidth : maxBarWidth; final double tabWidth = (barWidth - barPaddingH * 2) / itemCount; // 选中高亮:包裹「图标 + 文字」的紧凑胶囊 chip const double indicatorH = 46; final double indicatorW = tabWidth - 8 < 54 ? tabWidth - 8 : 54.0; // 用 Align + heightFactor 只水平居中,高度贴合内容; // 切勿用 Center:它在底部栏有界高度约束下会撑满全屏,把 body 挤没 return Align( alignment: Alignment.center, heightFactor: 1.0, child: Container( width: barWidth, padding: const EdgeInsets.symmetric( horizontal: barPaddingH, vertical: 2, ), decoration: BoxDecoration( color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(30), border: Border.all( color: context.appColors.divider, width: 1, ), boxShadow: [ BoxShadow( color: context.appColors.cardShadow, blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: SizedBox( height: 56, child: Stack( children: [ // 蓝色胶囊 chip:纯数学定位,居中包裹图标 + 文字 AnimatedPositioned( duration: const Duration(milliseconds: 280), curve: Curves.easeOutCubic, left: _currentIndex * tabWidth + (tabWidth - indicatorW) / 2, top: (56 - indicatorH) / 2, width: indicatorW, height: indicatorH, child: Container( decoration: BoxDecoration( color: context.appColors.primary.withOpacity( 0.12, ), borderRadius: BorderRadius.circular( indicatorH / 2, ), ), ), ), // Tab 项 Row( children: enabledItems.asMap().entries.map((entry) { final index = entry.key; final item = entry.value; final isSelected = _currentIndex == index; return Expanded( child: InkWell( borderRadius: BorderRadius.circular( indicatorH / 2, ), onTap: () => _onTabChanged(index), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ AnimatedScale( scale: isSelected ? 1.1 : 1.0, duration: const Duration( milliseconds: 200, ), curve: Curves.easeOutBack, child: Icon( _navIcon(item.icon, isSelected), size: 22, color: isSelected ? context.appColors.primary : context .appColors.textTertiary, ), ), const SizedBox(height: 2), AnimatedDefaultTextStyle( duration: const Duration( milliseconds: 200, ), style: TextStyle( fontSize: 11, height: 1.0, color: isSelected ? context.appColors.primary : context .appColors.textTertiary, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, ), child: Text(item.name), ), ], ), ), ); }).toList(), ), ], ), ), ), ); }, ), ), ), ); }, ); } }