Files
tuiche/lib/pages/device/BodyDeviceWidget.dart
2025-12-01 15:41:34 +08:00

727 lines
34 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 'dart:async';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/NullDataComponentWidget.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart';
import 'package:vbvs_app/controller/device/body_device_controller.dart';
import 'package:vbvs_app/controller/home/home_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/pages/device/component/DeviceDataComponentWidget.dart';
class BodyDeviceWidget extends StatefulWidget {
var type;
BodyDeviceWidget({super.key, required this.type});
@override
State<BodyDeviceWidget> createState() => _BodyDevicePageState();
}
class _BodyDevicePageState extends State<BodyDeviceWidget> {
final ThemeController themeController = Get.find();
final BodyDeviceController bodyDeviceController = Get.find();
HomeController homeController = Get.find();
final GlobalKey addIconKey = GlobalKey();
final ScrollController _myDeviceScrollController = ScrollController();
final ScrollController _cloudDeviceScrollController = ScrollController();
OverlayEntry? _popupEntry;
Timer? _timer;
late PageController _pageController;
void _showPopup() {
final renderBox =
addIconKey.currentContext?.findRenderObject() as RenderBox?;
if (renderBox == null) return;
final position = renderBox.localToGlobal(Offset.zero);
final size = renderBox.size;
double popupWidth = 190.rpx;
_popupEntry?.remove();
_popupEntry = OverlayEntry(
builder: (context) => Stack(
children: [
ModalBarrier(
dismissible: true,
color: Colors.transparent,
onDismiss: _hidePopup,
),
Positioned(
top: position.dy + size.height + 26.rpx,
left: position.dx + size.width - popupWidth - 40.rpx,
child: Material(
color: Colors.transparent,
child: Container(
width: popupWidth,
padding: EdgeInsets.all(20.rpx),
decoration: BoxDecoration(
color: themeController.currentColor.sc17,
borderRadius: BorderRadius.circular(12.rpx),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.5),
blurRadius: 12.rpx,
spreadRadius: 2.rpx,
offset: Offset(0, 6.rpx),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: 11.rpx),
ClickableContainer(
padding: EdgeInsets.symmetric(vertical: 10.rpx),
backgroundColor: Colors.transparent,
highlightColor:
themeController.currentColor.sc16.withOpacity(0.1),
borderRadius: 0.rpx,
onTap: () {
_hidePopup();
BlueteethBindController blueteethBindController =
Get.find();
blueteethBindController.returnPage = 1;
Get.toNamed("/deviceType");
},
child: Container(
width: double.infinity,
child: Center(
child: Text(
'蓝牙绑定.标题'.tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc3,
),
),
),
),
),
SizedBox(height: 13.rpx),
],
),
),
),
),
],
),
);
Overlay.of(context)!.insert(_popupEntry!);
}
void _hidePopup() {
_popupEntry?.remove();
_popupEntry = null;
}
@override
void initState() {
bodyDeviceController.keyWord.value = "";
super.initState();
// 初始化PageController根据当前类型设置初始页面
_pageController = PageController(
initialPage: bodyDeviceController.model.type == 1 ? 0 : 1);
// 处理传入的type参数
if (widget.type != null && widget.type is Map) {
final bindType = widget.type['bind_type'];
final mac = widget.type['mac'];
if (bindType != null) {
bodyDeviceController.model.type = bindType;
homeController.model.type = bindType;
// 更新PageController到正确的位置
_pageController = PageController(
initialPage: bodyDeviceController.model.type == 1 ? 0 : 1);
}
_fetchDeviceList().then((_) {
if (mac != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToDeviceWithMac(mac);
});
}
});
} else {
_fetchDeviceList();
}
_timer = Timer.periodic(Duration(seconds: 5), (timer) {
_fetchDeviceList();
});
}
Future<void> _fetchDeviceList() async {
await bodyDeviceController
.getDeviceList(key: bodyDeviceController.keyWord.value)
.then((apiResponse) {
if (apiResponse.code != HttpStatusCodes.ok) {
TopSlideNotification.show(
Get.context!,
text: apiResponse.msg!,
textColor: themeController.currentColor.sc9,
);
}
});
}
void _scrollToDeviceWithMac(String mac) {
final deviceList = bodyDeviceController.deviceList.value;
final index = deviceList.indexWhere((device) => device['mac'] == mac);
if (index != -1) {
final screenHeight = MediaQuery.of(Get.context!).size.height;
final dynamicItemHeight = (screenHeight * 0.266).rpx;
final itemHeight =
dynamicItemHeight < 501.rpx ? 501.rpx : dynamicItemHeight;
final spacing = 25.rpx;
final targetPosition = index * (itemHeight + spacing);
// 根据当前类型选择对应的ScrollController
final currentScrollController =
bodyDeviceController.model.type == 1
? _myDeviceScrollController
: _cloudDeviceScrollController;
if (currentScrollController.hasClients) {
currentScrollController.animateTo(
targetPosition,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
}
}
}
// 标签切换回调
void _onTabChanged(int index) {
_pageController.animateToPage(index,
duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
}
// 页面切换回调
void _onPageChanged(int index) {
int newType = index == 0 ? 1 : 2;
if (bodyDeviceController.model.type != newType) {
bodyDeviceController.model.type = newType;
homeController.model.type = newType;
bodyDeviceController.updateAll();
homeController.updateAll();
_fetchDeviceList();
}
}
@override
void dispose() {
_timer?.cancel();
_myDeviceScrollController.dispose();
_cloudDeviceScrollController.dispose();
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, bodysize) => GestureDetector(
onTap: () {
_hidePopup();
FocusScope.of(context).unfocus();
},
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/bgNoImg.png'),
fit: BoxFit.fill,
),
),
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: themeController.currentColor.sc17,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(
color: themeController.currentColor.sc3,
),
titleSpacing: 0,
title: Container(
width: double.infinity,
height: 180.rpx,
child: Stack(
alignment: Alignment.center,
children: [
Text(
'体征检测设备.标题'.tr,
style: TextStyle(
fontFamily: 'ReadexPro',
color: themeController.currentColor.sc3,
letterSpacing: 0,
fontSize: 30.rpx,
),
),
Positioned(
left: 0,
child: returnIconButtomAddCallback(() {
bodyDeviceController.getDeviceNum();
bodyDeviceController.getDeviceList();
bodyDeviceController.updateAll();
}),
),
Positioned(
right: 20.rpx,
child: ClickableContainer(
key: addIconKey,
backgroundColor: Colors.transparent,
highlightColor: themeController.currentColor.sc16,
padding: EdgeInsets.all(8.rpx),
onTap: () {
if (_popupEntry == null) {
_showPopup();
} else {
_hidePopup();
}
},
child: SvgPicture.asset(
'assets/img/icon/add.svg',
width: 39.rpx,
height: 39.rpx,
color: themeController.currentColor.sc16,
),
),
),
],
),
),
actions: [],
centerTitle: false,
),
body: GestureDetector(
onTap: _hidePopup,
child: SafeArea(
top: true,
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0, 0.rpx, 0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding:
EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0),
child: Container(
width: double.infinity,
constraints: BoxConstraints(
minHeight: 90.rpx,
),
decoration: BoxDecoration(
color: themeController.currentColor.sc5),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 15.rpx, 30.rpx, 15.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 标签切换部分 - 保持原有样式
Stack(
alignment: Alignment.bottomLeft,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Obx(() {
return ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: themeController
.currentColor.sc3,
borderRadius: 8.rpx,
padding: EdgeInsets.all(0),
onTap: () => _onTabChanged(0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 180.rpx,
alignment: Alignment.center,
child: Text(
'体征检测设备.我的e护'.tr,
style: TextStyle(
fontFamily: 'Inter',
fontSize: AppConstants()
.title_text_fontSize,
letterSpacing: 0.0,
color:
bodyDeviceController
.model
.type ==
2
? themeController
.currentColor
.sc3
: themeController
.currentColor
.sc2,
),
),
),
SizedBox(height: 10.rpx),
],
),
);
}),
Obx(() {
return ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: themeController
.currentColor.sc3,
borderRadius: 8.rpx,
padding: EdgeInsets.all(0),
onTap: () => _onTabChanged(1),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 180.rpx,
alignment: Alignment.center,
child: Text(
'体征检测设备.云关爱'.tr,
style: TextStyle(
fontFamily: 'Inter',
fontSize: AppConstants()
.title_text_fontSize,
letterSpacing: 0.0,
color:
bodyDeviceController
.model
.type ==
1
? themeController
.currentColor
.sc3
: themeController
.currentColor
.sc2,
),
overflow:
TextOverflow.ellipsis,
maxLines: 1,
),
),
SizedBox(height: 10.rpx),
],
),
);
}),
],
),
Obx(() {
// 保持原有的横线宽度180.rpx
double lineWidth = 180.rpx;
return AnimatedPositioned(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
bottom: 0,
left:
bodyDeviceController.model.type == 1
? 0
: 180.rpx,
child: Container(
width: lineWidth,
height: 4.rpx,
decoration: BoxDecoration(
color: themeController
.currentColor.sc2,
borderRadius:
BorderRadius.circular(2.rpx),
),
),
);
}),
],
),
// 搜索框部分保持不变
Container(
width:
MediaQuery.sizeOf(context).width * 0.38,
constraints: BoxConstraints(
minWidth: 285.rpx,
),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(20.rpx),
),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 0, 20.rpx, 0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
height: 80.rpx,
child: Align(
alignment:
AlignmentDirectional(-1, 0),
child: TextFormField(
onChanged: (value) {
bodyDeviceController
.keyWord.value = value;
},
autofocus: false,
obscureText: false,
decoration: InputDecoration(
contentPadding:
EdgeInsets.fromLTRB(
12.rpx, 0, 0.rpx, 0),
isDense: true,
labelStyle: TextStyle(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
),
hintText: '体征检测设备.输入关键词'.tr,
hintStyle: TextStyle(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc4,
),
enabledBorder:
OutlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1.rpx,
),
borderRadius:
BorderRadius.circular(
8.rpx),
),
focusedBorder:
OutlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1.rpx,
),
borderRadius:
BorderRadius.circular(
8.rpx),
),
errorBorder:
OutlineInputBorder(
borderSide: BorderSide(
color: Colors.red,
width: 1.rpx,
),
borderRadius:
BorderRadius.circular(
8.rpx),
),
focusedErrorBorder:
OutlineInputBorder(
borderSide: BorderSide(
color: Colors.red,
width: 1.rpx,
),
borderRadius:
BorderRadius.circular(
8.rpx),
),
filled: false,
fillColor: Colors.white,
),
style: TextStyle(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc3,
),
cursorColor: themeController
.currentColor.sc3,
),
),
),
),
Padding(
padding:
EdgeInsetsDirectional.fromSTEB(
26.rpx, 0, 0, 0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
SizedBox(
height: 40.rpx,
child: VerticalDivider(
thickness: 2.rpx,
color: themeController
.currentColor.sc2,
),
),
ClickableContainer(
backgroundColor:
Colors.transparent,
highlightColor: themeController
.currentColor.sc5,
borderRadius: 6.rpx,
padding: EdgeInsets.zero,
onTap: () async {
await bodyDeviceController
.getDeviceList(
key:
bodyDeviceController
.keyWord
.value);
bodyDeviceController
.updateAll();
},
child: Text(
'体征检测设备.搜索'.tr,
style: TextStyle(
fontFamily: 'Inter',
fontSize: AppConstants()
.normal_text_fontSize,
letterSpacing: 0.0,
color: themeController
.currentColor.sc2,
),
),
),
].divide(SizedBox(width: 14.rpx)),
),
),
],
),
),
),
],
),
),
),
),
// 使用PageView替换原来的单一列表
Expanded(
child: PageView(
controller: _pageController,
onPageChanged: _onPageChanged,
children: [
// 我的e护页面
Obx(() {
final myDeviceList = bodyDeviceController.deviceList.value
.where((device) => device['type'] == 1 || device['bind_type'] == 1)
.toList();
return myDeviceList.isEmpty
? NullDataWidget()
: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 26.rpx, 30.rpx, 0),
child: SingleChildScrollView(
controller: _myDeviceScrollController,
child: Column(
mainAxisSize: MainAxisSize.max,
children: myDeviceList
.map((device) =>
DeviceDataComponentWidget(
device: device))
.toList()
.divide(SizedBox(height: 25.rpx)),
),
),
);
}),
// 云关爱页面
Obx(() {
final cloudDeviceList = bodyDeviceController.deviceList.value
.where((device) => device['type'] == 2 || device['bind_type'] == 2)
.toList();
return cloudDeviceList.isEmpty
? NullDataWidget()
: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 26.rpx, 30.rpx, 0),
child: SingleChildScrollView(
controller: _cloudDeviceScrollController,
child: Column(
mainAxisSize: MainAxisSize.max,
children: cloudDeviceList
.map((device) =>
DeviceDataComponentWidget(
device: device))
.toList()
.divide(SizedBox(height: 25.rpx)),
),
),
);
}),
],
),
),
],
),
),
),
),
),
),
),
);
}
Widget _buildDeviceCard(BuildContext context,
{required String title, required String imageUrl, required String type}) {
return CustomCard(
borderRadius: 20.rpx,
onTap: () {
if (type != null) {
if (type == '1') {
Get.toNamed("/blueteethDevice");
}
}
},
colors: [themeController.currentColor.sc17],
child: Container(
width: double.infinity,
height: MediaQuery.sizeOf(context).height * 0.135,
constraints: BoxConstraints(
minHeight: 220.rpx,
),
padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: TextStyle(
fontFamily: 'Inter',
color: const Color(0xFFC2CED7),
fontSize: 30.rpx,
letterSpacing: 0.0,
),
),
ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Image.asset(
imageUrl,
width: 212.rpx,
height: 168.rpx,
),
),
],
),
),
);
}
}