Files
tuiche/lib/pages/device/deviceCopy.dart
2026-04-07 14:49:31 +08:00

909 lines
42 KiB
Dart
Raw Permalink 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/services.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/EventBus.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 ScrollNotificationEvent {}
class BodyDeviceWidgetCopy extends StatefulWidget {
var type;
BodyDeviceWidgetCopy({super.key, required this.type});
@override
State<BodyDeviceWidgetCopy> createState() => _BodyDevicePageState();
}
class _BodyDevicePageState extends State<BodyDeviceWidgetCopy>
with SingleTickerProviderStateMixin {
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;
// 飘动文字的变量(简化版)
late AnimationController _floatController;
double _floatX = 0.0; // 水平位置(-1到1之间表示屏幕宽度的百分比
double _floatY = 0.0; // 垂直位置(-1到1之间表示屏幕高度的百分比
double _speedX = 0.005; // X方向速度向右
double _speedY = -0.005; // Y方向速度负值表示向上45度方向
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();
});
// 初始化飘动动画
_floatController = AnimationController(
duration: Duration(milliseconds: 16), // 约60帧/秒
vsync: this,
)..repeat();
// 监听动画更新
_floatController.addListener(_updateFloatPosition);
}
// 更新飘动文字位置(简化版)
void _updateFloatPosition() {
if (!mounted) return;
// 更新位置
setState(() {
_floatX += _speedX;
_floatY += _speedY;
});
// 边界检测和反弹
final screenWidth = MediaQuery.of(Get.context!).size.width;
final screenHeight = MediaQuery.of(Get.context!).size.height;
// 文字尺寸
final textWidth = 100.rpx;
final textHeight = 30.rpx;
// 转换为实际像素位置
final actualX = (_floatX + 1) / 2 * (screenWidth - textWidth);
final actualY = (_floatY + 1) / 2 * (screenHeight - textHeight);
// 碰到右边界,向左反弹
if (actualX >= screenWidth - textWidth - 10) {
setState(() {
_speedX = -_speedX.abs(); // 向左
});
}
// 碰到左边界,向右反弹
else if (actualX <= 10) {
setState(() {
_speedX = _speedX.abs(); // 向右
});
}
// 碰到下边界,向上反弹
if (actualY >= screenHeight - textHeight - 80) {
// 留出底部空间
setState(() {
_speedY = -_speedY.abs(); // 向上
});
}
// 碰到上边界避开AppBar向下反弹
else if (actualY <= 180) {
// 留出AppBar和标签栏空间
setState(() {
_speedY = _speedY.abs(); // 向下
});
}
}
// 构建飘动文字组件(简化版)
Widget _buildFloatingText() {
return Positioned.fill(
child: IgnorePointer(
// 忽略点击,避免干扰其他交互
child: Transform.translate(
offset: Offset(
(_floatX + 1) / 2 * (MediaQuery.of(context).size.width - 100.rpx),
(_floatY + 1) / 2 * (MediaQuery.of(context).size.height - 30.rpx),
),
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 12.rpx,
vertical: 6.rpx,
),
decoration: BoxDecoration(
// color: themeController.currentColor.sc2.withOpacity(0.15),
color: Colors.transparent,
borderRadius: BorderRadius.circular(15.rpx),
// border: Border.all(
// color: themeController.currentColor.sc2.withOpacity(0.3),
// width: 1.rpx,
// ),
),
child: Text(
'太和e护',
style: TextStyle(
fontSize: 20.rpx,
fontWeight: FontWeight.w500,
color: themeController.currentColor.sc2,
),
),
),
),
),
);
}
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,
);
}
}
}
// 标签切换回调
Future<void> _onTabChanged(int index) async {
_pageController.animateToPage(index,
duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
BodyDeviceController deviceController = Get.find();
if (index == 0) {
deviceController.model.type = 1;
homeController.model.type = 1;
} else if (index == 1) {
deviceController.model.type = 2;
homeController.model.type = 2;
}
deviceController.updateAll();
await deviceController.getDeviceList();
await deviceController.getSleepReport();
}
// 页面切换回调
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();
_floatController.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(getBackgroundImageNoImage()),
fit: BoxFit.fill,
),
),
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
appBar: AppBar(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent, // 状态栏背景色
statusBarIconBrightness: Brightness.light, // 图标颜色Android
statusBarBrightness: Brightness.light, // 图标颜色iOS
),
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: Stack(
children: [
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: NotificationListener<ScrollNotification>(
onNotification:
(ScrollNotification notification) {
if (notification is ScrollStartNotification ||
notification is ScrollUpdateNotification) {
// 发送全局滚动事件
EventBus().emit(ScrollNotificationEvent());
}
return false;
},
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)),
),
),
);
}),
],
),
),
),
],
),
),
// 添加飘动文字(简化版)
_buildFloatingText(),
],
),
),
),
),
),
),
);
}
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,
),
),
],
),
),
);
}
}