
通知设置让用户可以自定义接收哪些类型的通知,以及何时接收。合理的通知设置能在不打扰用户的前提下,及时提醒重要事项。
设计思路
通知设置页面包括总开关、各类提醒开关、提醒时间和免打扰时段。用户可以根据自己的需求灵活配置。
创建页面结构
先搭建基本框架:
dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
import '../providers/settings_provider.dart';
class NotificationSettingsScreen extends StatefulWidget {
const NotificationSettingsScreen({super.key});
@override
State<NotificationSettingsScreen> createState() =>
_NotificationSettingsScreenState();
}
class _NotificationSettingsScreenState
extends State<NotificationSettingsScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('通知设置'),
elevation: 0,
),
body: Consumer<SettingsProvider>(
builder: (context, settings, _) {
return ListView(
children: [
_buildMainSwitch(settings),
if (settings.notificationsEnabled) ...[
_buildReminderTypes(settings),
_buildReminderTime(settings),
_buildDoNotDisturb(settings),
],
],
);
},
),
);
}
}
用Consumer监听设置变化,当通知总开关关闭时,其他选项会隐藏起来。这样的设计让界面更简洁。
通知总开关
最上面是通知的总开关:
dart
Widget _buildMainSwitch(SettingsProvider settings) {
return Container(
color: Colors.white,
child: SwitchListTile(
secondary: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: const Color(0xFFE91E63).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: const Icon(
Icons.notifications_active,
color: Color(0xFFE91E63),
),
),
title: Text(
'启用通知',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
),
),
subtitle: Text(
settings.notificationsEnabled ? '已开启' : '已关闭',
style: TextStyle(
fontSize: 13.sp,
color: Colors.grey[600],
),
),
value: settings.notificationsEnabled,
onChanged: (value) {
settings.setNotificationsEnabled(value);
if (!value) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('已关闭所有通知'),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
);
}
},
activeColor: const Color(0xFFE91E63),
),
);
}
总开关关闭时会显示一个提示,让用户知道操作生效了。图标用圆角容器包装,和设置页面保持一致的风格。
提醒类型设置
各种类型的提醒开关:
dart
Widget _buildReminderTypes(SettingsProvider settings) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(16.w, 20.h, 16.w, 8.h),
child: Text(
'提醒类型',
style: TextStyle(
fontSize: 13.sp,
color: Colors.grey[600],
fontWeight: FontWeight.w600,
),
),
),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(color: Colors.grey[200]!, width: 0.5),
bottom: BorderSide(color: Colors.grey[200]!, width: 0.5),
),
),
child: Column(
children: [
_buildReminderItem(
settings: settings,
icon: Icons.cake_outlined,
iconColor: const Color(0xFFFF9800),
title: '生日提醒',
subtitle: '家人生日前提醒',
value: settings.birthdayReminder,
onChanged: (value) => settings.setBirthdayReminder(value),
),
_buildDivider(),
_buildReminderItem(
settings: settings,
icon: Icons.favorite_outline,
iconColor: const Color(0xFFE91E63),
title: '纪念日提醒',
subtitle: '重要纪念日前提醒',
value: settings.anniversaryReminder,
onChanged: (value) => settings.setAnniversaryReminder(value),
),
_buildDivider(),
_buildReminderItem(
settings: settings,
icon: Icons.event_outlined,
iconColor: const Color(0xFF2196F3),
title: '活动提醒',
subtitle: '家庭活动前提醒',
value: settings.eventReminder,
onChanged: (value) => settings.setEventReminder(value),
),
_buildDivider(),
_buildReminderItem(
settings: settings,
icon: Icons.check_circle_outline,
iconColor: const Color(0xFF4CAF50),
title: '待办提醒',
subtitle: '待办事项到期提醒',
value: settings.todoReminder,
onChanged: (value) => settings.setTodoReminder(value),
),
],
),
),
],
);
}
Widget _buildReminderItem({
required SettingsProvider settings,
required IconData icon,
required Color iconColor,
required String title,
required String subtitle,
required bool value,
required Function(bool) onChanged,
}) {
return SwitchListTile(
secondary: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: iconColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(icon, color: iconColor, size: 20.sp),
),
title: Text(title),
subtitle: Text(
subtitle,
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey[600],
),
),
value: value,
onChanged: onChanged,
activeColor: const Color(0xFFE91E63),
);
}
Widget _buildDivider() {
return Divider(
height: 1,
indent: 72.w,
color: Colors.grey[200],
);
}
每种提醒类型都有独特的图标和颜色,让用户一眼就能区分。分隔线从图标后面开始,这样看起来更整齐。
提醒时间设置
让用户选择提前多久提醒:
dart
Widget _buildReminderTime(SettingsProvider settings) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(16.w, 20.h, 16.w, 8.h),
child: Text(
'提醒时间',
style: TextStyle(
fontSize: 13.sp,
color: Colors.grey[600],
fontWeight: FontWeight.w600,
),
),
),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(color: Colors.grey[200]!, width: 0.5),
bottom: BorderSide(color: Colors.grey[200]!, width: 0.5),
),
),
child: ListTile(
leading: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: const Color(0xFF9C27B0).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: const Icon(
Icons.access_time,
color: Color(0xFF9C27B0),
),
),
title: const Text('提前提醒时间'),
subtitle: Text(
_getReminderTimeText(settings.reminderTime),
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey[600],
),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_getReminderTimeText(settings.reminderTime),
style: TextStyle(
fontSize: 14.sp,
color: const Color(0xFFE91E63),
fontWeight: FontWeight.w500,
),
),
SizedBox(width: 4.w),
const Icon(Icons.chevron_right, color: Colors.grey),
],
),
onTap: () => _showReminderTimeDialog(settings),
),
),
],
);
}
String _getReminderTimeText(int minutes) {
if (minutes == 0) return '当天';
if (minutes == 1440) return '1天前';
if (minutes == 4320) return '3天前';
if (minutes == 10080) return '1周前';
return '$minutes分钟前';
}
提醒时间用一个列表项展示,点击后弹出选择对话框。右边显示当前选择的值,用主题色高亮。
提醒时间选择对话框
让用户选择具体的提醒时间:
dart
void _showReminderTimeDialog(SettingsProvider settings) {
final options = [
{'label': '当天', 'value': 0},
{'label': '1天前', 'value': 1440},
{'label': '3天前', 'value': 4320},
{'label': '1周前', 'value': 10080},
];
showDialog(
context: context,
builder: (dialogContext) => AlertDialog(
title: const Text('选择提醒时间'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: options.map((option) {
final isSelected = settings.reminderTime == option['value'];
return InkWell(
onTap: () {
settings.setReminderTime(option['value'] as int);
Navigator.pop(dialogContext);
},
borderRadius: BorderRadius.circular(8.r),
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 16.w,
vertical: 12.h,
),
margin: EdgeInsets.only(bottom: 8.h),
decoration: BoxDecoration(
color: isSelected
? const Color(0xFFE91E63).withOpacity(0.1)
: Colors.transparent,
borderRadius: BorderRadius.circular(8.r),
border: Border.all(
color: isSelected
? const Color(0xFFE91E63)
: Colors.grey[300]!,
width: isSelected ? 2 : 1,
),
),
child: Row(
children: [
Text(
option['label'] as String,
style: TextStyle(
fontSize: 15.sp,
color: isSelected
? const Color(0xFFE91E63)
: Colors.black87,
fontWeight:
isSelected ? FontWeight.w600 : FontWeight.normal,
),
),
const Spacer(),
if (isSelected)
const Icon(
Icons.check_circle,
color: Color(0xFFE91E63),
),
],
),
),
);
}).toList(),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
),
);
}
选项用卡片式设计,选中的选项有边框和背景色。用户点击后立即应用并关闭对话框。
免打扰设置
设置免打扰时段:
dart
Widget _buildDoNotDisturb(SettingsProvider settings) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(16.w, 20.h, 16.w, 8.h),
child: Text(
'免打扰',
style: TextStyle(
fontSize: 13.sp,
color: Colors.grey[600],
fontWeight: FontWeight.w600,
),
),
),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(color: Colors.grey[200]!, width: 0.5),
bottom: BorderSide(color: Colors.grey[200]!, width: 0.5),
),
),
child: Column(
children: [
SwitchListTile(
secondary: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: const Color(0xFF607D8B).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: const Icon(
Icons.do_not_disturb_on_outlined,
color: Color(0xFF607D8B),
),
),
title: const Text('启用免打扰'),
subtitle: Text(
'在指定时段内不接收通知',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey[600],
),
),
value: settings.doNotDisturbEnabled,
onChanged: (value) => settings.setDoNotDisturbEnabled(value),
activeColor: const Color(0xFFE91E63),
),
if (settings.doNotDisturbEnabled) ...[
_buildDivider(),
ListTile(
leading: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: const Color(0xFF00BCD4).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: const Icon(
Icons.bedtime_outlined,
color: Color(0xFF00BCD4),
),
),
title: const Text('免打扰时段'),
subtitle: Text(
'${settings.dndStartTime} - ${settings.dndEndTime}',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey[600],
),
),
trailing: const Icon(Icons.chevron_right, color: Colors.grey),
onTap: () => _showDndTimeDialog(settings),
),
],
],
),
),
],
);
}
免打扰开关打开后,才显示时段设置。这样的条件显示让界面更简洁。
免打扰时段对话框
让用户设置开始和结束时间:
dart
void _showDndTimeDialog(SettingsProvider settings) {
showDialog(
context: context,
builder: (dialogContext) => AlertDialog(
title: const Text('免打扰时段'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.wb_sunny_outlined, color: Color(0xFFFF9800)),
title: const Text('开始时间'),
trailing: Text(
settings.dndStartTime,
style: TextStyle(
fontSize: 16.sp,
color: const Color(0xFFE91E63),
fontWeight: FontWeight.w600,
),
),
onTap: () async {
final time = await showTimePicker(
context: dialogContext,
initialTime: TimeOfDay(
hour: int.parse(settings.dndStartTime.split(':')[0]),
minute: int.parse(settings.dndStartTime.split(':')[1]),
),
);
if (time != null) {
settings.setDndStartTime(
'${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}',
);
}
},
),
ListTile(
leading: const Icon(Icons.nightlight_outlined, color: Color(0xFF3F51B5)),
title: const Text('结束时间'),
trailing: Text(
settings.dndEndTime,
style: TextStyle(
fontSize: 16.sp,
color: const Color(0xFFE91E63),
fontWeight: FontWeight.w600,
),
),
onTap: () async {
final time = await showTimePicker(
context: dialogContext,
initialTime: TimeOfDay(
hour: int.parse(settings.dndEndTime.split(':')[0]),
minute: int.parse(settings.dndEndTime.split(':')[1]),
),
);
if (time != null) {
settings.setDndEndTime(
'${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}',
);
}
},
),
],
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext),
child: const Text(
'完成',
style: TextStyle(
color: Color(0xFFE91E63),
fontWeight: FontWeight.w600,
),
),
),
],
),
);
}
点击时间会弹出系统的时间选择器,用户可以方便地选择具体时间。开始时间用太阳图标,结束时间用月亮图标,很直观。
通知声音设置
让用户选择通知提示音:
dart
Widget _buildSoundSettings(SettingsProvider settings) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.fromLTRB(16.w, 20.h, 16.w, 8.h),
child: Text(
'声音与振动',
style: TextStyle(
fontSize: 13.sp,
color: Colors.grey[600],
fontWeight: FontWeight.w600,
),
),
),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(color: Colors.grey[200]!, width: 0.5),
bottom: BorderSide(color: Colors.grey[200]!, width: 0.5),
),
),
child: Column(
children: [
SwitchListTile(
secondary: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: const Color(0xFFFF9800).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: const Icon(
Icons.volume_up,
color: Color(0xFFFF9800),
),
),
title: const Text('提示音'),
subtitle: const Text('播放通知提示音'),
value: settings.notificationSound,
onChanged: (value) => settings.setNotificationSound(value),
activeColor: const Color(0xFFE91E63),
),
_buildDivider(),
SwitchListTile(
secondary: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: const Color(0xFF795548).withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: const Icon(
Icons.vibration,
color: Color(0xFF795548),
),
),
title: const Text('振动'),
subtitle: const Text('收到通知时振动'),
value: settings.notificationVibrate,
onChanged: (value) => settings.setNotificationVibrate(value),
activeColor: const Color(0xFFE91E63),
),
],
),
),
],
);
}
声音和振动设置让用户可以选择通知的提示方式。有些用户喜欢静音,有些喜欢振动,这样的设计满足不同需求。
通知预览
在设置页面添加测试通知功能:
dart
Widget _buildTestNotification() {
return Container(
margin: EdgeInsets.all(16.w),
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: const Color(0xFFE91E63).withOpacity(0.1),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: const Color(0xFFE91E63).withOpacity(0.3),
width: 1,
),
),
child: Row(
children: [
Icon(
Icons.info_outline,
color: const Color(0xFFE91E63),
size: 24.sp,
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'测试通知',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFFE91E63),
),
),
SizedBox(height: 4.h),
Text(
'点击发送测试通知,查看效果',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey[700],
),
),
],
),
),
ElevatedButton(
onPressed: () => _sendTestNotification(),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE91E63),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.r),
),
padding: EdgeInsets.symmetric(
horizontal: 16.w,
vertical: 8.h,
),
),
child: const Text('测试'),
),
],
),
);
}
void _sendTestNotification() {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const Icon(Icons.notifications_active, color: Colors.white),
SizedBox(width: 12.w),
const Expanded(
child: Text('这是一条测试通知'),
),
],
),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
backgroundColor: const Color(0xFFE91E63),
duration: const Duration(seconds: 3),
),
);
}
测试通知功能让用户可以预览通知效果,确保设置符合预期。这个小功能很实用,用户可以立即看到修改后的效果。
通知权限检查
检查系统通知权限状态:
dart
Widget _buildPermissionStatus() {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(8.r),
),
child: Row(
children: [
Icon(
Icons.check_circle,
color: Colors.blue[700],
size: 20.sp,
),
SizedBox(width: 8.w),
Expanded(
child: Text(
'已授予通知权限',
style: TextStyle(
fontSize: 13.sp,
color: Colors.blue[700],
),
),
),
TextButton(
onPressed: () => _openSystemSettings(),
child: Text(
'系统设置',
style: TextStyle(
fontSize: 12.sp,
color: Colors.blue[700],
),
),
),
],
),
);
}
void _openSystemSettings() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('打开系统设置'),
content: const Text('将跳转到系统设置页面,您可以在那里管理应用的通知权限。'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
// 这里调用打开系统设置的方法
},
child: const Text(
'前往',
style: TextStyle(
color: Color(0xFFE91E63),
fontWeight: FontWeight.w600,
),
),
),
],
),
);
}
权限状态提示让用户知道当前的权限情况,如果权限被关闭,可以引导用户去系统设置中开启。
总结
通知设置页面通过分组和条件显示,让用户可以精细控制通知行为。总开关控制是否接收通知,各类提醒开关控制具体类型,提醒时间和免打扰时段提供更多自定义选项。声音振动设置和测试通知功能让用户体验更好。整体设计清晰易用,满足不同用户的需求。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net