Commit 14b0b287 authored by akari's avatar akari

feat: 添加中控页

parent 5c739763
import 'package:flutter_bloc/flutter_bloc.dart';
import 'controller_index_state.dart';
class ControllerIndexCubit extends Cubit<ControllerIndexState> {
ControllerIndexCubit({
required String selectedMetricLabel,
}) : super(ControllerIndexState(selectedMetricLabel: selectedMetricLabel));
void selectMetric(String label) {
emit(state.copyWith(selectedMetricLabel: label));
}
}
import 'package:equatable/equatable.dart';
class ControllerIndexState extends Equatable {
final String selectedMetricLabel;
const ControllerIndexState({
required this.selectedMetricLabel,
});
ControllerIndexState copyWith({
String? selectedMetricLabel,
}) {
return ControllerIndexState(
selectedMetricLabel: selectedMetricLabel ?? this.selectedMetricLabel,
);
}
@override
List<Object?> get props => [selectedMetricLabel];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'cubit/controller_index_cubit.dart';
import 'cubit/controller_index_state.dart';
import 'widgets/monitoring_environment_detail_view.dart';
class MonitoringControllerView extends StatelessWidget {
const MonitoringControllerView({
super.key,
required this.metrics,
required this.selectedMetricLabel,
required this.onBack,
});
final List<MonitoringMetricBO> metrics;
final String selectedMetricLabel;
final VoidCallback onBack;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => ControllerIndexCubit(
selectedMetricLabel: selectedMetricLabel,
),
child: ControllerIndexContent(
metrics: metrics,
onBack: onBack,
),
);
}
}
class ControllerIndexContent extends StatelessWidget {
const ControllerIndexContent({
super.key,
required this.metrics,
required this.onBack,
});
final List<MonitoringMetricBO> metrics;
final VoidCallback onBack;
@override
Widget build(BuildContext context) {
return BlocBuilder<ControllerIndexCubit, ControllerIndexState>(
builder: (context, state) {
return MonitoringEnvironmentDetailView(
metrics: metrics,
selectedLabel: state.selectedMetricLabel,
onBack: onBack,
onMetricSelected: context.read<ControllerIndexCubit>().selectMetric,
);
},
);
}
}
...@@ -8,6 +8,7 @@ import 'package:laki_icu_app/enums/video_stream_mode_enum.dart'; ...@@ -8,6 +8,7 @@ import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart'; import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'package:laki_icu_app/utils/event_bus.dart'; import 'package:laki_icu_app/utils/event_bus.dart';
import '../controller/monitoring_controller_view.dart';
import 'cubit/monitoring_index_cubit.dart'; import 'cubit/monitoring_index_cubit.dart';
import 'cubit/monitoring_index_state.dart'; import 'cubit/monitoring_index_state.dart';
import 'widgets/bluetooth_bind_dialog.dart'; import 'widgets/bluetooth_bind_dialog.dart';
...@@ -39,6 +40,7 @@ class MonitoringIndexContent extends StatefulWidget { ...@@ -39,6 +40,7 @@ class MonitoringIndexContent extends StatefulWidget {
class _MonitoringIndexContentState extends State<MonitoringIndexContent> { class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
late final MonitoringIndexCubit _monitoringIndexCubit; late final MonitoringIndexCubit _monitoringIndexCubit;
StreamSubscription<OpenBluetoothScanEvent>? _bluetoothScanEventSub; StreamSubscription<OpenBluetoothScanEvent>? _bluetoothScanEventSub;
String? _selectedEnvironmentMetricLabel;
@override @override
void initState() { void initState() {
...@@ -69,6 +71,9 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -69,6 +71,9 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
state.metrics.isEmpty) { state.metrics.isEmpty) {
return _buildError(state.error); return _buildError(state.error);
} }
if (_selectedEnvironmentMetricLabel != null) {
return _buildEnvironmentDetail(state);
}
return _buildMainArea(state); return _buildMainArea(state);
}, },
); );
...@@ -154,7 +159,10 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -154,7 +159,10 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
for (int i = 0; i < displayMetrics.length; i++) { for (int i = 0; i < displayMetrics.length; i++) {
children.add( children.add(
Expanded( Expanded(
child: MonitoringMetricCard(metric: displayMetrics[i]), child: MonitoringMetricCard(
metric: displayMetrics[i],
onTap: () => _openEnvironmentDetail(displayMetrics[i]),
),
), ),
); );
if (i < displayMetrics.length - 1) { if (i < displayMetrics.length - 1) {
...@@ -166,6 +174,25 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> { ...@@ -166,6 +174,25 @@ class _MonitoringIndexContentState extends State<MonitoringIndexContent> {
); );
} }
Widget _buildEnvironmentDetail(MonitoringIndexState state) {
final metrics = _environmentMetrics(state.metrics);
return MonitoringControllerView(
metrics: metrics,
selectedMetricLabel: _selectedEnvironmentMetricLabel!,
onBack: () {
setState(() {
_selectedEnvironmentMetricLabel = null;
});
},
);
}
void _openEnvironmentDetail(MonitoringMetricBO metric) {
setState(() {
_selectedEnvironmentMetricLabel = metric.label;
});
}
/// 模式切换回调 /// 模式切换回调
void _onToggleStreamMode() { void _onToggleStreamMode() {
final cubit = _monitoringIndexCubit; final cubit = _monitoringIndexCubit;
......
...@@ -127,7 +127,6 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> { ...@@ -127,7 +127,6 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
_buildPanelStatus(context, state), _buildPanelStatus(context, state),
SizedBox(height: 28.h), SizedBox(height: 28.h),
Expanded(child: _buildDeviceList(context, state)), Expanded(child: _buildDeviceList(context, state)),
_buildLatestData(state),
], ],
), ),
); );
...@@ -138,7 +137,7 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> { ...@@ -138,7 +137,7 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
? '自动发现附近可配对的监护舱设备,搜索中...' ? '自动发现附近可配对的监护舱设备,搜索中...'
: state.bluetoothDevices.isEmpty : state.bluetoothDevices.isEmpty
? '未发现设备,可重新搜索附近蓝牙设备' ? '未发现设备,可重新搜索附近蓝牙设备'
: '自动发现附近可配对的监护舱设备'; : '蓝牙扫描结束,共发现 ${state.bluetoothDevices.length}设备';
return Row( return Row(
children: [ children: [
...@@ -266,143 +265,6 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> { ...@@ -266,143 +265,6 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
await context.read<MonitoringIndexCubit>().unbindBluetoothDevice(); await context.read<MonitoringIndexCubit>().unbindBluetoothDevice();
} }
} }
Widget _buildLatestData(MonitoringIndexState state) {
final report = state.latestMcuReport;
final rawHex = state.latestBluetoothRawHex;
final message = state.bluetoothMessage;
if (report == null &&
(rawHex == null || rawHex.isEmpty) &&
(message == null || message.isEmpty)) {
return SizedBox(height: 10.h);
}
if (report != null) {
return _BluetoothReportSummary(report: report);
}
return Padding(
padding: EdgeInsets.only(top: 18.h),
child: Text(
rawHex != null && rawHex.isNotEmpty ? '最新数据: $rawHex' : message!,
style: TextStyle(
color: rawHex != null && rawHex.isNotEmpty
? const Color(0xFF00F6FF)
: Colors.white.withValues(alpha: 0.66),
fontSize: 22.sp,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
);
}
}
class _BluetoothReportSummary extends StatelessWidget {
const _BluetoothReportSummary({required this.report});
final McuReport report;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
margin: EdgeInsets.only(top: 18.h),
padding: EdgeInsets.symmetric(horizontal: 18.w, vertical: 14.h),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.18),
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.white.withValues(alpha: 0.12)),
),
child: Row(
children: [
Expanded(
flex: 2,
child: Text(
report.sn.isEmpty ? 'SN --' : 'SN ${report.sn}',
style: TextStyle(
color: const Color(0xFF00F6FF),
fontSize: 22.sp,
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
_BluetoothReportMetric(
label: '温度',
value: report.mainTemperature.toStringAsFixed(1),
unit: '℃',
),
_BluetoothReportMetric(
label: '湿度',
value: '${report.humidity}',
unit: '%',
),
_BluetoothReportMetric(
label: '氧气',
value: '${report.oxygenConcentration}',
unit: '%',
),
_BluetoothReportMetric(
label: 'CO2',
value: '${report.co2Measurement}',
unit: 'ppm',
),
],
),
);
}
}
class _BluetoothReportMetric extends StatelessWidget {
const _BluetoothReportMetric({
required this.label,
required this.value,
required this.unit,
});
final String label;
final String value;
final String unit;
@override
Widget build(BuildContext context) {
return SizedBox(
width: 128.w,
child: RichText(
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: TextSpan(
children: [
TextSpan(
text: '$label ',
style: TextStyle(
color: Colors.white.withValues(alpha: 0.58),
fontSize: 20.sp,
),
),
TextSpan(
text: value,
style: TextStyle(
color: Colors.white,
fontSize: 23.sp,
fontWeight: FontWeight.w600,
),
),
TextSpan(
text: unit,
style: TextStyle(
color: Colors.white.withValues(alpha: 0.72),
fontSize: 19.sp,
),
),
],
),
),
);
}
} }
class _BluetoothDeviceRow extends StatelessWidget { class _BluetoothDeviceRow extends StatelessWidget {
......
...@@ -3,7 +3,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; ...@@ -3,7 +3,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_switch/flutter_switch.dart'; import 'package:flutter_switch/flutter_switch.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart'; import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
class MonitoringMetricCard extends StatelessWidget { class MonitoringMetricCard extends StatelessWidget {
final MonitoringMetricBO metric; final MonitoringMetricBO metric;
...@@ -13,25 +12,32 @@ class MonitoringMetricCard extends StatelessWidget { ...@@ -13,25 +12,32 @@ class MonitoringMetricCard extends StatelessWidget {
/// 开关切换回调 /// 开关切换回调
final ValueChanged<bool>? onSwitchToggle; final ValueChanged<bool>? onSwitchToggle;
/// 卡片点击回调
final VoidCallback? onTap;
const MonitoringMetricCard({ const MonitoringMetricCard({
super.key, super.key,
required this.metric, required this.metric,
this.switchValue = false, this.switchValue = false,
this.onSwitchToggle, this.onSwitchToggle,
this.onTap,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final displayLabel = _displayLabel(metric.label); final displayLabel = _displayLabel(metric.label);
return Container( return GestureDetector(
decoration: BoxDecoration( onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Container(
decoration: const BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage('lib/assets/monitoring/box_bg_325x372.png'), image: AssetImage('lib/assets/monitoring/box_bg_325x372.png'),
fit: BoxFit.fill, fit: BoxFit.fill,
), ),
// border: BoxBorder.all(width: 1.w,color: Colors.red) // border: BoxBorder.all(width: 1.w,color: Colors.red)
), ),
padding: EdgeInsets.only(left: 10.w,right: 10.w, top: 54.h), padding: EdgeInsets.only(left: 10.w, right: 10.w, top: 54.h),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
...@@ -61,7 +67,7 @@ class MonitoringMetricCard extends StatelessWidget { ...@@ -61,7 +67,7 @@ class MonitoringMetricCard extends StatelessWidget {
value: switchValue, value: switchValue,
onToggle: onSwitchToggle ?? (_) {}, onToggle: onSwitchToggle ?? (_) {},
activeColor: const Color(0xFF75C1DD), activeColor: const Color(0xFF75C1DD),
inactiveColor: Color(0xFF0A1B34), inactiveColor: const Color(0xFF0A1B34),
toggleSize: 22.w, toggleSize: 22.w,
padding: 4.w, padding: 4.w,
), ),
...@@ -71,7 +77,7 @@ class MonitoringMetricCard extends StatelessWidget { ...@@ -71,7 +77,7 @@ class MonitoringMetricCard extends StatelessWidget {
), ),
// const Spacer(), // const Spacer(),
Container( Container(
padding: EdgeInsets.only(left: 32.w,top: 24.h), padding: EdgeInsets.only(left: 32.w, top: 24.h),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
...@@ -98,17 +104,12 @@ class MonitoringMetricCard extends StatelessWidget { ...@@ -98,17 +104,12 @@ class MonitoringMetricCard extends StatelessWidget {
), ),
], ],
), ),
), ),
SizedBox(height: 22.h), SizedBox(height: 22.h),
Container( Container(
width: 220.w, width: 220.w,
margin: EdgeInsets.only(left: 32.w,), margin: EdgeInsets.only(
decoration: BoxDecoration( left: 32.w,
// border: Border.all(
// color: Colors.red,
// width: 1.w
// )
), ),
child: Stack( child: Stack(
clipBehavior: Clip.none, clipBehavior: Clip.none,
...@@ -117,7 +118,7 @@ class MonitoringMetricCard extends StatelessWidget { ...@@ -117,7 +118,7 @@ class MonitoringMetricCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"设定24"+metric.unit, '设定24${metric.unit}',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 24.sp, fontSize: 24.sp,
...@@ -159,6 +160,7 @@ class MonitoringMetricCard extends StatelessWidget { ...@@ -159,6 +160,7 @@ class MonitoringMetricCard extends StatelessWidget {
) )
], ],
), ),
),
); );
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment