flutter_for_openharmony家庭药箱管理app实战+设置功能实现
本文介绍了Flutter在OpenHarmony应用中实现设置功能的方法。设置页面包含提醒设置、过期预警和数据管理三大模块,采用分组列表布局。开关选项使用Switch组件,选择类选项通过弹出对话框实现。文章详细展示了页面结构、分组标题设计、开关与选择项的组件实现,以及提前提醒时间、过期预警天数的选择对话框。对于危险操作如清除数据,使用红色文字标识并添加二次确认机制。通过状态管理维护各项设置值,确保
设置功能是应用的配置中心,用户可以在这里调整提醒设置、过期预警、数据管理等选项。本文将介绍如何在Flutter for OpenHarmony应用中实现设置功能,包括开关控制、选项选择、数据管理等特性。
功能设计思路
设置页面包含三个主要模块:提醒设置、过期提醒、数据管理。提醒设置包括开启提醒通知、提醒声音、震动提醒、提前提醒时间等选项。过期提醒可以设置过期预警天数。数据管理包括导出数据、导入数据、清除所有数据等功能。
页面采用列表布局,使用分组标题将不同模块分隔开。开关类选项使用Switch组件,选择类选项点击后弹出对话框。数据管理中的清除数据功能使用红色文字标识,提醒用户谨慎操作。
页面结构实现
页面使用有状态组件,维护各个设置项的状态:
class _SettingsScreenState extends State<SettingsScreen> {
bool _notificationEnabled = true;
bool _soundEnabled = true;
bool _vibrationEnabled = true;
int _reminderAdvanceMinutes = 5;
int _expiryWarningDays = 30;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('设置')),
body: ListView(
children: [
_buildSectionHeader('提醒设置'),
_buildSwitchTile(
'开启提醒通知',
'接收用药提醒推送通知',
_notificationEnabled,
(v) => setState(() => _notificationEnabled = v),
),
_buildSwitchTile(
'提醒声音',
'提醒时播放声音',
_soundEnabled,
(v) => setState(() => _soundEnabled = v),
),
_buildSwitchTile(
'震动提醒',
'提醒时震动',
_vibrationEnabled,
(v) => setState(() => _vibrationEnabled = v),
),
_buildListTile(
'提前提醒时间',
'$_reminderAdvanceMinutes 分钟',
() => _showAdvanceTimeDialog(),
),
_buildSectionHeader('过期提醒'),
_buildListTile(
'过期预警天数',
'$_expiryWarningDays 天',
() => _showExpiryWarningDialog(),
),
_buildSectionHeader('数据管理'),
_buildListTile('导出数据', '导出药品和健康数据', () {}),
_buildListTile('导入数据', '从备份文件导入', () {}),
_buildListTile(
'清除所有数据',
'删除所有本地数据',
() => _showClearDataDialog(),
isDestructive: true,
),
],
),
);
}
}
页面使用ListView构建设置列表,包含多个分组和设置项。状态变量用于存储各个设置的值,通过setState更新UI。设置项分为开关类和选择类两种,分别使用不同的组件实现。
分组标题
分组标题用于分隔不同的设置模块:
Widget _buildSectionHeader(String title) {
return Container(
padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 8.h),
child: Text(
title,
style: TextStyle(fontSize: 14.sp, color: Colors.grey[600], fontWeight: FontWeight.w500),
),
);
}
分组标题使用灰色小号字体,与设置项区分开。上下有适当的间距,让分组更加清晰。这种设计让设置页面的结构一目了然。
开关类设置项
开关类设置项使用Switch组件实现:
Widget _buildSwitchTile(String title, String subtitle, bool value, ValueChanged<bool> onChanged) {
return ListTile(
title: Text(title),
subtitle: Text(subtitle, style: TextStyle(fontSize: 12.sp, color: Colors.grey[500])),
trailing: Switch(
value: value,
onChanged: onChanged,
activeColor: const Color(0xFF00897B),
),
);
}
开关设置项使用ListTile布局,标题和副标题在左侧,Switch开关在右侧。开关的激活颜色使用应用主题色,保持视觉统一。当用户切换开关时,通过回调函数更新状态。
选择类设置项
选择类设置项点击后弹出对话框:
Widget _buildListTile(String title, String subtitle, VoidCallback onTap, {bool isDestructive = false}) {
return ListTile(
title: Text(title, style: TextStyle(color: isDestructive ? Colors.red : null)),
subtitle: Text(subtitle, style: TextStyle(fontSize: 12.sp, color: Colors.grey[500])),
trailing: const Icon(Icons.chevron_right, color: Colors.grey),
onTap: onTap,
);
}
选择设置项也使用ListTile布局,右侧是右箭头提示可点击。isDestructive参数用于标识危险操作,如清除数据,这类操作的标题使用红色字体。点击设置项会触发onTap回调,通常是弹出对话框。
提前提醒时间选择
提前提醒时间通过单选对话框选择:
void _showAdvanceTimeDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('提前提醒时间'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [5, 10, 15, 30].map((minutes) {
return RadioListTile<int>(
title: Text('$minutes 分钟'),
value: minutes,
groupValue: _reminderAdvanceMinutes,
onChanged: (v) {
setState(() => _reminderAdvanceMinutes = v!);
Navigator.pop(context);
},
);
}).toList(),
),
),
);
}
对话框包含四个选项:5分钟、10分钟、15分钟、30分钟。使用RadioListTile实现单选效果,当前选中的值通过groupValue指定。用户选择后立即更新状态并关闭对话框,体验流畅。
过期预警天数选择
过期预警天数的选择方式与提前提醒时间类似:
void _showExpiryWarningDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('过期预警天数'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [7, 14, 30, 60, 90].map((days) {
return RadioListTile<int>(
title: Text('$days 天'),
value: days,
groupValue: _expiryWarningDays,
onChanged: (v) {
setState(() => _expiryWarningDays = v!);
Navigator.pop(context);
},
);
}).toList(),
),
),
);
}
对话框包含五个选项:7天、14天、30天、60天、90天。用户可以根据自己的需求选择合适的预警天数。选择后立即生效,系统会根据新的设置进行过期提醒。
清除数据确认
清除数据是危险操作,需要二次确认:
void _showClearDataDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('清除所有数据'),
content: const Text('此操作将删除所有药品、家庭成员、提醒和健康记录数据,且无法恢复。确定要继续吗?'),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
TextButton(
onPressed: () {
Navigator.pop(context);
// 清除数据逻辑
},
child: const Text('确定', style: TextStyle(color: Colors.red)),
),
],
),
);
}
确认对话框详细说明了操作的后果,提醒用户数据无法恢复。确定按钮使用红色字体,再次强调操作的危险性。这种设计可以有效防止用户误操作。
技术要点
状态管理:页面使用多个状态变量存储设置值,通过setState更新UI。这种简单的状态管理方式适合设置页面这种相对独立的功能。
组件封装:将分组标题、开关设置项、选择设置项封装成独立的方法,代码结构清晰,易于维护和扩展。
用户体验:开关类设置可以直接切换,选择类设置通过对话框选择,交互方式符合用户习惯。危险操作使用红色标识并二次确认,防止误操作。
对话框设计:选择对话框使用RadioListTile实现单选效果,选择后立即生效并关闭对话框,体验流畅。确认对话框详细说明操作后果,让用户做出明智的决定。
设置持久化存储
为了让设置在应用重启后依然生效,需要将设置值持久化存储到本地:
import 'package:shared_preferences/shared_preferences.dart';
Future<void> _loadSettings() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_notificationEnabled = prefs.getBool('notification_enabled') ?? true;
_soundEnabled = prefs.getBool('sound_enabled') ?? true;
_vibrationEnabled = prefs.getBool('vibration_enabled') ?? true;
_reminderAdvanceMinutes = prefs.getInt('reminder_advance_minutes') ?? 5;
_expiryWarningDays = prefs.getInt('expiry_warning_days') ?? 30;
});
}
在initState中调用_loadSettings方法加载保存的设置值。使用SharedPreferences库实现键值对存储,每个设置项对应一个键名。如果没有保存过该设置,则使用默认值。这种设计确保了用户的设置偏好能够被记住。
保存设置变更
当用户修改设置时,需要同步保存到本地存储:
Future<void> _saveNotificationEnabled(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('notification_enabled', value);
setState(() => _notificationEnabled = value);
}
Future<void> _saveReminderAdvanceMinutes(int minutes) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('reminder_advance_minutes', minutes);
setState(() => _reminderAdvanceMinutes = minutes);
}
每个设置项都有对应的保存方法,先写入本地存储,再更新UI状态。这种方式确保了数据的一致性,即使应用意外退出,设置也不会丢失。开关类设置在切换时立即保存,选择类设置在对话框确认后保存。
导出数据功能实现
导出数据功能将应用数据导出为JSON文件,方便用户备份:
Future<void> _exportData() async {
final medicines = context.read<MedicineProvider>().medicines;
final members = context.read<FamilyProvider>().members;
final reminders = context.read<ReminderProvider>().reminders;
final exportData = {
'version': '1.0',
'exportTime': DateTime.now().toIso8601String(),
'medicines': medicines.map((m) => m.toJson()).toList(),
'members': members.map((m) => m.toJson()).toList(),
'reminders': reminders.map((r) => r.toJson()).toList(),
};
final jsonString = jsonEncode(exportData);
// 保存到文件或分享
Get.snackbar('成功', '数据已导出', snackPosition: SnackPosition.BOTTOM);
}
导出功能从各个Provider获取数据,转换为JSON格式。导出文件包含版本号和导出时间,方便后续导入时进行版本兼容处理。用户可以将导出的文件保存到本地或通过分享功能发送到其他设备。
导入数据功能实现
导入数据功能从备份文件恢复数据:
Future<void> _importData(String jsonString) async {
try {
final data = jsonDecode(jsonString) as Map<String, dynamic>;
final version = data['version'] as String;
if (version == '1.0') {
final medicines = (data['medicines'] as List)
.map((m) => Medicine.fromJson(m))
.toList();
final members = (data['members'] as List)
.map((m) => FamilyMember.fromJson(m))
.toList();
context.read<MedicineProvider>().importMedicines(medicines);
context.read<FamilyProvider>().importMembers(members);
Get.snackbar('成功', '数据已导入', snackPosition: SnackPosition.BOTTOM);
}
} catch (e) {
Get.snackbar('错误', '导入失败:文件格式不正确', snackPosition: SnackPosition.BOTTOM);
}
}
导入功能解析JSON文件,根据版本号进行相应的数据转换。使用try-catch捕获解析错误,向用户显示友好的错误提示。导入成功后,各个Provider会更新数据并通知UI刷新。
通知权限检查
开启提醒通知前需要检查系统通知权限:
Future<void> _checkNotificationPermission() async {
// 检查通知权限状态
final status = await Permission.notification.status;
if (status.isDenied) {
final result = await Permission.notification.request();
if (result.isGranted) {
_saveNotificationEnabled(true);
} else {
Get.snackbar(
'权限提示',
'请在系统设置中开启通知权限',
snackPosition: SnackPosition.BOTTOM,
);
}
} else if (status.isGranted) {
_saveNotificationEnabled(true);
}
}
当用户开启提醒通知时,首先检查系统通知权限。如果权限被拒绝,则请求权限。如果用户拒绝授权,显示提示引导用户到系统设置中手动开启。这种设计确保了通知功能能够正常工作。
设置项动画效果
为设置项添加切换动画,提升用户体验:
Widget _buildAnimatedSwitchTile(
String title,
String subtitle,
bool value,
ValueChanged<bool> onChanged,
) {
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
decoration: BoxDecoration(
color: value ? const Color(0xFF00897B).withOpacity(0.05) : Colors.transparent,
),
child: ListTile(
title: Text(title),
subtitle: Text(
subtitle,
style: TextStyle(fontSize: 12.sp, color: Colors.grey[500]),
),
trailing: Switch(
value: value,
onChanged: onChanged,
activeColor: const Color(0xFF00897B),
),
),
);
}
使用AnimatedContainer为设置项添加背景色动画。当开关打开时,设置项背景会渐变为浅青色,提供视觉反馈。动画时长设置为200毫秒,既能让用户感知到变化,又不会显得拖沓。
版本信息展示
在设置页面底部展示应用版本信息:
Widget _buildVersionInfo() {
return Padding(
padding: EdgeInsets.symmetric(vertical: 24.h),
child: Column(
children: [
Text(
'家庭药箱管理',
style: TextStyle(fontSize: 14.sp, color: Colors.grey[600]),
),
SizedBox(height: 4.h),
Text(
'v1.0.0',
style: TextStyle(fontSize: 12.sp, color: Colors.grey[400]),
),
],
),
);
}
版本信息使用灰色小号字体,不抢夺视觉焦点。用户可以在这里查看当前应用版本,方便反馈问题时提供版本信息。这是设置页面的常见设计模式。
总结
设置功能通过清晰的分组和友好的交互设计,为用户提供了灵活的配置选项。从提醒设置到数据管理,涵盖了应用的各个方面。开关类设置可以快速切换,选择类设置通过对话框选择,危险操作有二次确认保护。这种设计让用户可以根据自己的需求定制应用,提升使用体验。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)