Commit 59de0036 authored by akari's avatar akari

feat: 软件设置子页面

parent 72d5f64e
......@@ -45,8 +45,7 @@ class BluetoothReadBloc extends Bloc<BluetoothReadEvent, BluetoothReadState> {
for (int i = 0; i < maxRetries; i++) {
try {
info = await _storageService.getBluetoothReadInfo();
Log.warn(
'**************sn 第${i + 1}次获取************: ${info.sn}');
Log.warn('**************sn 第${i + 1}次获取************: ${info.sn}');
if (info.hasSn) {
Log.warn('**************sn 获取成功,停止重试************');
break;
......@@ -75,7 +74,28 @@ class BluetoothReadBloc extends Bloc<BluetoothReadEvent, BluetoothReadState> {
BluetoothReadUpdated event,
Emitter<BluetoothReadState> emit,
) {
emit(state.copyWith(info: event.info, clearError: true));
emit(state.copyWith(
info: _resolveDisplayInfo(event.info), clearError: true));
}
BluetoothReadModel _resolveDisplayInfo(BluetoothReadModel next) {
final currentSn = state.info.sn;
final nextSn = next.sn;
if (currentSn == null ||
currentSn.isEmpty ||
nextSn == null ||
nextSn.isEmpty ||
currentSn == nextSn) {
return next;
}
final currentSide = currentSn[currentSn.length - 1].toUpperCase();
if ((currentSide == 'L' || currentSide == 'R') &&
currentSn.substring(0, currentSn.length - 1) == nextSn) {
return next.copyWith(sn: currentSn);
}
return next;
}
void _onCleared(
......
......@@ -588,10 +588,10 @@ class DeviceControlBloc
// ==================== 护理等级控制事件处理 ====================
/// [用户操作] 设置护理等级(发送给MCU)—— 持久化
void _onCareLevelChanged(
Future<void> _onCareLevelChanged(
DeviceControlCareLevelChanged event,
Emitter<DeviceControlState> emit,
) {
) async {
final newData = state.data.copyWith(
careLevel: state.data.careLevel.copyWith(
currentLevel: event.level,
......@@ -602,7 +602,12 @@ class DeviceControlBloc
userSettings: state.userSettings.copyWith(careLevelValue: event.level),
clearError: true,
));
// TODO: 发送设置到MCU
await _writeControlValueToBluetooth(
identifier: 0xA8,
setValue: '${_careLevelProtocolValue(event.level)}',
errorPrefix: '护理等级',
emit: emit,
);
}
// ==================== 新风进化控制事件处理 ====================
......@@ -653,10 +658,10 @@ class DeviceControlBloc
// ==================== 雾化时间设置事件处理 ====================
/// [用户操作] 设置雾化时间(发送给MCU)—— 持久化
void _onNebulizationTimeChanged(
Future<void> _onNebulizationTimeChanged(
DeviceControlNebulizationTimeChanged event,
Emitter<DeviceControlState> emit,
) {
) async {
final newData = state.data.copyWith(
nebulizationTime: state.data.nebulizationTime.copyWith(
setMinutes: event.minutes,
......@@ -668,16 +673,21 @@ class DeviceControlBloc
state.userSettings.copyWith(nebulizationTimeMinutes: event.minutes),
clearError: true,
));
// TODO: 发送设置到MCU
await _writeControlValueToBluetooth(
identifier: 0x0C,
setValue: event.minutes,
errorPrefix: '雾化时间',
emit: emit,
);
}
// ==================== 消毒时间设置事件处理 ====================
/// [用户操作] 设置消毒时间(发送给MCU)—— 持久化
void _onDisinfectionTimeChanged(
Future<void> _onDisinfectionTimeChanged(
DeviceControlDisinfectionTimeChanged event,
Emitter<DeviceControlState> emit,
) {
) async {
final newData = state.data.copyWith(
disinfectionTime: state.data.disinfectionTime.copyWith(
setMinutes: event.minutes,
......@@ -689,7 +699,12 @@ class DeviceControlBloc
state.userSettings.copyWith(disinfectionTimeMinutes: event.minutes),
clearError: true,
));
// TODO: 发送设置到MCU
await _writeControlValueToBluetooth(
identifier: 0x0D,
setValue: event.minutes,
errorPrefix: '消毒时间',
emit: emit,
);
}
// ==================== 开放供氧控制事件处理 ====================
......@@ -735,10 +750,10 @@ class DeviceControlBloc
}
/// [用户操作] 清零供氧计时
void _onOxygenTimerCleared(
Future<void> _onOxygenTimerCleared(
DeviceControlOxygenTimerCleared event,
Emitter<DeviceControlState> emit,
) {
) async {
final newData = state.data.copyWith(
oxygenTimer: const OxygenTimerBO(
hours: '00',
......@@ -747,7 +762,12 @@ class DeviceControlBloc
),
);
emit(state.copyWith(data: newData, clearError: true));
// TODO: 发送清零指令到MCU
await _writeControlValueToBluetooth(
identifier: 0x29,
setValue: '0',
errorPrefix: '供氧累计时间清零',
emit: emit,
);
}
// ==================== 循环模式控制事件处理 ====================
......@@ -984,6 +1004,21 @@ class DeviceControlBloc
}
}
int _careLevelProtocolValue(CareLevel level) {
switch (level) {
case CareLevel.off:
return 0;
case CareLevel.level3:
return 1;
case CareLevel.level2:
return 2;
case CareLevel.level1:
return 3;
case CareLevel.special:
return 4;
}
}
List<int>? _buildControlValueCommand({
required int identifier,
required String setValue,
......
This diff is collapsed.
......@@ -150,8 +150,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
).copyWith(sn: reportSn.isEmpty ? null : reportSn);
if (bluetoothReadInfo.hasSn) {
await _storageService.saveBluetoothReadSn(bluetoothReadInfo.sn!);
await _connectMqttForDeviceSn(bluetoothReadInfo.sn!);
await _fetchCabinDetailAndConnectVideo(bluetoothReadInfo.sn!);
unawaited(_fetchCabinDetailAndConnectVideo(bluetoothReadInfo.sn!));
}
eventBus.emit(
BluetoothReadInfoChangedEvent(bluetoothReadInfo),
......@@ -232,18 +231,24 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
final boundDevice = await _storageService.getBoundBluetoothDevice();
if (isClosed) return;
final deviceId = boundDevice['deviceId'];
final bluetoothReadInfo = await _storageService.getBluetoothReadInfo();
if (isClosed) return;
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG load_bound_device '
'deviceId=$deviceId deviceName=${boundDevice['deviceName']} '
'hasSn=${bluetoothReadInfo.hasSn}',
'hasDeviceId=${deviceId != null && deviceId.isNotEmpty}',
);
emit(state.copyWith(
boundBluetoothDeviceId:
deviceId == null || deviceId.isEmpty ? null : deviceId,
boundBluetoothDeviceName: boundDevice['deviceName'],
));
if (deviceId != null && deviceId.isNotEmpty) {
_isAutoReconnectEnabled = true;
await _connectBoundBluetoothDevice();
if (isClosed) return;
}
final bluetoothReadInfo = await _storageService.getBluetoothReadInfo();
if (isClosed) return;
if (bluetoothReadInfo.hasSn) {
final sn = _appendBluetoothSideToSn(bluetoothReadInfo.sn!);
if (sn.isNotEmpty) {
......@@ -253,14 +258,9 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
BluetoothReadInfoChangedEvent(bluetoothReadInfo.copyWith(sn: sn)),
);
}
_connectMqttForDeviceSn(sn);
_fetchCabinDetailAndConnectVideo(sn);
}
}
if (deviceId != null && deviceId.isNotEmpty) {
_isAutoReconnectEnabled = true;
_connectBoundBluetoothDevice();
}
}
/// 页面初始化入口。
......@@ -288,8 +288,10 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
final cameraSn = state.cabinCameraSn;
final wifiPwd = state.cabinWifiPwd;
if (cameraSn == null || cameraSn.isEmpty ||
wifiPwd == null || wifiPwd.isEmpty) {
if (cameraSn == null ||
cameraSn.isEmpty ||
wifiPwd == null ||
wifiPwd.isEmpty) {
debugPrint('[MonitoringIndexCubit] WebRTC 凭证未就绪,跳过连接');
return;
}
......@@ -313,8 +315,10 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
final cameraSn = state.cabinCameraSn;
final wifiPwd = state.cabinWifiPwd;
if (cameraSn == null || cameraSn.isEmpty ||
wifiPwd == null || wifiPwd.isEmpty) {
if (cameraSn == null ||
cameraSn.isEmpty ||
wifiPwd == null ||
wifiPwd.isEmpty) {
debugPrint('[MonitoringIndexCubit] P2P 凭证未就绪,跳过连接');
return;
}
......@@ -461,6 +465,18 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
}
}
Future<void> stopBluetoothScan() async {
await _bluetoothManager.stopScan();
if (!isClosed) {
emit(state.copyWith(isBluetoothScanning: false));
}
}
Future<void> connectBoundBluetoothDevice() async {
_isAutoReconnectEnabled = true;
await _connectBoundBluetoothDevice();
}
Future<void> bindBluetoothDevice(BluetoothScanDevice device) async {
if (state.isBluetoothBinding) return;
......@@ -472,6 +488,7 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
));
try {
await stopBluetoothScan();
await _bluetoothManager.connectToDevice(device);
if (!_bluetoothManager.isConnected) {
throw StateError('蓝牙连接未就绪');
......@@ -573,16 +590,15 @@ class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
);
try {
await _bluetoothManager.connectToDeviceId(deviceId, prescan: true);
await _bluetoothManager.connectToDeviceId(deviceId);
debugPrint(
'🔥🔥🔥 BLE_CONNECT_DEBUG auto_connect_success '
'deviceId=$deviceId isConnected=${_bluetoothManager.isConnected}',
);
if (!isClosed) {
emit(state.copyWith(
bluetoothMessage: _bluetoothManager.isConnected
? '蓝牙设备已自动连接'
: '蓝牙自动连接未就绪,等待重试',
bluetoothMessage:
_bluetoothManager.isConnected ? '蓝牙设备已自动连接' : '蓝牙自动连接未就绪,等待重试',
));
}
if (_bluetoothManager.isConnected) {
......
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
......@@ -91,7 +93,11 @@ class _BluetoothBindDialogState extends State<BluetoothBindDialog> {
),
),
IconButton(
onPressed: () => Navigator.of(context).pop(),
onPressed: () {
final cubit = context.read<MonitoringIndexCubit>();
Navigator.of(context).pop();
unawaited(cubit.stopBluetoothScan());
},
icon: Icon(Icons.close, color: Colors.white, size: 48.w),
tooltip: '关闭',
padding: EdgeInsets.zero,
......
import 'package:equatable/equatable.dart';
class CustomerServiceIndexState extends Equatable {
const CustomerServiceIndexState();
const CustomerServiceIndexState({
this.title = '联系我们',
this.hotlineLabel = '服务热线',
this.hotline = '400-0000-0000',
this.serviceTimeLabel = '服务时间',
this.serviceTime = '工作日 早上9:00 - 下午19:00',
});
final String title;
final String hotlineLabel;
final String hotline;
final String serviceTimeLabel;
final String serviceTime;
@override
List<Object?> get props => [];
List<Object?> get props => [
title,
hotlineLabel,
hotline,
serviceTimeLabel,
serviceTime,
];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../widgets/settings_panel_frame.dart';
import '../cubit/customer_service_index_cubit.dart';
import '../cubit/customer_service_index_state.dart';
class CustomerServicePanel extends StatelessWidget {
const CustomerServicePanel({super.key});
......@@ -10,18 +13,77 @@ class CustomerServicePanel extends StatelessWidget {
Widget build(BuildContext context) {
return SettingsPanelFrame(
backgroundAsset: settingsAssetPanelMain,
padding: EdgeInsets.all(42.w),
child: Align(
alignment: Alignment.topLeft,
child: Text(
'客户服务',
style: TextStyle(
color: Colors.white,
fontSize: 34.sp,
fontWeight: FontWeight.w800,
padding: EdgeInsets.fromLTRB(42.w, 64.h, 42.w, 48.h),
child: BlocBuilder<CustomerServiceIndexCubit, CustomerServiceIndexState>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
state.title,
style: TextStyle(
color: Colors.white,
fontSize: 38.sp,
fontWeight: FontWeight.w800,
),
),
SizedBox(height: 46.h),
_ContactLine(
label: state.hotlineLabel,
value: state.hotline,
),
SizedBox(height: 30.h),
_ContactLine(
label: state.serviceTimeLabel,
value: state.serviceTime,
),
SizedBox(height: 42.h),
Divider(color: const Color(0xFF4CA7D2).withValues(alpha: 0.36)),
],
);
},
),
);
}
}
class _ContactLine extends StatelessWidget {
const _ContactLine({
required this.label,
required this.value,
});
final String label;
final String value;
@override
Widget build(BuildContext context) {
return Row(
children: [
SizedBox(
width: 220.w,
child: Text(
label,
style: TextStyle(
color: Colors.white,
fontSize: 29.sp,
fontWeight: FontWeight.w800,
),
),
),
),
Expanded(
child: Text(
value,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 29.sp,
fontWeight: FontWeight.w800,
),
),
),
],
);
}
}
import 'package:equatable/equatable.dart';
class RemoteControlAccount extends Equatable {
const RemoteControlAccount({
required this.phone,
});
final String phone;
@override
List<Object?> get props => [phone];
}
class RemoteControlIndexState extends Equatable {
const RemoteControlIndexState();
const RemoteControlIndexState({
this.productName = 'TSAAS ICU 动物医疗监护舱',
this.modelName = '730SE-CICU-A',
this.appName = 'TSAAS 移动APP',
this.adminAccounts = const [
RemoteControlAccount(phone: '130****1233'),
],
this.monitorAccounts = const [
RemoteControlAccount(phone: '130****1233'),
],
});
final String productName;
final String modelName;
final String appName;
final List<RemoteControlAccount> adminAccounts;
final List<RemoteControlAccount> monitorAccounts;
@override
List<Object?> get props => [];
List<Object?> get props => [
productName,
modelName,
appName,
adminAccounts,
monitorAccounts,
];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../widgets/settings_panel_frame.dart';
import '../cubit/remote_control_index_cubit.dart';
import '../cubit/remote_control_index_state.dart';
class RemoteControlPanel extends StatelessWidget {
const RemoteControlPanel({super.key});
......@@ -10,17 +13,233 @@ class RemoteControlPanel extends StatelessWidget {
Widget build(BuildContext context) {
return SettingsPanelFrame(
backgroundAsset: settingsAssetPanelMain,
padding: EdgeInsets.all(42.w),
child: Align(
alignment: Alignment.topLeft,
child: Text(
'远程控制',
style: TextStyle(
color: Colors.white,
fontSize: 34.sp,
fontWeight: FontWeight.w800,
padding: EdgeInsets.fromLTRB(42.w, 54.h, 42.w, 48.h),
child: BlocBuilder<RemoteControlIndexCubit, RemoteControlIndexState>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_RemoteHeader(state: state),
SizedBox(height: 34.h),
Divider(color: const Color(0xFF4CA7D2).withValues(alpha: 0.36)),
SizedBox(height: 32.h),
Expanded(
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_AccountSection(
title: '管理员',
description: '可以设置更改设备各项参数,启动TSAAS等功能。',
accounts: state.adminAccounts,
),
SizedBox(height: 32.h),
_AccountSection(
title: '监测员',
description: '只能查看设备各项参数,记录诊疗状态等权限。',
accounts: state.monitorAccounts,
),
],
),
),
),
],
);
},
),
);
}
}
class _RemoteHeader extends StatelessWidget {
const _RemoteHeader({required this.state});
final RemoteControlIndexState state;
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(top: 20.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
state.productName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 42.sp,
fontWeight: FontWeight.w800,
),
),
SizedBox(height: 22.h),
Text(
state.modelName,
style: TextStyle(
color: Colors.white,
fontSize: 36.sp,
fontWeight: FontWeight.w800,
),
),
],
),
),
),
SizedBox(width: 40.w),
_AppQrCard(appName: state.appName),
],
);
}
}
class _AppQrCard extends StatelessWidget {
const _AppQrCard({required this.appName});
final String appName;
@override
Widget build(BuildContext context) {
return SizedBox(
width: 226.w,
child: Column(
children: [
Container(
width: 190.w,
height: 190.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18.r),
),
),
SizedBox(height: 20.h),
Text(
appName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
fontWeight: FontWeight.w800,
),
),
],
),
);
}
}
class _AccountSection extends StatelessWidget {
const _AccountSection({
required this.title,
required this.description,
required this.accounts,
});
final String title;
final String description;
final List<RemoteControlAccount> accounts;
@override
Widget build(BuildContext context) {
final items = [
for (final account in accounts)
_AccountActionCard(
text: account.phone,
icon: Icons.manage_accounts_outlined,
),
const _AccountActionCard(
text: '新增账户',
icon: Icons.person_add_alt_1_outlined,
),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
title,
style: TextStyle(
color: Colors.white,
fontSize: 38.sp,
fontWeight: FontWeight.w800,
),
),
SizedBox(width: 24.w),
Expanded(
child: Padding(
padding: EdgeInsets.only(bottom: 6.h),
child: Text(
description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: const Color(0xFFAAEFFF),
fontSize: 17.sp,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
SizedBox(height: 28.h),
Wrap(
spacing: 20.w,
runSpacing: 18.h,
children: items,
),
],
);
}
}
class _AccountActionCard extends StatelessWidget {
const _AccountActionCard({
required this.text,
required this.icon,
});
final String text;
final IconData icon;
@override
Widget build(BuildContext context) {
return Container(
width: 235.w,
height: 92.h,
padding: EdgeInsets.symmetric(horizontal: 22.w),
decoration: settingsButtonDecoration(),
child: Row(
children: [
Icon(
icon,
color: const Color(0xFF78C8F5).withValues(alpha: 0.78),
size: 42.w,
),
SizedBox(width: 16.w),
Expanded(
child: Text(
text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 22.sp,
fontWeight: FontWeight.w800,
),
),
),
],
),
);
}
......
import 'package:equatable/equatable.dart';
class SoftwareUpdateInfo extends Equatable {
const SoftwareUpdateInfo({
required this.title,
required this.currentVersion,
required this.latestVersion,
required this.updateNotes,
required this.actionText,
required this.canUpdate,
this.footerText,
});
final String title;
final String currentVersion;
final String latestVersion;
final List<String> updateNotes;
final String actionText;
final bool canUpdate;
final String? footerText;
@override
List<Object?> get props => [
title,
currentVersion,
latestVersion,
updateNotes,
actionText,
canUpdate,
footerText,
];
}
class SoftwareUpdateIndexState extends Equatable {
const SoftwareUpdateIndexState();
const SoftwareUpdateIndexState({
this.productName = 'TSAAS ICU 动物医疗监护舱',
this.modelName = '730SE-CICU-A',
this.tabletSystem = const SoftwareUpdateInfo(
title: '平板系统',
currentVersion: 'V2.32.4',
latestVersion: 'V2.32.5',
updateNotes: [
'1.重置UI界面',
'2.优化已知bug',
],
actionText: '立即更新',
canUpdate: true,
),
this.deviceFirmware = const SoftwareUpdateInfo(
title: '设备固件OTA(实体按键)',
currentVersion: 'V2.32.4',
latestVersion: 'V2.32.4(当前版本)',
updateNotes: [
'修复bug',
],
actionText: '已是最新版本',
canUpdate: false,
footerText: '*固件升级时间较长,请勿断电',
),
});
final String productName;
final String modelName;
final SoftwareUpdateInfo tabletSystem;
final SoftwareUpdateInfo deviceFirmware;
@override
List<Object?> get props => [];
List<Object?> get props => [
productName,
modelName,
tabletSystem,
deviceFirmware,
];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../widgets/settings_panel_frame.dart';
import '../cubit/software_update_index_cubit.dart';
import '../cubit/software_update_index_state.dart';
class SoftwareUpdatePanel extends StatelessWidget {
const SoftwareUpdatePanel({super.key});
......@@ -10,17 +13,275 @@ class SoftwareUpdatePanel extends StatelessWidget {
Widget build(BuildContext context) {
return SettingsPanelFrame(
backgroundAsset: settingsAssetPanelMain,
padding: EdgeInsets.all(42.w),
child: Align(
alignment: Alignment.topLeft,
child: Text(
'软件更新',
padding: EdgeInsets.fromLTRB(42.w, 54.h, 42.w, 48.h),
child: BlocBuilder<SoftwareUpdateIndexCubit, SoftwareUpdateIndexState>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_UpdateHeader(state: state),
SizedBox(height: 30.h),
Divider(color: const Color(0xFF4CA7D2).withValues(alpha: 0.36)),
SizedBox(height: 36.h),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(child: _UpdateColumn(info: state.tabletSystem)),
Container(
width: 1.w,
margin: EdgeInsets.symmetric(horizontal: 38.w),
color: const Color(0xFF3A9BD2).withValues(alpha: 0.42),
),
Expanded(child: _UpdateColumn(info: state.deviceFirmware)),
],
),
),
],
);
},
),
);
}
}
class _UpdateHeader extends StatelessWidget {
const _UpdateHeader({required this.state});
final SoftwareUpdateIndexState state;
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(top: 22.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
state.productName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 42.sp,
fontWeight: FontWeight.w800,
),
),
SizedBox(height: 22.h),
Text(
state.modelName,
style: TextStyle(
color: Colors.white,
fontSize: 36.sp,
fontWeight: FontWeight.w800,
),
),
],
),
),
),
SizedBox(width: 34.w),
const _CheckUpdateButton(),
],
);
}
}
class _CheckUpdateButton extends StatelessWidget {
const _CheckUpdateButton();
@override
Widget build(BuildContext context) {
return Container(
width: 245.w,
height: 104.h,
padding: EdgeInsets.symmetric(horizontal: 24.w),
decoration: settingsButtonDecoration(active: true),
child: Row(
children: [
Container(
width: 52.w,
height: 52.w,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: const Color(0xFF78C8F5).withValues(alpha: 0.86),
width: 3.w,
),
),
child: Icon(
Icons.arrow_upward,
color: const Color(0xFF78C8F5).withValues(alpha: 0.9),
size: 32.w,
),
),
SizedBox(width: 22.w),
Expanded(
child: Text(
'检查更新',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 23.sp,
fontWeight: FontWeight.w800,
),
),
),
],
),
);
}
}
class _UpdateColumn extends StatelessWidget {
const _UpdateColumn({required this.info});
final SoftwareUpdateInfo info;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
info.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 34.sp,
fontSize: 38.sp,
fontWeight: FontWeight.w800,
),
),
SizedBox(height: 42.h),
_VersionLine(label: '当前版本:', value: info.currentVersion),
SizedBox(height: 20.h),
_VersionLine(label: '最新版本:', value: info.latestVersion),
SizedBox(height: 22.h),
Text(
'更新内容:',
style: TextStyle(
color: Colors.white,
fontSize: 27.sp,
fontWeight: FontWeight.w800,
),
),
SizedBox(height: 18.h),
for (final note in info.updateNotes)
Padding(
padding: EdgeInsets.only(bottom: 6.h),
child: Text(
note,
style: TextStyle(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.w500,
),
),
),
const Spacer(),
Center(child: _UpdateActionButton(info: info)),
if (info.footerText != null) ...[
SizedBox(height: 18.h),
Center(
child: Text(
info.footerText!,
style: TextStyle(
color: Colors.white,
fontSize: 17.sp,
fontWeight: FontWeight.w700,
),
),
),
] else
SizedBox(height: 38.h),
],
);
}
}
class _VersionLine extends StatelessWidget {
const _VersionLine({
required this.label,
required this.value,
});
final String label;
final String value;
@override
Widget build(BuildContext context) {
return Text.rich(
TextSpan(
children: [
TextSpan(text: label),
TextSpan(text: value),
],
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 26.sp,
fontWeight: FontWeight.w800,
),
);
}
}
class _UpdateActionButton extends StatelessWidget {
const _UpdateActionButton({required this.info});
final SoftwareUpdateInfo info;
@override
Widget build(BuildContext context) {
final foreground =
info.canUpdate ? Colors.white : Colors.white.withValues(alpha: 0.42);
return Container(
width: 345.w,
height: 70.h,
padding: EdgeInsets.symmetric(horizontal: 28.w),
decoration: settingsButtonDecoration(active: info.canUpdate),
child: Row(
children: [
Text(
'《《',
style: TextStyle(
color: const Color(0xFF67C9F5)
.withValues(alpha: info.canUpdate ? 0.72 : 0.34),
fontSize: 26.sp,
fontWeight: FontWeight.w700,
),
),
Expanded(
child: Text(
info.actionText,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: TextStyle(
color: foreground,
fontSize: 26.sp,
fontWeight: FontWeight.w800,
),
),
),
Text(
'》》',
style: TextStyle(
color: const Color(0xFF67C9F5)
.withValues(alpha: info.canUpdate ? 0.72 : 0.34),
fontSize: 26.sp,
fontWeight: FontWeight.w700,
),
),
],
),
);
}
......
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