Commit 050ace38 authored by akari's avatar akari

feat: 添加软件设置页面

parent e28bbcc2
import 'package:flutter_bloc/flutter_bloc.dart';
import 'settings_about_index_state.dart';
class SettingsAboutIndexCubit extends Cubit<SettingsAboutIndexState> {
SettingsAboutIndexCubit() : super(const SettingsAboutIndexState());
}
import 'package:equatable/equatable.dart';
class SettingsAboutIndexState extends Equatable {
const SettingsAboutIndexState();
@override
List<Object?> get props => [];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../widgets/settings_about_panel.dart';
import 'cubit/settings_about_index_cubit.dart';
class SettingsAboutIndexView extends StatelessWidget {
const SettingsAboutIndexView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => SettingsAboutIndexCubit(),
child: const SettingsAboutPanel(),
);
}
}
...@@ -3,4 +3,16 @@ import 'settings_index_state.dart'; ...@@ -3,4 +3,16 @@ import 'settings_index_state.dart';
class SettingsIndexCubit extends Cubit<SettingsIndexState> { class SettingsIndexCubit extends Cubit<SettingsIndexState> {
SettingsIndexCubit() : super(const SettingsIndexState()); SettingsIndexCubit() : super(const SettingsIndexState());
void selectMenu(String menu) {
emit(state.copyWith(selectedMenu: menu));
}
void selectTemperatureUnit(bool useCelsius) {
emit(state.copyWith(useCelsius: useCelsius));
}
void setLockScreen(bool lockScreen) {
emit(state.copyWith(lockScreen: lockScreen));
}
} }
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
class SettingsIndexState extends Equatable { class SettingsIndexState extends Equatable {
const SettingsIndexState(); const SettingsIndexState({
this.selectedMenu = '关于本机',
this.language = '简体中文',
this.useCelsius = true,
this.lockScreen = true,
});
final String selectedMenu;
final String language;
final bool useCelsius;
final bool lockScreen;
SettingsIndexState copyWith({
String? selectedMenu,
String? language,
bool? useCelsius,
bool? lockScreen,
}) {
return SettingsIndexState(
selectedMenu: selectedMenu ?? this.selectedMenu,
language: language ?? this.language,
useCelsius: useCelsius ?? this.useCelsius,
lockScreen: lockScreen ?? this.lockScreen,
);
}
@override @override
List<Object?> get props => []; List<Object?> get props => [
selectedMenu,
language,
useCelsius,
lockScreen,
];
} }
import 'package:flutter_bloc/flutter_bloc.dart';
import 'customer_service_index_state.dart';
class CustomerServiceIndexCubit extends Cubit<CustomerServiceIndexState> {
CustomerServiceIndexCubit() : super(const CustomerServiceIndexState());
}
import 'package:equatable/equatable.dart';
class CustomerServiceIndexState extends Equatable {
const CustomerServiceIndexState();
@override
List<Object?> get props => [];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../widgets/settings_title_panel.dart';
import 'cubit/customer_service_index_cubit.dart';
class CustomerServiceIndexView extends StatelessWidget {
const CustomerServiceIndexView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => CustomerServiceIndexCubit(),
child: const SettingsTitlePanel(title: '客户服务'),
);
}
}
import 'package:flutter_bloc/flutter_bloc.dart';
import 'factory_settings_index_state.dart';
class FactorySettingsIndexCubit extends Cubit<FactorySettingsIndexState> {
FactorySettingsIndexCubit() : super(const FactorySettingsIndexState());
}
import 'package:equatable/equatable.dart';
class FactorySettingsIndexState extends Equatable {
const FactorySettingsIndexState();
@override
List<Object?> get props => [];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../widgets/settings_title_panel.dart';
import 'cubit/factory_settings_index_cubit.dart';
class FactorySettingsIndexView extends StatelessWidget {
const FactorySettingsIndexView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => FactorySettingsIndexCubit(),
child: const SettingsTitlePanel(title: '出厂设置'),
);
}
}
import 'package:flutter_bloc/flutter_bloc.dart';
import 'remote_control_index_state.dart';
class RemoteControlIndexCubit extends Cubit<RemoteControlIndexState> {
RemoteControlIndexCubit() : super(const RemoteControlIndexState());
}
import 'package:equatable/equatable.dart';
class RemoteControlIndexState extends Equatable {
const RemoteControlIndexState();
@override
List<Object?> get props => [];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../widgets/settings_title_panel.dart';
import 'cubit/remote_control_index_cubit.dart';
class RemoteControlIndexView extends StatelessWidget {
const RemoteControlIndexView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => RemoteControlIndexCubit(),
child: const SettingsTitlePanel(title: '远程控制'),
);
}
}
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'about/settings_about_index_view.dart';
import 'cubit/settings_index_cubit.dart';
import 'cubit/settings_index_state.dart';
import 'customer_service/customer_service_index_view.dart';
import 'factory_settings/factory_settings_index_view.dart';
import 'remote_control/remote_control_index_view.dart';
import 'software_update/software_update_index_view.dart';
import 'widgets/settings_side_menu.dart';
@RoutePage() @RoutePage()
class SettingsIndexView extends StatelessWidget { class SettingsIndexView extends StatelessWidget {
...@@ -7,11 +18,56 @@ class SettingsIndexView extends StatelessWidget { ...@@ -7,11 +18,56 @@ class SettingsIndexView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const Center( return BlocProvider(
child: Text( create: (_) => SettingsIndexCubit(),
'设置', child: const _SettingsIndexContent(),
style: TextStyle(color: Colors.white, fontSize: 24), );
), }
}
class _SettingsIndexContent extends StatelessWidget {
const _SettingsIndexContent();
@override
Widget build(BuildContext context) {
return BlocBuilder<SettingsIndexCubit, SettingsIndexState>(
builder: (context, state) {
return Padding(
padding: EdgeInsets.fromLTRB(30.w, 28.h, 30.w, 28.h),
child: Row(
children: [
Expanded(
flex: 25,
child: SettingsSideMenu(
selectedMenu: state.selectedMenu,
onSelected: context.read<SettingsIndexCubit>().selectMenu,
),
),
SizedBox(width: 24.w),
Expanded(
flex: 75,
child: _buildSelectedPanel(state.selectedMenu),
),
],
),
);
},
); );
} }
Widget _buildSelectedPanel(String selectedMenu) {
switch (selectedMenu) {
case '远程控制':
return const RemoteControlIndexView();
case '软件更新':
return const SoftwareUpdateIndexView();
case '客户服务':
return const CustomerServiceIndexView();
case '出厂设置':
return const FactorySettingsIndexView();
case '关于本机':
default:
return const SettingsAboutIndexView();
}
}
} }
import 'package:flutter_bloc/flutter_bloc.dart';
import 'software_update_index_state.dart';
class SoftwareUpdateIndexCubit extends Cubit<SoftwareUpdateIndexState> {
SoftwareUpdateIndexCubit() : super(const SoftwareUpdateIndexState());
}
import 'package:equatable/equatable.dart';
class SoftwareUpdateIndexState extends Equatable {
const SoftwareUpdateIndexState();
@override
List<Object?> get props => [];
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../widgets/settings_title_panel.dart';
import 'cubit/software_update_index_cubit.dart';
class SoftwareUpdateIndexView extends StatelessWidget {
const SoftwareUpdateIndexView({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => SoftwareUpdateIndexCubit(),
child: const SettingsTitlePanel(title: '软件更新'),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:laki_icu_app/blocs/bluetooth_read/bluetooth_read_bloc.dart';
import 'package:laki_icu_app/blocs/bluetooth_read/bluetooth_read_state.dart';
import '../cubit/settings_index_cubit.dart';
import '../cubit/settings_index_state.dart';
import 'settings_panel_frame.dart';
class SettingsAboutPanel extends StatelessWidget {
const SettingsAboutPanel({super.key});
@override
Widget build(BuildContext context) {
return SettingsPanelFrame(
backgroundAsset: settingsAssetPanelMain,
padding: EdgeInsets.fromLTRB(42.w, 54.h, 42.w, 48.h),
child: BlocBuilder<SettingsIndexCubit, SettingsIndexState>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const _LogoPlaceholder(),
SizedBox(height: 62.h),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(child: _ProductTitle()),
SizedBox(width: 42.w),
const _BarcodeCard(),
],
),
SizedBox(height: 36.h),
Divider(color: const Color(0xFF4CA7D2).withValues(alpha: 0.36)),
SizedBox(height: 18.h),
Expanded(
child: Row(
children: [
Expanded(child: _DeviceInfoColumn()),
Container(
width: 1.w,
margin: EdgeInsets.symmetric(horizontal: 36.w),
color: const Color(0xFF3A9BD2).withValues(alpha: 0.42),
),
Expanded(
child: _PreferenceColumn(
state: state,
onTemperatureUnitChanged: context
.read<SettingsIndexCubit>()
.selectTemperatureUnit,
onLockChanged:
context.read<SettingsIndexCubit>().setLockScreen,
),
),
],
),
),
],
);
},
),
);
}
}
class _LogoPlaceholder extends StatelessWidget {
const _LogoPlaceholder();
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 855.w,
height: 184.h,
color: const Color(0xFFD7D7D7),
alignment: Alignment.center,
child: Text(
'LOGO',
style: TextStyle(
color: Colors.black,
fontSize: 32.sp,
fontWeight: FontWeight.w800,
),
),
),
);
}
}
class _ProductTitle extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'TSAAS ICU 动物医疗监护舱',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 42.sp,
fontWeight: FontWeight.w800,
),
),
SizedBox(height: 18.h),
Text(
'730SE-CICU-A',
style: TextStyle(
color: Colors.white,
fontSize: 34.sp,
fontWeight: FontWeight.w800,
),
),
],
);
}
}
class _BarcodeCard extends StatelessWidget {
const _BarcodeCard();
@override
Widget build(BuildContext context) {
return Container(
width: 400.w,
height: 150.h,
padding: EdgeInsets.fromLTRB(28.w, 20.h, 28.w, 14.h),
decoration: BoxDecoration(
color: const Color(0xFFF4F4F4),
borderRadius: BorderRadius.circular(18.r),
),
child: Column(
children: [
Expanded(child: CustomPaint(painter: _BarcodePainter())),
SizedBox(height: 6.h),
Text(
'ICU-A03-2026-0601-001',
style: TextStyle(
color: const Color(0xFF1B1B1B),
fontSize: 14.sp,
fontWeight: FontWeight.w700,
),
),
],
),
);
}
}
class _DeviceInfoColumn extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<BluetoothReadBloc, BluetoothReadState>(
buildWhen: (previous, current) => previous.info.sn != current.info.sn,
builder: (context, state) {
final sn =
state.info.sn?.isNotEmpty == true ? state.info.sn! : 'CNA07212L';
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_InfoRow(label: '序列号:', value: sn),
const _InfoRow(label: '设备码:', value: 'TS8P2603CUA019CN'),
const _InfoRow(label: '设备激活时间:', value: '2026-05-11'),
const _InfoRow(label: '保修截止时间:', value: '2027-05-11'),
],
);
},
);
}
}
class _PreferenceColumn extends StatelessWidget {
const _PreferenceColumn({
required this.state,
required this.onTemperatureUnitChanged,
required this.onLockChanged,
});
final SettingsIndexState state;
final ValueChanged<bool> onTemperatureUnitChanged;
final ValueChanged<bool> onLockChanged;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_InfoRow(
label: '语言:',
valueWidget: _SelectButton(text: state.language),
),
_InfoRow(
label: '温度单位:',
valueWidget: Row(
mainAxisSize: MainAxisSize.min,
children: [
_UnitButton(
text: '℃',
active: state.useCelsius,
onTap: () => onTemperatureUnitChanged(true),
),
SizedBox(width: 22.w),
_UnitButton(
text: '℉',
active: !state.useCelsius,
onTap: () => onTemperatureUnitChanged(false),
),
],
),
),
_InfoRow(
label: '锁屏:',
valueWidget: _SettingsSwitch(
value: state.lockScreen,
onChanged: onLockChanged,
),
),
const _InfoRow(
label: '缓存:',
valueWidget: _ClearButton(),
),
],
);
}
}
class _InfoRow extends StatelessWidget {
const _InfoRow({
required this.label,
this.value,
this.valueWidget,
});
final String label;
final String? value;
final Widget? valueWidget;
@override
Widget build(BuildContext context) {
return Row(
children: [
SizedBox(
width: 210.w,
child: Text(
label,
style: TextStyle(
color: Colors.white,
fontSize: 27.sp,
fontWeight: FontWeight.w700,
),
),
),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: valueWidget ??
Text(
value ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 27.sp,
fontWeight: FontWeight.w700,
),
),
),
),
],
);
}
}
class _SelectButton extends StatelessWidget {
const _SelectButton({required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Container(
width: 350.w,
height: 52.h,
decoration: settingsButtonDecoration(),
padding: EdgeInsets.symmetric(horizontal: 28.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
text,
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
fontWeight: FontWeight.w700,
),
),
SizedBox(width: 28.w),
Icon(
Icons.keyboard_arrow_down,
color: const Color(0xFF7CCDF0).withValues(alpha: 0.74),
size: 30.w,
),
],
),
);
}
}
class _UnitButton extends StatelessWidget {
const _UnitButton({
required this.text,
required this.active,
required this.onTap,
});
final String text;
final bool active;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Container(
width: 110.w,
height: 52.h,
decoration: settingsButtonDecoration(active: active),
alignment: Alignment.center,
child: Text(
text,
style: TextStyle(
color: Colors.white,
fontSize: 27.sp,
fontWeight: FontWeight.w800,
),
),
),
);
}
}
class _SettingsSwitch extends StatelessWidget {
const _SettingsSwitch({
required this.value,
required this.onChanged,
});
final bool value;
final ValueChanged<bool> onChanged;
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => onChanged(!value),
child: AnimatedContainer(
duration: const Duration(milliseconds: 180),
width: 92.w,
height: 40.h,
padding: EdgeInsets.all(3.w),
decoration: BoxDecoration(
color: const Color(0xFF062444),
borderRadius: BorderRadius.circular(22.r),
border: Border.all(color: const Color(0xFF58BCE8), width: 1.w),
),
alignment: value ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
width: 34.w,
height: 34.w,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
colors: [Color(0xFFB9F4FF), Color(0xFF3189D9)],
),
boxShadow: [
BoxShadow(
color: const Color(0xFF5ED4FF).withValues(alpha: 0.45),
blurRadius: 10.r,
),
],
),
),
),
);
}
}
class _ClearButton extends StatelessWidget {
const _ClearButton();
@override
Widget build(BuildContext context) {
return Container(
width: 110.w,
height: 52.h,
decoration: settingsButtonDecoration(),
alignment: Alignment.center,
child: Text(
'清理',
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
fontWeight: FontWeight.w700,
),
),
);
}
}
class _BarcodePainter extends CustomPainter {
final List<int> widths = const [
2,
1,
4,
1,
1,
3,
2,
4,
1,
2,
3,
1,
4,
2,
1,
3,
2,
1,
4,
3,
1,
2,
4,
1,
3,
2,
1,
4,
2,
3,
1,
2,
];
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = const Color(0xFF06101A);
final total =
widths.fold<int>(0, (sum, item) => sum + item) + widths.length;
final unit = size.width / total;
var x = 0.0;
for (var index = 0; index < widths.length; index++) {
final width = widths[index] * unit;
if (index.isEven) {
canvas.drawRect(Rect.fromLTWH(x, 0, width, size.height), paint);
}
x += width + unit;
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
const String settingsAssetPanelLeft =
'lib/assets/monitoring/box_bg_460x905.png';
const String settingsAssetPanelMain =
'lib/assets/monitoring/box_bg_674x509.png';
const String settingsAssetButton =
'lib/assets/monitoring/frame_button_324x164.png';
class SettingsPanelFrame extends StatelessWidget {
const SettingsPanelFrame({
super.key,
required this.child,
required this.padding,
required this.backgroundAsset,
});
final Widget child;
final EdgeInsetsGeometry padding;
final String backgroundAsset;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
padding: padding,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(backgroundAsset),
fit: BoxFit.fill,
),
),
child: child,
);
}
}
BoxDecoration settingsButtonDecoration({bool active = false}) {
return BoxDecoration(
image: const DecorationImage(
image: AssetImage(settingsAssetButton),
fit: BoxFit.fill,
),
boxShadow: active
? [
BoxShadow(
color: const Color(0xFF45CFFF).withValues(alpha: 0.3),
blurRadius: 18.r,
spreadRadius: 1.r,
),
]
: null,
);
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'settings_panel_frame.dart';
class SettingsSideMenu extends StatelessWidget {
const SettingsSideMenu({
super.key,
required this.selectedMenu,
required this.onSelected,
});
final String selectedMenu;
final ValueChanged<String> onSelected;
static const List<String> menus = [
'关于本机',
'远程控制',
'软件更新',
'客户服务',
'出厂设置',
];
@override
Widget build(BuildContext context) {
return SettingsPanelFrame(
backgroundAsset: settingsAssetPanelLeft,
padding: EdgeInsets.fromLTRB(42.w, 48.h, 42.w, 48.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'软件设置',
style: TextStyle(
color: Colors.white,
fontSize: 30.sp,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 34.h),
Divider(color: const Color(0xFF4CA7D2).withValues(alpha: 0.42)),
SizedBox(height: 30.h),
for (final menu in menus) ...[
_SettingsMenuButton(
text: menu,
active: menu == selectedMenu,
onTap: () => onSelected(menu),
),
SizedBox(height: 22.h),
],
],
),
);
}
}
class _SettingsMenuButton extends StatelessWidget {
const _SettingsMenuButton({
required this.text,
required this.active,
required this.onTap,
});
final String text;
final bool active;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Container(
height: 64.h,
padding: EdgeInsets.symmetric(horizontal: 28.w),
decoration: settingsButtonDecoration(active: active),
child: Row(
children: [
Expanded(
child: Text(
text,
textAlign: TextAlign.center,
style: TextStyle(
color: active ? const Color(0xFFC9F8FF) : Colors.white,
fontSize: 30.sp,
fontWeight: FontWeight.w700,
),
),
),
Text(
'》》',
style: TextStyle(
color: const Color(0xFF67C9F5)
.withValues(alpha: active ? 0.72 : 0.36),
fontSize: 28.sp,
fontWeight: FontWeight.w700,
),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'settings_panel_frame.dart';
class SettingsTitlePanel extends StatelessWidget {
const SettingsTitlePanel({
super.key,
required this.title,
});
final String title;
@override
Widget build(BuildContext context) {
return SettingsPanelFrame(
backgroundAsset: settingsAssetPanelMain,
padding: EdgeInsets.all(42.w),
child: Align(
alignment: Alignment.topLeft,
child: Text(
title,
style: TextStyle(
color: Colors.white,
fontSize: 34.sp,
fontWeight: FontWeight.w800,
),
),
),
);
}
}
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