chore: 完成多模块迭代优化与依赖更新

本次提交包含多项更新:
1. 更新file_picker依赖到11.0.0-ohos.1版本
2. 清理SecureStorage、Catcher2配置冗余代码
3. 优化鸿蒙系统下HomeWidget调用方式
4. 重构编辑器导航栏图标与页面路由引用
5. 修复边框样式、简化空值判断逻辑
6. 移除冗余系统UI样式配置
7. 新增共享组件导出与自适应返回按钮
8. 批量替换路由引用为app_routes
9. 标记过时通知服务并补充注释
10. 新增引导页扫一扫功能卡片
11. 完善沉浸式状态栏配置逻辑
12. 为大量页面添加统一自适应返回按钮
This commit is contained in:
Developer
2026-05-22 23:14:38 +08:00
parent 1a42e347cf
commit 85d856f0ed
161 changed files with 9058 additions and 3459 deletions

View File

@@ -1,11 +1,13 @@
/// ============================================================
/// 闲言APP — 权限管理服务
/// 创建时间: 2026-04-23
/// 更新时间: 2026-05-21
/// 作用: 统一管理应用权限请求,支持相机/相册/通知/位置/蓝牙/附近设备权限
/// 上次更新: 重写AppPermission枚举移除废弃storage权限新增isRequired和usageScenes字段
/// 更新时间: 2026-05-22
/// 作用: 统一管理应用权限请求,支持相机/相册/通知/位置/蓝牙/附近设备/麦克风/存储/网络/剪贴板权限
/// 上次更新: 移除3个高敏感权限(悬浮窗/通讯录/忽略电池优化),降低应用商店审核风险
/// ============================================================
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:permission_handler/permission_handler.dart';
@@ -87,7 +89,41 @@ enum AppPermission {
Color(0xFF64D2FF),
usageScenes: ['文件传输 — 局域网发现', '设备连接 — WiFi直连'],
),
;
microphone(
'麦克风',
Permission.microphone,
CupertinoIcons.mic_fill,
'用于语音朗读句子、语音搜索、AI对话语音输入。仅在您主动使用语音功能时请求不会后台录音。',
Color(0xFFFF3B30),
usageScenes: ['语音朗读 — 朗读句子', '语音搜索 — 语音输入关键词', 'AI对话 — 语音输入消息'],
),
storage(
'存储空间',
Permission.storage,
CupertinoIcons.folder_fill,
'用于保存编辑的卡片、壁纸到本地导出字体文件和数据。Android 12及以下版本需要此权限。',
Color(0xFFFF9500),
usageScenes: ['保存卡片 — 导出到本地', '字体管理 — 下载字体文件', '数据导出 — 导出用户数据'],
),
network(
'网络连接',
Permission.notification,
CupertinoIcons.wifi,
'闲言需要网络连接来获取句子、同步数据和推送通知。请在系统设置中确保网络权限已开启。',
Color(0xFF007AFF),
isRequired: true,
isVirtual: true,
usageScenes: ['句子获取 — 加载每日推荐', '数据同步 — 云端同步', '推送通知 — 接收消息'],
),
clipboard(
'剪贴板',
Permission.notification,
CupertinoIcons.doc_on_clipboard_fill,
'用于复制句子到剪贴板、粘贴文本到编辑器。应用仅在您主动操作时访问剪贴板,不会自动读取。',
Color(0xFFAF52DE),
isVirtual: true,
usageScenes: ['复制句子 — 一键复制', '编辑器 — 粘贴文本', '搜索 — 粘贴关键词'],
);
const AppPermission(
this.label,
@@ -96,6 +132,7 @@ enum AppPermission {
this.description,
this.color, {
this.isRequired = false,
this.isVirtual = false,
this.usageScenes = const [],
});
@@ -105,7 +142,26 @@ enum AppPermission {
final String description;
final Color color;
final bool isRequired;
final bool isVirtual;
final List<String> usageScenes;
/// Android 13+ 不需要 storage 权限(由 photos 替代)
bool get isPlatformRelevant {
if (this == AppPermission.storage) {
if (!Platform.isAndroid) return false;
final sdkInt = _androidSdkInt;
return sdkInt != null && sdkInt <= 32;
}
return true;
}
static int? get _androidSdkInt {
try {
return int.tryParse(Platform.version.split('.').first);
} catch (_) {
return null;
}
}
}
/// 权限管理服务 — iOS 风格权限请求
@@ -114,6 +170,9 @@ class PermissionService {
/// 检查单个权限状态
static Future<AppPermissionStatus> checkStatus(AppPermission perm) async {
if (perm.isVirtual) {
return _checkVirtualStatus(perm);
}
try {
final status = await perm.permission.status;
return AppPermissionStatus.fromPermissionStatus(status);
@@ -123,11 +182,26 @@ class PermissionService {
}
}
/// 批量查询所有权限状态
/// 虚拟权限状态检查
static Future<AppPermissionStatus> _checkVirtualStatus(
AppPermission perm,
) async {
switch (perm) {
case AppPermission.network:
return AppPermissionStatus.granted;
case AppPermission.clipboard:
return AppPermissionStatus.granted;
default:
return AppPermissionStatus.granted;
}
}
/// 批量查询所有权限状态(过滤平台不相关权限)
static Future<Map<AppPermission, AppPermissionStatus>>
checkAllStatus() async {
final results = <AppPermission, AppPermissionStatus>{};
for (final perm in AppPermission.values) {
if (!perm.isPlatformRelevant) continue;
results[perm] = await checkStatus(perm);
}
return results;
@@ -139,6 +213,7 @@ class PermissionService {
AppPermission perm, {
String? rationale,
}) async {
if (perm.isVirtual) return true;
try {
final status = await perm.permission.status;
@@ -177,6 +252,10 @@ class PermissionService {
}) async {
final results = <AppPermission, bool>{};
for (final perm in permissions) {
if (perm.isVirtual) {
results[perm] = true;
continue;
}
results[perm] = await requestPermission(
context,
perm,
@@ -216,6 +295,14 @@ class PermissionService {
rationale: '需要位置权限才能提供天气和节气信息',
);
/// 快捷方法: 请求麦克风权限
static Future<bool> requestMicrophone(BuildContext context) =>
requestPermission(
context,
AppPermission.microphone,
rationale: '需要麦克风权限才能使用语音功能',
);
/// 打开系统设置
static Future<bool> openSettings() => openAppSettings();

View File

@@ -8,8 +8,6 @@
import 'package:catcher_2/catcher_2.dart';
import 'package:catcher_2/model/platform_type.dart';
import 'package:catcher_2/model/report.dart';
import 'package:catcher_2/model/report_mode.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' show SelectableText;

View File

@@ -303,7 +303,7 @@ class HomeWidgetService {
// 官方SDK下不支持使用 dynamic 调用绕过编译检查
if (pu.isOhos) {
try {
final dynamic updateWidget = HomeWidget.updateWidget;
const dynamic updateWidget = HomeWidget.updateWidget;
await updateWidget(
qualifiedAndroidName: type.qualifiedAndroidName,
androidName: type.androidProviderName,

View File

@@ -1,12 +1,13 @@
// ============================================================
// 闲言APP — 设备信息服务
// 创建时间: 2026-05-10
// 更新时间: 2026-05-20
// 更新时间: 2026-05-22
// 作用: 采集设备信息并自动注册到服务端
// 上次更新: v14.31.0 新增cachedDeviceModel/cachedDeviceName缓存+initCache()方法
// 上次更新: 新增refresh()/clearCache()方法,支持强制刷新设备缓存
// ============================================================
import 'dart:io';
import 'dart:math';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
@@ -24,6 +25,7 @@ class DeviceInfoService {
static final _deviceInfoPlugin = DeviceInfoPlugin();
static const _prefKeyDeviceRegistered = 'device_registered_v2';
static const _prefKeyFallbackDeviceId = 'fallback_device_id';
static String? _cachedDeviceModel;
static String? _cachedDeviceName;
@@ -36,7 +38,357 @@ class DeviceInfoService {
_cachedDeviceName ??= await getDeviceName();
}
/// 获取设备唯一标识
// ============================================================
// 品牌中文名映射
// ============================================================
static const Map<String, String> _brandNameMap = {
'huawei': '华为',
'honor': '荣耀',
'xiaomi': '小米',
'redmi': '红米',
'oppo': 'OPPO',
'vivo': 'vivo',
'oneplus': '一加',
'realme': '真我',
'meizu': '魅族',
'samsung': '三星',
'zte': '中兴',
'lenovo': '联想',
'motorola': '摩托罗拉',
'nubia': '努比亚',
'iqoo': 'iQOO',
'pixel': 'Google Pixel',
'apple': 'Apple',
'sony': '索尼',
'asus': '华硕',
'rog': 'ROG',
'nothing': 'Nothing',
'transsion': '传音',
'tecno': 'TECNO',
'infinix': 'Infinix',
'poco': 'POCO',
'sharp': '夏普',
'htc': 'HTC',
'lg': 'LG',
'nokia': '诺基亚',
'google': 'Google',
};
// ============================================================
// 热门型号友好名称映射
// ============================================================
static const Map<String, String> _modelNameMap = {
// 华为
'ELE-AL00': '华为 P40',
'ELE-TL00': '华为 P40',
'ANA-AL00': '华为 P40 Pro',
'ANA-TL00': '华为 P40 Pro',
'ELS-AN00': '华为 P40 Pro+',
'JAD-AL00': '华为 P50',
'JAD-AN00': '华为 P50',
'JAD-LX9': '华为 P50',
'JAD-AL50': '华为 P50 Pro',
'ABR-AL00': '华为 P50 Pro',
'ALT-AL00': '华为 P50 Pocket',
'MATE-AL00': '华为 Mate 50',
'MATE-AN00': '华为 Mate 50',
'CET-AL00': '华为 Mate 50 Pro',
'CET-AN00': '华为 Mate 50 Pro',
'TAH-AN00': '华为 Mate 60',
'TAH-AN00m': '华为 Mate 60',
'ALT-AN00': '华为 Mate 60 Pro',
'BRA-AN00': '华为 Mate 60 Pro+',
'ALN-AL00': '华为 Mate 30',
'ALN-AN00': '华为 Mate 30',
'LIO-AL00': '华为 Mate 30 Pro',
'LIO-AN00': '华为 Mate 30 Pro',
'OCE-AN10': '华为 Mate 40',
'NOH-AN00': '华为 Mate 40 Pro',
'NOH-AN01': '华为 Mate 40 Pro',
'NOP-AN00': '华为 Mate 40 Pro+',
'TET-AN00': '华为 Mate X',
'TET-AN10': '华为 Mate X',
'TAH-N1H': '华为 Mate X3',
'GEM-AN00': '华为 Mate X5',
'WGR-AN00': '华为 nova 12',
'ADA-AL00': '华为 nova 7',
'ANG-AN00': '华为 nova 8',
'RTE-AL00': '华为 nova 9',
'NAM-AL00': '华为 nova 10',
'BZT-AN00': '华为 nova 11',
'FIG-AL00': '华为 Mate 20',
'FIG-TL00': '华为 Mate 20',
'LYA-AL00': '华为 Mate 20 Pro',
'LYA-AN00': '华为 Mate 20 Pro',
'EVR-AL00': '华为 Mate 20 X',
'EVR-AN00': '华为 Mate 20 X',
'VOG-AL00': '华为 P30',
'VOG-AN00': '华为 P30 Pro',
'VCE-AL00': '华为 P30',
'MAR-AL00': '华为 P30 lite',
// 荣耀
'ANY-AN00': '荣耀 30',
'BMH-AN10': '荣耀 30S',
'EBG-AN00': '荣耀 50',
'FNE-AN00': '荣耀 60',
'FRI-AN00': '荣耀 70',
'VMA-AN00': '荣耀 80',
'MGI-AN00': '荣耀 90',
'MAA-AN00': '荣耀 Magic5',
'BVL-AN00': '荣耀 Magic6',
'BVL-N49': '荣耀 Magic6 Pro',
// 小米
'23127PN0CG': '小米 14',
'23116PN5BC': '小米 14 Pro',
'23117RA68G': '小米 14 Ultra',
'2211133C': '小米 13',
'2210132C': '小米 13 Pro',
'2304FPN6DG': '小米 13 Ultra',
'2201123C': '小米 12',
'2201122C': '小米 12 Pro',
'2203121C': '小米 12S Ultra',
'M2011K2C': '小米 11',
'M2012K11AC': '小米 11 Pro',
'M2102K1C': '小米 11 Ultra',
'M2007J22C': '小米 10',
'M2001J2C': '小米 10 Pro',
'M2004J11C': '小米 10 Ultra',
'M1902F1A': '小米 9',
'M1803E1A': '小米 8',
'M2101K9C': '小米 11i',
'23013RK75C': '小米 Civi 3',
'23076MO4BC': '小米 Civi 4 Pro',
'24030PN60G': '小米 14 CIVI',
'24053PN09G': '小米 15',
'24129PN74G': '小米 15 Pro',
// 红米
'2312DRA50G': '红米 Note 13 Pro',
'23090RA98G': '红米 Note 13',
'23076RA4DG': '红米 12',
'22101316G': '红米 Note 12 Pro',
'22111319G': '红米 Note 12',
'22071212AG': '红米 Note 11 Pro',
'2201117TG': '红米 Note 11',
'M2010J19SY': '红米 Note 9 Pro',
'M2003J15SC': '红米 Note 9',
'23122RAA0G': '红米 K70',
'23113RKC6G': '红米 K70 Pro',
'23078PND5G': '红米 K60',
'22127RK46C': '红米 K60 Pro',
'22081212C': '红米 K50 Ultra',
'22041211AC': '红米 K50 Pro',
'22041216C': '红米 K50',
'21081111RG': '红米 K40 Pro',
'M2012K11C': '红米 K40',
'M2007J22B': '红米 K30 Pro',
'24122RKC7C': '红米 K80',
'24117RKV7C': '红米 K80 Pro',
// OPPO
'CPH2591': 'OPPO Find X7',
'PHZ110': 'OPPO Find X7 Ultra',
'CPH2505': 'OPPO Find X6 Pro',
'CPH2449': 'OPPO Find X5 Pro',
'CPH2375': 'OPPO Find X3 Pro',
'CPH2581': 'OPPO Reno 11 Pro',
'CPH2521': 'OPPO Reno 10 Pro',
'CPH2441': 'OPPO Reno 9 Pro',
'PFDM00': 'OPPO Find N3',
'PEQM00': 'OPPO Find N2',
'PGBM10': 'OPPO Reno 12 Pro',
'PJC110': 'OPPO Find X8',
'PJC1': 'OPPO Find X8 Pro',
// vivo
'V2324A': 'vivo X100',
'V2324HA': 'vivo X100 Pro',
'V2231A': 'vivo X90 Pro',
'V2219A': 'vivo X90',
'V2145A': 'vivo X80 Pro',
'V2171A': 'vivo X Fold3',
'V2307A': 'vivo X Fold3 Pro',
'V2338A': 'vivo S18 Pro',
'V2355A': 'vivo S19 Pro',
'V2408A': 'vivo X200',
'V2408CA': 'vivo X200 Pro',
// iQOO
'I2302': 'iQOO 12',
'I2219': 'iQOO 11',
'I2123': 'iQOO 10',
'I2012': 'iQOO 9 Pro',
'V2339A': 'iQOO Neo9',
'V2403A': 'iQOO Neo9S Pro',
'V2352A': 'iQOO Z9 Turbo',
// 一加
'CPH2583': '一加 12',
'CPH2415': '一加 10 Pro',
'PJD110': '一加 13',
'PGB110': '一加 Ace 3',
'PHB110': '一加 Ace 2 Pro',
'PJE110': '一加 Ace 3V',
// 真我
'RMX3901': '真我 GT5 Pro',
'RMX3700': '真我 GT5',
'RMX3708': '真我 GT Neo5',
'RMX3881': '真我 GT Neo6',
'RMX3921': '真我 GT Neo6 SE',
'RMX3999': '真我 12 Pro+',
'RMX3951': '真我 13 Pro+',
// 魅族
'MEIZU 21': '魅族 21',
'MEIZU 21 Pro': '魅族 21 Pro',
'MEIZU 20': '魅族 20',
'MEIZU 20 Pro': '魅族 20 Pro',
'MEIZU 20 Infinity': '魅族 20 Infinity',
'M1916Q': '魅族 18',
'M1917Q': '魅族 18 Pro',
// 三星
'SM-S928B': '三星 Galaxy S25 Ultra',
'SM-S926B': '三星 Galaxy S25+',
'SM-S921B': '三星 Galaxy S25',
'SM-S928U': '三星 Galaxy S25 Ultra',
'SM-S928N': '三星 Galaxy S25 Ultra',
'SM-S918B': '三星 Galaxy S24 Ultra',
'SM-S916B': '三星 Galaxy S24+',
'SM-S924B': '三星 Galaxy S24',
'SM-S908B': '三星 Galaxy S23 Ultra',
'SM-S906B': '三星 Galaxy S23+',
'SM-S901B': '三星 Galaxy S23',
'SM-S911B': '三星 Galaxy S22 Ultra',
'SM-S901E': '三星 Galaxy S22',
'SM-G998B': '三星 Galaxy S21 Ultra',
'SM-G996B': '三星 Galaxy S21+',
'SM-G991B': '三星 Galaxy S21',
'SM-N986B': '三星 Galaxy Note20 Ultra',
'SM-N981B': '三星 Galaxy Note20',
'SM-F731B': '三星 Galaxy Z Flip5',
'SM-F946B': '三星 Galaxy Z Fold5',
'SM-F721B': '三星 Galaxy Z Flip4',
'SM-F936B': '三星 Galaxy Z Fold4',
// 中兴
'ZTE A2023': '中兴 Axon 40 Ultra',
'ZTE V8020': '中兴 Axon 50 Ultra',
// 努比亚
'NX712J': '努比亚 Z50 Ultra',
'NX769J': '努比亚 Z60 Ultra',
// 联想
'L78032': '联想 拯救者 Y70',
'L38111': '联想 拯救者 Y90',
// 摩托罗拉
'XT2301-5': '摩托罗拉 edge 40 Pro',
'XT2401-1': '摩托罗拉 edge 50 Ultra',
};
// ============================================================
// iOS 设备型号映射 (utsname.machine → 友好名称)
// ============================================================
static const Map<String, String> _iosModelMap = {
'iPhone17,1': 'iPhone 16 Pro',
'iPhone17,2': 'iPhone 16 Pro Max',
'iPhone17,3': 'iPhone 16',
'iPhone17,4': 'iPhone 16 Plus',
'iPhone17,5': 'iPhone 16e',
'iPhone16,1': 'iPhone 15 Pro',
'iPhone16,2': 'iPhone 15 Pro Max',
'iPhone16,3': 'iPhone 15',
'iPhone16,4': 'iPhone 15 Plus',
'iPhone15,2': 'iPhone 14 Pro',
'iPhone15,3': 'iPhone 14 Pro Max',
'iPhone15,4': 'iPhone 14',
'iPhone15,5': 'iPhone 14 Plus',
'iPhone14,2': 'iPhone 13 Pro',
'iPhone14,3': 'iPhone 13 Pro Max',
'iPhone14,4': 'iPhone 13 mini',
'iPhone14,5': 'iPhone 13',
'iPhone14,6': 'iPhone SE (3rd gen)',
'iPhone14,7': 'iPhone 13',
'iPhone14,8': 'iPhone 13',
'iPhone13,2': 'iPhone 12',
'iPhone13,3': 'iPhone 12',
'iPhone13,4': 'iPhone 12 Pro',
'iPhone13,5': 'iPhone 12 Pro',
'iPhone13,6': 'iPhone 12 mini',
'iPhone13,7': 'iPhone 12 mini',
'iPhone12,1': 'iPhone 11',
'iPhone12,3': 'iPhone 11 Pro',
'iPhone12,5': 'iPhone 11 Pro Max',
'iPhone12,8': 'iPhone SE (2nd gen)',
'iPhone11,2': 'iPhone XS',
'iPhone11,4': 'iPhone XS Max',
'iPhone11,8': 'iPhone XR',
'iPhone10,3': 'iPhone X',
'iPhone10,6': 'iPhone X',
'iPad16,1': 'iPad Pro 13" (M4)',
'iPad16,2': 'iPad Pro 13" (M4)',
'iPad16,3': 'iPad Pro 11" (M4)',
'iPad16,4': 'iPad Pro 11" (M4)',
'iPad14,1': 'iPad Air (M2)',
'iPad14,2': 'iPad Air (M2)',
'iPad14,3': 'iPad Pro 12.9" (M2)',
'iPad14,4': 'iPad Pro 12.9" (M2)',
'iPad14,5': 'iPad Pro 11" (M2)',
'iPad14,6': 'iPad Pro 11" (M2)',
'iPad13,1': 'iPad Air (5th gen)',
'iPad13,2': 'iPad Air (5th gen)',
'iPad13,4': 'iPad Pro 12.9" (M1)',
'iPad13,5': 'iPad Pro 12.9" (M1)',
'iPad13,8': 'iPad Pro 11" (M1)',
'iPad13,9': 'iPad Pro 11" (M1)',
'iPad13,16': 'iPad (10th gen)',
'iPad13,17': 'iPad (10th gen)',
'iPad13,18': 'iPad mini (6th gen)',
'iPad13,19': 'iPad mini (6th gen)',
};
// ============================================================
// 品牌中文名查询
// ============================================================
static String getBrandChineseName(String brand) {
final key = brand.toLowerCase().trim();
return _brandNameMap[key] ?? brand;
}
// ============================================================
// 型号友好名称查询
// ============================================================
static String? getModelFriendlyName(String model) {
final trimmed = model.trim();
if (trimmed.isEmpty) return null;
if (_modelNameMap.containsKey(trimmed)) return _modelNameMap[trimmed];
if (_iosModelMap.containsKey(trimmed)) return _iosModelMap[trimmed];
for (final entry in _modelNameMap.entries) {
if (trimmed.toUpperCase().contains(entry.key.toUpperCase())) {
return entry.value;
}
}
return null;
}
// ============================================================
// 生成稳定UUID
// ============================================================
static String _generateStableUuid() {
final r = Random();
const chars = '0123456789abcdef';
const segments = [8, 4, 4, 4, 12];
final buf = StringBuffer();
for (var i = 0; i < segments.length; i++) {
if (i > 0) buf.write('-');
for (var j = 0; j < segments[i]; j++) {
buf.write(chars[r.nextInt(16)]);
}
}
return buf.toString();
}
// ============================================================
// 获取设备唯一标识
// ============================================================
static Future<String> getDeviceId() async {
try {
if (pu.isOhos) {
@@ -52,54 +404,87 @@ class DeviceInfoService {
} catch (e) {
Log.w('获取设备ID失败: $e');
}
return 'unknown_${DateTime.now().millisecondsSinceEpoch}';
final prefs = await SharedPreferences.getInstance();
var fallbackId = prefs.getString(_prefKeyFallbackDeviceId);
if (fallbackId != null && fallbackId.isNotEmpty)
return 'fallback_$fallbackId';
fallbackId = _generateStableUuid();
await prefs.setString(_prefKeyFallbackDeviceId, fallbackId);
Log.i('生成稳定回退设备ID: $fallbackId');
return 'fallback_$fallbackId';
}
/// 获取设备名称
// ============================================================
// 获取设备名称 (友好名称)
// ============================================================
static Future<String> getDeviceName() async {
if (_cachedDeviceName != null) return _cachedDeviceName!;
try {
if (pu.isOhos) {
final android = await _deviceInfoPlugin.androidInfo;
final friendly = _buildFriendlyName(android.brand, android.model);
if (friendly != null) return friendly;
if (android.model.isNotEmpty) return android.model;
if (android.brand.isNotEmpty) return android.brand;
if (android.brand.isNotEmpty) return getBrandChineseName(android.brand);
if (android.display.isNotEmpty) return android.display;
if (android.product.isNotEmpty) return android.product;
return 'HarmonyOS Device';
return '鸿蒙设备';
} else if (Platform.isAndroid) {
final android = await _deviceInfoPlugin.androidInfo;
return android.model.isNotEmpty
? android.model
: (android.brand.isNotEmpty ? android.brand : 'Android');
final friendly = _buildFriendlyName(android.brand, android.model);
if (friendly != null) return friendly;
if (android.model.isNotEmpty) return android.model;
if (android.brand.isNotEmpty) return getBrandChineseName(android.brand);
return 'Android 设备';
} else if (Platform.isIOS) {
final ios = await _deviceInfoPlugin.iosInfo;
return ios.utsname.machine.isNotEmpty ? ios.utsname.machine : 'iPhone';
final machine = ios.utsname.machine;
if (machine.isNotEmpty) {
final friendly = _iosModelMap[machine];
if (friendly != null) return friendly;
return machine;
}
return 'iPhone';
}
} catch (e) {
Log.w('获取设备名称失败: $e');
}
return 'Unknown';
return '未知设备';
}
// ============================================================
// 获取设备型号 (品牌+型号)
// ============================================================
static Future<String> getDeviceModel() async {
if (_cachedDeviceModel != null) return _cachedDeviceModel!;
try {
if (pu.isOhos) {
final android = await _deviceInfoPlugin.androidInfo;
final brand = android.brand.isNotEmpty ? android.brand : 'HarmonyOS';
final model = android.model.isNotEmpty
? android.model
: (android.display.isNotEmpty ? android.display : (android.product.isNotEmpty ? android.product : 'Device'));
final result = '$brand $model'.trim();
return result == 'HarmonyOS Device' ? 'HarmonyOS Device' : result;
return _buildDeviceModel(
android.brand,
android.model,
android.display,
android.product,
isOhos: true,
);
} else if (Platform.isAndroid) {
final android = await _deviceInfoPlugin.androidInfo;
return '${android.brand} ${android.model}'.trim();
return _buildDeviceModel(
android.brand,
android.model,
android.display,
android.product,
);
} else if (Platform.isIOS) {
final ios = await _deviceInfoPlugin.iosInfo;
return ios.utsname.machine.isNotEmpty
? ios.utsname.machine
: 'iOS Device';
final machine = ios.utsname.machine;
if (machine.isNotEmpty) {
final friendly = _iosModelMap[machine];
return friendly ?? machine;
}
return 'iOS Device';
}
} catch (e) {
Log.w('获取设备型号失败: $e');
@@ -107,7 +492,57 @@ class DeviceInfoService {
return 'Unknown';
}
/// 获取平台标识
// ============================================================
// 构建友好设备名称
// ============================================================
static String? _buildFriendlyName(String brand, String model) {
if (model.isNotEmpty) {
final friendly = getModelFriendlyName(model);
if (friendly != null) return friendly;
}
if (brand.isNotEmpty && model.isNotEmpty) {
final brandCn = getBrandChineseName(brand);
if (brandCn != brand) return '$brandCn $model';
}
return null;
}
// ============================================================
// 构建设备型号字符串
// ============================================================
static String _buildDeviceModel(
String brand,
String model,
String display,
String product, {
bool isOhos = false,
}) {
final brandCn = brand.isNotEmpty
? getBrandChineseName(brand)
: (isOhos ? '鸿蒙' : '');
final modelFallback = model.isNotEmpty
? model
: (display.isNotEmpty ? display : (product.isNotEmpty ? product : ''));
if (model.isNotEmpty) {
final friendly = getModelFriendlyName(model);
if (friendly != null) return friendly;
}
if (brandCn.isNotEmpty && modelFallback.isNotEmpty) {
return '$brandCn $modelFallback';
}
if (brandCn.isNotEmpty) return brandCn;
if (modelFallback.isNotEmpty) return modelFallback;
return isOhos ? '鸿蒙设备' : 'Android 设备';
}
// ============================================================
// 获取平台标识
// ============================================================
static String getPlatform() {
if (kIsWeb) return 'web';
if (pu.isOhos) return 'ohos';
@@ -119,7 +554,10 @@ class DeviceInfoService {
return 'other';
}
/// 获取APP名称+版本
// ============================================================
// 获取APP名称+版本
// ============================================================
static Future<String> getAppName() async {
try {
final packageInfo = await PackageInfo.fromPlatform();
@@ -129,7 +567,10 @@ class DeviceInfoService {
}
}
/// 注册设备到服务端(登录后调用)
// ============================================================
// 注册设备到服务端(登录后调用)
// ============================================================
static Future<bool> registerDeviceIfNeeded() async {
try {
final deviceId = await getDeviceId();
@@ -138,7 +579,6 @@ class DeviceInfoService {
final platform = getPlatform();
final appName = await getAppName();
// 查询IP归属地(不阻塞注册,失败时降级)
String? ipCity;
String? ipRange;
try {
@@ -185,11 +625,46 @@ class DeviceInfoService {
}
}
/// 重置注册状态(退出登录时调用)
// ============================================================
// 重置注册状态(退出登录时调用)
// ============================================================
static Future<void> resetRegistration() async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_prefKeyDeviceRegistered);
} catch (_) {}
}
// ============================================================
// 强制刷新设备缓存(重新读取平台信息+重新注册)
// ============================================================
static Future<void> refresh() async {
clearCache();
_cachedDeviceModel = await getDeviceModel();
_cachedDeviceName = await getDeviceName();
Log.i('设备缓存已刷新: model=$_cachedDeviceModel, name=$_cachedDeviceName');
try {
final prefs = await SharedPreferences.getInstance();
final wasRegistered = prefs.getBool(_prefKeyDeviceRegistered) ?? false;
if (wasRegistered) {
await registerDeviceIfNeeded();
Log.i('设备信息已重新注册到服务端');
}
} catch (e) {
Log.w('设备重新注册失败: $e');
}
}
// ============================================================
// 仅清除本地缓存(不重新注册)
// ============================================================
static void clearCache() {
_cachedDeviceModel = null;
_cachedDeviceName = null;
Log.d('设备缓存已清除');
}
}

View File

@@ -1,14 +1,15 @@
// ============================================================
// 闲言APP — 每日定时通知服务
// 创建时间: 2026-05-20
// 更新时间: 2026-05-20
// 更新时间: 2026-05-22
// 作用: 每日定时推送一句好句
// 上次更新: 初始版本
// 上次更新: @Deprecated 已合并至NotificationCenter请使用NotificationCenter替代
// ============================================================
import '../../utils/logger.dart';
import 'local_notification_service.dart';
@Deprecated('已合并至NotificationCenter请使用NotificationCenter替代')
class DailyNotifyService {
DailyNotifyService._();
static final DailyNotifyService instance = DailyNotifyService._();

View File

@@ -1,9 +1,9 @@
/// ============================================================
/// 闲言APP — 本地通知服务
/// 创建时间: 2026-05-02
/// 更新时间: 2026-05-17
/// 更新时间: 2026-05-22
/// 作用: 本地推送通知管理 (初始化/调度/取消/点击处理)
/// 上次更新: 鸿蒙适配-使用桥接方法隔离OhosInitializationSettings
/// 上次更新: 添加study_progress通知点击路由
/// ============================================================
import 'dart:io';
@@ -15,7 +15,7 @@ import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import '../../router/app_router.dart';
import 'notification_scheduler.dart';
import 'notification_center.dart';
import '../../storage/app_kv_store.dart';
import '../../utils/logger.dart';
import '../../utils/platform_utils.dart' as pu;
@@ -121,6 +121,8 @@ class LocalNotificationService {
context.appGo('/countdown');
case 'daily_fortune':
context.appGo('/daily-fortune');
case 'study_progress':
context.appGo('/home');
case 'readlater':
context.appGo('/readlater-chat');
default:
@@ -292,7 +294,7 @@ class LocalNotificationService {
}
static Future<void> setupDailyNotifications(WidgetRef ref) async {
if (!NotificationScheduler.isNotificationsEnabled) {
if (!NotificationCenter.isNotificationsEnabled) {
await cancelAll();
return;
}

View File

@@ -0,0 +1,303 @@
/// ============================================================
/// 闲言APP — 统一通知中心
/// 创建时间: 2026-05-22
/// 更新时间: 2026-05-22
/// 作用: 合并 NotificationScheduler + DailyNotifyService统一管理所有本地通知调度
/// 上次更新: 初始版本,合并每日推荐/签到/节气/运势/学习进度/稍后读通知
/// ============================================================
import 'local_notification_service.dart';
import '../../storage/app_kv_store.dart';
import '../../utils/logger.dart';
class NotificationCenter {
NotificationCenter._();
// ── 通知ID命名空间 ──
static const int _idDailyRecommend = 1001;
static const int _idSigninReminder = 1002;
static const int _idFortune = 1003;
static const int _idStudyProgress = 1004;
static const int _idSolarTerm = 2001;
// ── 存储键 ──
static const _keyNotificationsEnabled = 'notifications_enabled';
static const _keyDailyRecommendEnabled = 'daily_sentence_enabled';
static const _keyDailyRecommendHour = 'daily_sentence_hour';
static const _keyDailyRecommendMinute = 'daily_sentence_minute';
static const _keySigninReminderEnabled = 'signin_reminder_enabled';
static const _keySigninReminderHour = 'signin_reminder_hour';
static const _keySigninReminderMinute = 'signin_reminder_minute';
static const _keySolarTermEnabled = 'solar_term_enabled';
static const _keyFortuneEnabled = 'fortune_enabled';
static const _keyFortuneHour = 'fortune_hour';
static const _keyFortuneMinute = 'fortune_minute';
static const _keyStudyProgressEnabled = 'study_progress_enabled';
static const _keyReadlaterEnabled = 'notif_charging_readlater';
// ── 全局通知开关 ──
static bool get isNotificationsEnabled =>
AppKVStore.getBool(_keyNotificationsEnabled) ?? false;
static Future<void> setNotificationsEnabled(bool v) async {
await AppKVStore.setBool(_keyNotificationsEnabled, v);
await configureAll();
}
// ── 每日推荐 ──
static bool get isDailyRecommendEnabled =>
AppKVStore.getBool(_keyDailyRecommendEnabled) ?? true;
static int get dailyRecommendHour =>
AppKVStore.getInt(_keyDailyRecommendHour) ?? 8;
static int get dailyRecommendMinute =>
AppKVStore.getInt(_keyDailyRecommendMinute) ?? 0;
static Future<void> setDailyRecommendEnabled(bool v) async {
await AppKVStore.setBool(_keyDailyRecommendEnabled, v);
await configureAll();
}
static Future<void> setDailyRecommendTime(int hour, int minute) async {
await AppKVStore.setInt(_keyDailyRecommendHour, hour);
await AppKVStore.setInt(_keyDailyRecommendMinute, minute);
await configureAll();
}
// ── 签到提醒 ──
static bool get isSigninReminderEnabled =>
AppKVStore.getBool(_keySigninReminderEnabled) ?? true;
static int get signinReminderHour =>
AppKVStore.getInt(_keySigninReminderHour) ?? 20;
static int get signinReminderMinute =>
AppKVStore.getInt(_keySigninReminderMinute) ?? 0;
static Future<void> setSigninReminderEnabled(bool v) async {
await AppKVStore.setBool(_keySigninReminderEnabled, v);
await configureAll();
}
static Future<void> setSigninReminderTime(int hour, int minute) async {
await AppKVStore.setInt(_keySigninReminderHour, hour);
await AppKVStore.setInt(_keySigninReminderMinute, minute);
await configureAll();
}
// ── 节气通知 ──
static bool get isSolarTermEnabled =>
AppKVStore.getBool(_keySolarTermEnabled) ?? true;
static Future<void> setSolarTermEnabled(bool v) async {
await AppKVStore.setBool(_keySolarTermEnabled, v);
await configureAll();
}
// ── 每日运势 ──
static bool get isFortuneEnabled =>
AppKVStore.getBool(_keyFortuneEnabled) ?? false;
static int get fortuneHour => AppKVStore.getInt(_keyFortuneHour) ?? 8;
static int get fortuneMinute => AppKVStore.getInt(_keyFortuneMinute) ?? 0;
static Future<void> setFortuneEnabled(bool v) async {
await AppKVStore.setBool(_keyFortuneEnabled, v);
await configureAll();
}
static Future<void> setFortuneTime(int hour, int minute) async {
await AppKVStore.setInt(_keyFortuneHour, hour);
await AppKVStore.setInt(_keyFortuneMinute, minute);
await configureAll();
}
// ── 学习进度 ──
static bool get isStudyProgressEnabled =>
AppKVStore.getBool(_keyStudyProgressEnabled) ?? false;
static Future<void> setStudyProgressEnabled(bool v) async {
await AppKVStore.setBool(_keyStudyProgressEnabled, v);
await configureAll();
}
// ── 稍后读提醒 ──
static bool get isReadlaterEnabled =>
AppKVStore.getBool(_keyReadlaterEnabled) ?? false;
static Future<void> setReadlaterEnabled(bool v) async {
await AppKVStore.setBool(_keyReadlaterEnabled, v);
}
// ── 核心调度 ──
static Future<void> configureAll() async {
final enabled = AppKVStore.getBool(_keyNotificationsEnabled) ?? false;
if (!enabled) {
await cancelAllManaged();
Log.i('NotificationCenter: 通知已关闭,取消所有调度');
return;
}
await cancelAllManaged();
await _configureDailyRecommend();
await _configureSigninReminder();
await _configureSolarTerm();
await _configureFortune();
await _configureStudyProgress();
Log.i('NotificationCenter: 所有通知已配置');
}
static Future<void> cancelAllManaged() async {
await LocalNotificationService.cancel(_idDailyRecommend);
await LocalNotificationService.cancel(_idSigninReminder);
await LocalNotificationService.cancel(_idFortune);
await LocalNotificationService.cancel(_idStudyProgress);
await LocalNotificationService.cancel(_idSolarTerm);
}
// ── 各通知调度 ──
static Future<void> _configureDailyRecommend() async {
final enabled = AppKVStore.getBool(_keyDailyRecommendEnabled) ?? true;
if (!enabled) return;
final hour = AppKVStore.getInt(_keyDailyRecommendHour) ?? 8;
final minute = AppKVStore.getInt(_keyDailyRecommendMinute) ?? 0;
await LocalNotificationService.scheduleDaily(
id: _idDailyRecommend,
title: '闲言每日一句',
body: '今天的句子已准备好,来看看吧 ✨',
hour: hour,
minute: minute,
payload: 'daily_sentence',
);
}
static Future<void> _configureSigninReminder() async {
final enabled = AppKVStore.getBool(_keySigninReminderEnabled) ?? true;
if (!enabled) return;
final hour = AppKVStore.getInt(_keySigninReminderHour) ?? 20;
final minute = AppKVStore.getInt(_keySigninReminderMinute) ?? 0;
await LocalNotificationService.scheduleDaily(
id: _idSigninReminder,
title: '闲言 · 签到提醒',
body: '别忘了今日签到哦 📝',
hour: hour,
minute: minute,
payload: 'signin_reminder',
);
}
static Future<void> _configureSolarTerm() async {
final enabled = AppKVStore.getBool(_keySolarTermEnabled) ?? true;
if (!enabled) return;
final nextTerm = _getNextSolarTerm();
if (nextTerm == null) return;
final scheduledTime = DateTime(
nextTerm['year'] as int,
nextTerm['month'] as int,
nextTerm['day'] as int,
8,
);
await LocalNotificationService.scheduleOnce(
id: _idSolarTerm,
title: '闲言 · ${nextTerm['emoji']} ${nextTerm['name']}',
body: '今日${nextTerm['name']}${nextTerm['poem']}',
scheduledTime: scheduledTime,
payload: 'solar_term',
);
}
static Future<void> _configureFortune() async {
final enabled = AppKVStore.getBool(_keyFortuneEnabled) ?? false;
if (!enabled) return;
final hour = AppKVStore.getInt(_keyFortuneHour) ?? 8;
final minute = AppKVStore.getInt(_keyFortuneMinute) ?? 0;
await LocalNotificationService.scheduleDaily(
id: _idFortune,
title: '闲言 · 🔮 每日运势',
body: '今日运势已生成,快来看看你的运势吧 ✨',
hour: hour,
minute: minute,
payload: 'daily_fortune',
);
}
static Future<void> _configureStudyProgress() async {
final enabled = AppKVStore.getBool(_keyStudyProgressEnabled) ?? false;
if (!enabled) return;
await LocalNotificationService.scheduleDaily(
id: _idStudyProgress,
title: '闲言 · 学习进度',
body: '该复习今天的学习内容了 📊',
hour: 20,
payload: 'study_progress',
);
}
// ── 节气数据 ──
static Map<String, dynamic>? _getNextSolarTerm() {
final now = DateTime.now();
final terms = _solarTerms2026;
for (final term in terms) {
final date = DateTime(
term['year'] as int,
term['month'] as int,
term['day'] as int,
);
if (date.isAfter(now)) return term;
}
return terms.isNotEmpty ? terms.first : null;
}
static final List<Map<String, dynamic>> _solarTerms2026 = [
{'year': 2026, 'month': 1, 'day': 5, 'name': '小寒', 'emoji': '❄️', 'poem': '小寒连大吕,欢鹊垒新巢'},
{'year': 2026, 'month': 1, 'day': 20, 'name': '大寒', 'emoji': '🧊', 'poem': '大寒须守火,无事不出门'},
{'year': 2026, 'month': 2, 'day': 4, 'name': '立春', 'emoji': '🌱', 'poem': '春风如贵客,一到便繁华'},
{'year': 2026, 'month': 2, 'day': 18, 'name': '雨水', 'emoji': '🌧️', 'poem': '好雨知时节,当春乃发生'},
{'year': 2026, 'month': 3, 'day': 5, 'name': '惊蛰', 'emoji': '', 'poem': '微雨众卉新,一雷惊蛰始'},
{'year': 2026, 'month': 3, 'day': 20, 'name': '春分', 'emoji': '🌸', 'poem': '雪入春分省见稀,半开桃李不胜威'},
{'year': 2026, 'month': 4, 'day': 5, 'name': '清明', 'emoji': '🍃', 'poem': '清明时节雨纷纷,路上行人欲断魂'},
{'year': 2026, 'month': 4, 'day': 20, 'name': '谷雨', 'emoji': '🌾', 'poem': '谷雨如丝复似尘,煮瓶浮蜡正尝新'},
{'year': 2026, 'month': 5, 'day': 5, 'name': '立夏', 'emoji': '☀️', 'poem': '绿树阴浓夏日长,楼台倒影入池塘'},
{'year': 2026, 'month': 5, 'day': 21, 'name': '小满', 'emoji': '🌿', 'poem': '夜莺啼绿柳,皓月醒长空'},
{'year': 2026, 'month': 6, 'day': 5, 'name': '芒种', 'emoji': '🌻', 'poem': '时雨及芒种,四野皆插秧'},
{'year': 2026, 'month': 6, 'day': 21, 'name': '夏至', 'emoji': '🌞', 'poem': '昼晷已云极,宵漏自此长'},
{'year': 2026, 'month': 7, 'day': 7, 'name': '小暑', 'emoji': '🌡️', 'poem': '倏忽温风至,因循小暑来'},
{'year': 2026, 'month': 7, 'day': 22, 'name': '大暑', 'emoji': '🔥', 'poem': '大暑三秋近,林钟九夏移'},
{'year': 2026, 'month': 8, 'day': 7, 'name': '立秋', 'emoji': '🍂', 'poem': '乳鸦啼散玉屏空,一枕新凉一扇风'},
{'year': 2026, 'month': 8, 'day': 23, 'name': '处暑', 'emoji': '🎐', 'poem': '处暑无三日,新凉直万金'},
{'year': 2026, 'month': 9, 'day': 7, 'name': '白露', 'emoji': '💎', 'poem': '露从今夜白,月是故乡明'},
{'year': 2026, 'month': 9, 'day': 23, 'name': '秋分', 'emoji': '🍁', 'poem': '金气秋分,风清露冷秋期半'},
{'year': 2026, 'month': 10, 'day': 8, 'name': '寒露', 'emoji': '💧', 'poem': '袅袅凉风动,凄凄寒露零'},
{'year': 2026, 'month': 10, 'day': 23, 'name': '霜降', 'emoji': '🧊', 'poem': '霜降碧天静,秋事促西风'},
{'year': 2026, 'month': 11, 'day': 7, 'name': '立冬', 'emoji': '🧣', 'poem': '冻笔新诗懒写,寒炉美酒时温'},
{'year': 2026, 'month': 11, 'day': 22, 'name': '小雪', 'emoji': '🌨️', 'poem': '片片互玲珑,飞扬玉漏终'},
{'year': 2026, 'month': 12, 'day': 7, 'name': '大雪', 'emoji': '❄️', 'poem': '大雪江南见未曾,今年方始是严凝'},
{'year': 2026, 'month': 12, 'day': 21, 'name': '冬至', 'emoji': '🥟', 'poem': '天时人事日相催,冬至阳生春又来'},
];
}

View File

@@ -1,15 +1,16 @@
/// ============================================================
/// 闲言APP — 通知调度器
/// 创建时间: 2026-05-02
/// 更新时间: 2026-05-13
/// 作用: 统一管理本地通知调度 (每日推荐/签到/节气/番茄钟/运势)
/// 上次更新: 增加运势推送调度
/// 更新时间: 2026-05-22
/// 作用: 统一管理本地通知调度 (每日推荐/签到/节气/番茄钟/运势/学习进度)
/// 上次更新: @Deprecated 已合并至NotificationCenter请使用NotificationCenter替代
/// ============================================================
import 'local_notification_service.dart';
import '../../storage/app_kv_store.dart';
import '../../utils/logger.dart';
@Deprecated('已合并至NotificationCenter请使用NotificationCenter替代')
class NotificationScheduler {
NotificationScheduler._();
@@ -24,16 +25,25 @@ class NotificationScheduler {
static const _keyFortuneEnabled = 'fortune_enabled';
static const _keyFortuneHour = 'fortune_hour';
static const _keyFortuneMinute = 'fortune_minute';
static const _keyStudyProgressEnabled = 'study_progress_enabled';
static Future<void> _cancelAllManaged() async {
await LocalNotificationService.cancel(1001);
await LocalNotificationService.cancel(1002);
await LocalNotificationService.cancel(1003);
await LocalNotificationService.cancel(1004);
await LocalNotificationService.cancel(2001);
}
static Future<void> configureAll() async {
final enabled = AppKVStore.getBool(_keyNotificationsEnabled) ?? false;
if (!enabled) {
await LocalNotificationService.cancelAll();
await _cancelAllManaged();
Log.i('通知调度: 通知已关闭,取消所有调度');
return;
}
await LocalNotificationService.cancelAll();
await _cancelAllManaged();
final dailySentence = AppKVStore.getBool(_keyDailySentenceEnabled) ?? true;
if (dailySentence) {
@@ -41,8 +51,8 @@ class NotificationScheduler {
final minute = AppKVStore.getInt(_keyDailySentenceMinute) ?? 0;
await LocalNotificationService.scheduleDaily(
id: 1001,
title: '闲言 · 每日一句',
body: '新的一天,送你一句好话',
title: '闲言每日一句',
body: '今天的句子已准备好,来看看吧',
hour: hour,
minute: minute,
payload: 'daily_sentence',
@@ -83,6 +93,17 @@ class NotificationScheduler {
);
}
final studyProgress = AppKVStore.getBool(_keyStudyProgressEnabled) ?? false;
if (studyProgress) {
await LocalNotificationService.scheduleDaily(
id: 1004,
title: '闲言 · 学习进度',
body: '该复习今天的学习内容了 📊',
hour: 20,
payload: 'study_progress',
);
}
Log.i('通知调度: 所有通知已配置');
}
@@ -151,6 +172,9 @@ class NotificationScheduler {
static int get fortuneMinute => AppKVStore.getInt(_keyFortuneMinute) ?? 0;
static bool get isStudyProgressEnabled =>
AppKVStore.getBool(_keyStudyProgressEnabled) ?? false;
static Future<void> setNotificationsEnabled(bool v) async {
await AppKVStore.setBool(_keyNotificationsEnabled, v);
await configureAll();
@@ -194,6 +218,11 @@ class NotificationScheduler {
await configureAll();
}
static Future<void> setStudyProgressEnabled(bool v) async {
await AppKVStore.setBool(_keyStudyProgressEnabled, v);
await configureAll();
}
static final List<Map<String, dynamic>> _solarTerms2026 = [
{
'year': 2026,

View File

@@ -0,0 +1,65 @@
/// ============================================================
/// 闲言APP — 状态栏统一管理服务
/// 创建时间: 2026-05-22
/// 更新时间: 2026-05-22
/// 作用: 集中管理状态栏样式,响应主题变化,支持沉浸式模式
/// 上次更新: 初始创建,统一所有 SystemUiOverlayStyle 设置
/// ============================================================
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class StatusBarService {
StatusBarService._();
static SystemUiOverlayStyle resolveStyle({required bool isDark}) {
return SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: isDark ? Brightness.light : Brightness.dark,
statusBarBrightness: isDark ? Brightness.dark : Brightness.light,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarIconBrightness:
isDark ? Brightness.light : Brightness.dark,
systemNavigationBarDividerColor: Colors.transparent,
);
}
static void applyStyle({required bool isDark}) {
SystemChrome.setSystemUIOverlayStyle(resolveStyle(isDark: isDark));
}
static void setImmersive(bool immersive) {
if (immersive) {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.immersiveSticky,
);
} else {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.edgeToEdge,
);
}
}
static void enterEdgeToEdge() {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
}
}
class StatusBarStyleRegion extends StatelessWidget {
const StatusBarStyleRegion({
super.key,
required this.isDark,
required this.child,
});
final bool isDark;
final Widget child;
@override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: StatusBarService.resolveStyle(isDark: isDark),
child: child,
);
}
}