Files
flutterApp/lib/features/main_container/presentation/main_wrapper.dart
Songzex 0db64a96e7 1.优化补足了首页下拉刷新中对获取场站接口的加入,确保能够实时获取数据并渲染。
2.优化修改了tcp重连机制,具体操作:断开重连时候不再建立新链接 而是将旧链接重置,并重新发送数据。
3.大面积的给项目中的各个页面 手动接入了多语言的支持。同时也调整了相应带来的问题英文长度较大影响原有的样式的问题,已解决了对应的样式不和谐问题。保证了外观的统一性。
4.给项目中的主要操作加了几个核心的操作比如远程遥控和路径规划添加了双层的保护,设置里安全模式,即在此期间对服务器推送的重复登录操作后的自动退出登录做一个拦截 保证操作的安全性。
2026-07-02 16:04:30 +08:00

312 lines
11 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.

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';
class MainWrapper extends StatefulWidget {
const MainWrapper({super.key});
@override
State<MainWrapper> createState() => _MainWrapperState();
}
class _MainWrapperState extends State<MainWrapper> {
int _currentIndex = 0;
final PageController _pageController = PageController();
final List<GlobalKey> _tabKeys = [];
double? _circleLeft;
@override
void initState() {
super.initState();
// 🔥 确保 FloatBarSettingService 已初始化
GetIt.I<FloatBarSettingService>();
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateCirclePosition();
});
}
void _updateCirclePosition() {
if (_tabKeys.isEmpty || _currentIndex >= _tabKeys.length) return;
final key = _tabKeys[_currentIndex];
final renderBox = key.currentContext?.findRenderObject() as RenderBox?;
if (renderBox != null) {
setState(() {
_circleLeft = renderBox.localToGlobal(Offset.zero).dx;
});
}
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
void _onTabChanged(int index) {
setState(() {
_currentIndex = index;
});
_pageController.jumpToPage(index);
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateCirclePosition();
});
}
IconData _getIconData(String iconName) {
switch (iconName) {
case 'home_rounded':
return Icons.home_rounded;
case 'grid_view_rounded':
return Icons.grid_view_rounded;
case 'devices_rounded':
return Icons.devices_rounded;
case 'auto_awesome_rounded':
return Icons.auto_awesome_rounded;
case 'warning_amber_rounded':
return Icons.warning_amber_rounded;
case 'assignment_rounded':
return Icons.assignment_rounded;
case 'post_add_rounded':
return Icons.post_add_rounded;
case 'person_rounded':
return Icons.person_rounded;
default:
return Icons.home_rounded;
}
}
double _getIndicatorPosition(int itemCount, int currentIndex) {
final screenWidth = MediaQuery.of(context).size.width;
final itemWidth = screenWidth / itemCount;
return currentIndex * itemWidth;
}
double _getCircularLeftPosition(int itemCount, int currentIndex) {
final screenWidth = MediaQuery.of(context).size.width;
final itemWidth = screenWidth / itemCount;
// 圆形应该在每个Tab项的中心
final tabCenterX = 12 + (currentIndex * itemWidth) + (itemWidth / 2);
final circularLeft = tabCenterX - 22; // 22 = 44/2,让圆形中心对齐Tab中心
// 边界限制
final maxLeft = screenWidth - 12 - 44;
return circularLeft.clamp(12.0, maxLeft);
}
Widget _buildCurrentPage(TabConfigItem tab) {
switch (tab.id) {
case 'home_v2':
return BlocProvider(
create: (_) => sl<HomeV2Bloc>(),
child: const HomeV2Page(),
);
case 'home':
return const HomePage();
case 'device':
return const DeviceStatusPage();
case 'ai':
return const AiPage();
case 'warning':
return BlocProvider(
create: (_) => sl<AlarmCubit>(),
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) {
return BlocBuilder<TabConfigCubit, TabConfigState>(
builder: (context, state) {
if (state is! TabConfigLoaded) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
final config = state.config;
final enabledItems = config.enabledItems;
if (enabledItems.isEmpty) {
return const Scaffold(body: Center(child: Text('请至少启用一个 Tab')));
}
if (_currentIndex >= enabledItems.length) {
_currentIndex = 0;
}
// 初始化 GlobalKey
if (_tabKeys.length != enabledItems.length) {
_tabKeys.clear();
for (int i = 0; i < enabledItems.length; i++) {
_tabKeys.add(GlobalKey());
}
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateCirclePosition();
});
}
return Scaffold(
extendBody: false,
body: Stack(
children: [
PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
children: enabledItems
.map((tab) => _buildCurrentPage(tab))
.toList(),
),
// 悬浮条 - 可拖动的悬浮按钮
ValueListenableBuilder<bool>(
valueListenable: FloatBarSettingService.settingNotifier,
builder: (context, isEnabled, child) {
return Visibility(
visible: isEnabled,
child: const FloatBarWidget(),
);
},
),
],
),
bottomNavigationBar: Container(
decoration: const BoxDecoration(color: Colors.white),
child: SafeArea(
top: false,
child: LayoutBuilder(
builder: (context, constraints) {
return Container(
margin: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
padding: const EdgeInsets.symmetric(vertical: 2),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: const Color(0xFFE5E6EB),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Stack(
children: [
// 滑动圆形背景
if (_circleLeft != null)
AnimatedPositioned(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
left: _circleLeft! - 8, // 减去外层 margin + 向右偏移
top: 6,
child: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: const Color(0xFFF2F3F5),
shape: BoxShape.circle,
border: Border.all(
color: const Color(0xFFE5E6EB),
width: 1,
),
),
),
),
// Tab 项
Row(
children: enabledItems.asMap().entries.map((entry) {
final index = entry.key;
final item = entry.value;
final isSelected = _currentIndex == index;
return Expanded(
key: _tabKeys[index],
child: InkWell(
onTap: () => _onTabChanged(index),
child: SizedBox(
height: 56,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
_getIconData(item.icon),
size: 24,
color: isSelected
? const Color(0xFF1D2129)
: const Color(0xFF86909C),
),
const SizedBox(height: 4),
Text(
item.name,
style: TextStyle(
fontSize: 11,
color: isSelected
? const Color(0xFF1D2129)
: const Color(0xFF86909C),
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w400,
),
),
],
),
),
),
);
}).toList(),
),
],
),
);
},
),
),
),
);
},
);
}
}