first commit

This commit is contained in:
ytc1012
2025-11-22 18:17:35 +08:00
commit d427916c6a
169 changed files with 15241 additions and 0 deletions

View File

@@ -0,0 +1,530 @@
# 📱 本地通知功能 - 实现文档
**实现日期**: 2025-11-22
**状态**: ✅ 完成
**平台支持**: Android, iOS (Web不支持)
---
## 📋 功能概述
FocusBuddy 现在支持本地通知,在专注计时完成时自动提醒用户,即使应用在后台运行。
### 核心特性
- ✅ 计时完成时自动发送通知
- ✅ 显示专注时长和分心次数
- ✅ 支持震动和声音
- ✅ 自动请求权限
- ✅ Web 平台优雅降级(不报错)
---
## 🏗️ 架构设计
### 文件结构
```
lib/services/notification_service.dart # 通知服务(新增)
lib/main.dart # 初始化通知服务(已修改)
lib/screens/focus_screen.dart # 计时完成时调用通知(已修改)
android/app/src/main/AndroidManifest.xml # Android权限已修改
```
### 服务设计
- **单例模式**: 全局只有一个 NotificationService 实例
- **延迟初始化**: 首次调用时才初始化
- **平台检测**: 自动识别 Web 平台并跳过
---
## 📝 代码实现
### 1. NotificationService 类
**位置**: `lib/services/notification_service.dart`
**关键方法**:
#### `initialize()` - 初始化服务
```dart
Future<void> initialize() async {
if (_initialized) return;
// Web 平台跳过
if (kIsWeb) return;
// Android/iOS 配置
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings(...);
await _notifications.initialize(initSettings);
}
```
#### `requestPermissions()` - 请求权限iOS
```dart
Future<bool> requestPermissions() async {
// iOS 需要显式请求Android 自动授予
final result = await _notifications
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(...);
return result ?? true;
}
```
#### `showFocusCompletedNotification()` - 显示完成通知
```dart
Future<void> showFocusCompletedNotification({
required int minutes,
required int distractionCount,
}) async {
final title = '🎉 Focus session complete!';
final body = distractionCount == 0
? 'You focused for $minutes minutes without distractions!'
: 'You focused for $minutes minutes. Great effort!';
await _notifications.show(0, title, body, details);
}
```
---
### 2. main.dart 初始化
**修改内容**:
```dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await StorageService.init();
// 新增:初始化通知服务
final notificationService = NotificationService();
await notificationService.initialize();
await notificationService.requestPermissions();
runApp(MyApp(...));
}
```
**何时调用**:
- 应用启动时自动初始化
- 自动请求权限iOS 会弹出权限对话框)
---
### 3. FocusScreen 计时完成
**修改位置**: `lib/screens/focus_screen.dart` 第 56-79 行
**修改内容**:
```dart
void _onTimerComplete() async {
_timer.cancel();
_saveFocusSession(completed: true);
// 新增:发送通知
final notificationService = NotificationService();
await notificationService.showFocusCompletedNotification(
minutes: widget.durationMinutes,
distractionCount: _distractions.length,
);
if (!mounted) return;
Navigator.pushReplacement(...);
}
```
**触发时机**:
- 计时器倒数到 0 时
- 在导航到 Complete Screen 之前
- 保存数据之后
---
## 📱 Android 配置
### AndroidManifest.xml
**文件位置**: `android/app/src/main/AndroidManifest.xml`
**添加的权限**:
```xml
<!-- Android 13+ 通知权限(必需)-->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- 震动权限(可选,增强体验)-->
<uses-permission android:name="android.permission.VIBRATE"/>
<!-- Wake Lock可选后台计时-->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
```
### 权限说明
| 权限 | 必需性 | 用途 | Android版本 |
|------|--------|------|------------|
| POST_NOTIFICATIONS | **必需** | 发送通知 | 13+ (API 33+) |
| VIBRATE | 可选 | 通知震动 | 所有版本 |
| WAKE_LOCK | 可选 | 后台计时 | 所有版本 |
### Android 权限流程
**Android 12 及以下**:
1. 自动授予通知权限
2. 无需用户确认
**Android 13+**:
1. 首次发送通知时,系统自动弹出权限对话框
2. 用户可以选择"允许"或"拒绝"
3. 拒绝后可在设置中手动开启
---
## 🍎 iOS 配置
### Info.plist
**不需要修改** - iOS 通知权限在运行时请求,无需配置文件声明。
### iOS 权限流程
1. **首次启动**:
- App 启动时调用 `requestPermissions()`
- 系统弹出权限对话框:
```
"FocusBuddy" Would Like to Send You Notifications
Notifications may include alerts, sounds, and icon badges.
[Don't Allow] [Allow]
```
2. **用户选择**:
- **允许**: 正常发送通知
- **拒绝**: 静默失败(不影响应用运行)
3. **后续修改**:
- 用户可在 设置 > FocusBuddy > 通知 中修改
---
## 🌐 Web 平台处理
### 策略:优雅降级
**为什么 Web 不支持**:
- `flutter_local_notifications` 不支持 Web
- Web 使用不同的通知 API需要 Service Worker
**如何处理**:
```dart
if (kIsWeb) {
print('Notifications not supported on web platform');
return; // 静默跳过,不报错
}
```
**用户体验**:
- Web 版用户不会看到通知
- 不会报错或崩溃
- 计时完成后仍正常跳转到 Complete Screen
**未来改进** (可选):
- 使用 Web Notification API
- 或显示应用内弹窗提示
---
## 🧪 测试指南
### Android 测试
#### 准备工作
```bash
# 1. 连接 Android 设备或启动模拟器
flutter devices
# 2. 运行应用
flutter run -d <android-device-id>
```
#### 测试步骤
**测试 1: 首次权限请求Android 13+**
```
1. 卸载应用(清除权限状态)
2. 重新安装并启动
3. 开始一次专注(设置为 1 分钟测试)
4. 等待计时完成
5. 预期:系统弹出权限对话框
6. 点击"允许"
7. 预期:看到通知
```
**测试 2: 前台通知**
```
1. 应用在前台
2. 开始专注1分钟
3. 等待完成
4. 预期:
- 顶部通知栏出现通知
- 有震动(如果手机未静音)
- 有声音(如果手机未静音)
- 自动跳转到 Complete Screen
```
**测试 3: 后台通知**
```
1. 开始专注1分钟
2. 按 Home 键,应用进入后台
3. 等待计时完成
4. 预期:
- 收到通知
- 点击通知可回到应用
- 看到 Complete Screen
```
**测试 4: 拒绝权限**
```
1. 在设置中禁用通知权限
2. 开始专注
3. 完成后不会有通知
4. 但应用正常跳转到 Complete Screen
5. 预期:无崩溃
```
---
### iOS 测试
#### 准备工作
```bash
# 需要 macOS + Xcode
# 1. 连接 iPhone 或启动模拟器
flutter devices
# 2. 运行应用
flutter run -d <ios-device-id>
```
#### 测试步骤
**测试 1: 权限对话框**
```
1. 首次启动应用
2. 预期:立即看到权限对话框
"FocusBuddy Would Like to Send You Notifications"
3. 点击 "Allow"
```
**测试 2: 通知内容**
```
1. 完成一次专注0次分心
2. 预期通知内容:
标题: 🎉 Focus session complete!
正文: You focused for 15 minutes without distractions!
3. 完成一次专注3次分心
4. 预期通知内容:
标题: 🎉 Focus session complete!
正文: You focused for 15 minutes. Great effort!
```
**测试 3: 后台通知**
```
1. 开始专注
2. 滑回主屏幕
3. 等待完成
4. 预期:锁屏/顶部有通知
5. 点击通知打开应用
```
---
### Web 测试
#### 测试步骤
```
1. 运行 Web 版flutter run -d edge
2. 开始专注
3. 等待完成
4. 预期:
- 控制台输出Notifications not supported on web platform
- 无通知
- 正常跳转到 Complete Screen
- 无报错
```
✅ **通过标准**: 应用正常运行,无崩溃
---
## 📊 通知内容逻辑
### 标题
固定内容:`🎉 Focus session complete!`
### 正文
动态内容,根据分心次数变化:
```dart
if (distractionCount == 0) {
body = 'You focused for $minutes minutes without distractions!';
} else {
body = 'You focused for $minutes minutes. Great effort!';
}
```
**示例**:
- 25分钟0次分心: "You focused for 25 minutes without distractions!"
- 15分钟3次分心: "You focused for 15 minutes. Great effort!"
### 设计理念
- **正向鼓励**: 即使有分心,也用"Great effort"
- **无惩罚**: 不会说"但你分心了3次"
- **符合产品价值观**: 温柔、支持、无评判
---
## 🔧 配置选项
### 当前实现
| 特性 | Android | iOS |
|------|---------|-----|
| 声音 | ✅ | ✅ |
| 震动 | ✅ | ✅ |
| Badge | ❌ | ✅ |
| 优先级 | High | Default |
| Channel | focus_completed | - |
### 可调整的参数
在 `notification_service.dart` 中:
```dart
// Android 配置
const androidDetails = AndroidNotificationDetails(
'focus_completed', // Channel ID不建议改
'Focus Session Completed', // Channel Name用户可见
channelDescription: '...', // Channel描述
importance: Importance.high, // 重要性(改为 max 会横幅提示)
priority: Priority.high, // 优先级
enableVibration: true, // 震动开关
playSound: true, // 声音开关
);
// iOS 配置
const iosDetails = DarwinNotificationDetails(
presentAlert: true, // 显示提示
presentBadge: true, // 显示角标
presentSound: true, // 播放声音
);
```
---
## 🚀 未来改进建议
### 优先级低(可选)
1. **自定义通知声音**
- 添加温柔的提示音
- 替换系统默认声音
- 需要音频资源
2. **定时提醒**
- "你已经2小时没专注了要来一次吗"
- 使用 `showReminderNotification()`
- 需要后台任务WorkManager
3. **通知操作按钮**
- Android: 通知上添加 "再来一次" 按钮
- 点击直接开始新的专注
- 需要额外配置
4. **通知统计**
- "本周你已经专注了 12 小时!"
- 定期(如周日)发送总结
- 需要调度逻辑
5. **Web 通知支持**
- 使用 Web Notification API
- 需要 Service Worker
- 需要用户手动授权
---
## ❗ 常见问题
### Q1: 为什么 Android 13 没有弹出权限对话框?
**A**: Android 13+ 权限会在**首次发送通知时**自动弹出,不是应用启动时。
**解决方案**:
- 完成一次完整的专注会话
- 或在设置中手动开启
---
### Q2: iOS 模拟器收不到通知?
**A**: iOS 模拟器通知功能有限制。
**解决方案**:
- 使用真机测试
- 或检查模拟器的通知设置
---
### Q3: Web 版为什么没有通知?
**A**: `flutter_local_notifications` 不支持 Web。
**当前方案**: 优雅降级,不报错
**未来方案**: 实现 Web Notification API
---
### Q4: 通知没有声音?
**A**: 检查以下设置:
1. 手机是否静音/勿扰模式
2. 应用通知权限是否开启
3. 通知重要性是否足够高
---
### Q5: 后台计时不准确?
**A**: Android 后台限制可能影响计时。
**建议**:
- 添加前台服务Foreground Service
- 或使用 WorkManager
- 当前 MVP 不实现,用户应在前台使用
---
## 📝 总结
### ✅ 已实现
- [x] 通知服务架构
- [x] 计时完成通知
- [x] Android 权限配置
- [x] iOS 权限请求
- [x] Web 平台兼容
- [x] 完整文档
### 📊 影响
- **代码变更**: 3 个文件新增1个修改2个
- **新增行数**: ~200 行
- **配置变更**: Android + iOS 权限
- **测试时间**: ~15 分钟(手动测试)
### 🎯 MVP 完成度
```
██████████████████▓░ 95% → 98%
```
**新增功能**: 本地通知 ✅
**待完成**: 应用图标、截图、上架准备
---
**文档版本**: 1.0
**最后更新**: 2025-11-22
**维护者**: Claude