import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/navigation_provider.dart'; // ── 通知偏好状态 ── final notificationPrefsProvider = NotifierProvider>( NotificationPrefsNotifier.new, ); class NotificationPrefsNotifier extends Notifier> { @override Map build() { // TODO: 从 SQLite 读取持久化值,此处先用默认值 return { 'medication': true, 'healthAlert': true, 'followUp': true, 'aiReply': false, 'dndEnabled': false, 'pushEnabled': true, 'dndStart': false, // 占位:实际用 TimeOfDay 'dndEnd': false, }; } void toggle(String key) { state = {...state, key: !state[key]!}; // TODO: 持久化到 SQLite } void setDndStart(TimeOfDay time) { state = {...state, 'dndStart': true}; // 简化存储 } void setDndEnd(TimeOfDay time) { state = {...state, 'dndEnd': true}; } } // ── 页面 ── class NotificationPrefsPage extends ConsumerWidget { const NotificationPrefsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final prefs = ref.watch(notificationPrefsProvider); final dndOn = prefs['dndEnabled'] ?? false; return Scaffold( backgroundColor: const Color(0xFFF6F9FB), appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new, color: Color(0xFF1A1A1A)), onPressed: () => popRoute(ref), ), title: const Text('消息通知', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))), centerTitle: true, ), body: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ── 推送总开关 ── _SectionTitle(title: '推送通知'), _SwitchTile( title: '允许推送通知', subtitle: '关闭后将不再收到任何系统推送', value: prefs['pushEnabled'] ?? true, onChanged: (v) => ref.read(notificationPrefsProvider.notifier).toggle('pushEnabled'), ), const SizedBox(height: 24), // ── 各类通知开关 ── _SectionTitle(title: '通知类型'), _SwitchTile( icon: Icons.medication_rounded, iconBg: const Color(0xFFFFF3E0), iconColor: const Color(0xFFFF9800), title: '用药提醒', subtitle: '服药时间到达时提醒您', value: prefs['medication'] ?? true, onChanged: (v) => ref.read(notificationPrefsProvider.notifier).toggle('medication'), ), _SwitchTile( icon: Icons.warning_amber_rounded, iconBg: const Color(0xFFFFEBEE), iconColor: const Color(0xFFE53935), title: '健康异常提醒', subtitle: '检测到数据异常时及时通知', value: prefs['healthAlert'] ?? true, onChanged: (v) => ref.read(notificationPrefsProvider.notifier).toggle('healthAlert'), ), _SwitchTile( icon: Icons.event_available_rounded, iconBg: const Color(0xFFE8F5E9), iconColor: const Color(0xFF4CAF50), title: '复查日期提醒', subtitle: '复查日前一天提醒您预约', value: prefs['followUp'] ?? true, onChanged: (v) => ref.read(notificationPrefsProvider.notifier).toggle('followUp'), ), _SwitchTile( icon: Icons.smart_toy_outlined, iconBg: const Color(0xFFF3E5F5), iconColor: const Color(0xFF14B8A6), title: 'AI 回复通知', subtitle: 'AI 助手回复时发送通知', value: prefs['aiReply'] ?? false, onChanged: (v) => ref.read(notificationPrefsProvider.notifier).toggle('aiReply'), ), const SizedBox(height: 24), // ── 免打扰时段 ── _SectionTitle(title: '免打扰时段'), _SwitchTile( title: '开启免打扰模式', subtitle: dndOn ? '22:00 - 08:00 期间静音' : '关闭后全天接收通知', value: dndOn, onChanged: (v) => ref.read(notificationPrefsProvider.notifier).toggle('dndEnabled'), ), if (dndOn) ...[ Container( margin: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)), child: Row(children: [ Expanded(child: _TimeButton(label: '开始', time: '22:00', onTap: () async { final picked = await showTimePicker(context: context, initialTime: const TimeOfDay(hour: 22, minute: 0)); if (picked != null && context.mounted) ref.read(notificationPrefsProvider.notifier).setDndStart(picked); })), Padding(padding: const EdgeInsets.symmetric(horizontal: 12), child: Text('~', style: TextStyle(fontSize: 16, color: Colors.grey[400]))), Expanded(child: _TimeButton(label: '结束', time: '08:00', onTap: () async { final picked = await showTimePicker(context: context, initialTime: const TimeOfDay(hour: 8, minute: 0)); if (picked != null && context.mounted) ref.read(notificationPrefsProvider.notifier).setDndEnd(picked); })), ]), ), const SizedBox(height: 8), ], const SizedBox(height: 40), ], ), ), ); } } // ── 子组件 ── class _SectionTitle extends StatelessWidget { final String title; const _SectionTitle({required this.title}); @override Widget build(BuildContext context) { return Padding(padding: const EdgeInsets.only(left: 4, bottom: 10), child: Text(title, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF999999)))); } } class _SwitchTile extends StatelessWidget { final IconData? icon; final Color? iconBg; final Color? iconColor; final String title; final String? subtitle; final bool value; final ValueChanged onChanged; const _SwitchTile({ this.icon, this.iconBg, this.iconColor, required this.title, this.subtitle, required this.value, required this.onChanged, }); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 3), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)), child: Row(children: [ if (icon != null) ...[ Container(width: 38, height: 38, decoration: BoxDecoration(color: iconBg, borderRadius: BorderRadius.circular(10)), child: Icon(icon, size: 20, color: iconColor)), const SizedBox(width: 12), ], Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle(fontSize: 15, color: Color(0xFF1A1A1A), fontWeight: FontWeight.w500)), if (subtitle != null && subtitle!.isNotEmpty) Text(subtitle!, style: TextStyle(fontSize: 12, color: Colors.grey[500])), ])), Switch(value: value, onChanged: onChanged, activeThumbColor: const Color(0xFF14B8A6), activeTrackColor: const Color(0xFFC5BFFF)), ]), ); } } class _TimeButton extends StatelessWidget { final String label; final String time; final VoidCallback onTap; const _TimeButton({required this.label, required this.time, required this.onTap}); @override Widget build(BuildContext context) { return GestureDetector(onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration(border: Border.all(color: const Color(0xFFE0E0E0)), borderRadius: BorderRadius.circular(10)), child: Column(children: [Text(label, style: TextStyle(fontSize: 11, color: Colors.grey[500])), Text(time, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF14B8A6)))]), )); } }