chore: 批量完成2026-05-24版本迭代更新

本次提交涵盖多项功能优化与重构:
1. 重构pro_image_editor依赖为官方托管版本,移除本地包引用
2. 拆分角色表情枚举至独立文件,优化代码复用性
3. 新增壁纸收藏、预加载、健康检测服务与本地存储支持
4. 完善API响应类型安全检查与排行榜服务能力
5. 新增应用锁设置路由与页面支持
6. 优化路由跳转使用常量路径替代硬编码字符串
7. 新增阅读报告分享功能与设置变更日志服务
8. 修复多处类型转换与空指针风险问题
9. 调整API超时时间优化网络请求表现
10. 统一文件头格式与部分UI组件样式
This commit is contained in:
Developer
2026-05-24 04:00:49 +08:00
parent f5a75cb6d3
commit df1f127a12
84 changed files with 11368 additions and 2176 deletions

View File

@@ -1,9 +1,9 @@
/// ============================================================
/// ============================================================
/// 闲言APP — 桌面小部件管理页面
/// 创建时间: 2026-05-19
/// 更新时间: 2026-05-23
/// 更新时间: 2026-05-24
/// 作用: 管理桌面小部件的安装、数据推送、主题和平台兼容说明
/// 上次更新: 修复添加小部件假成功提示,增加失败检测和原生添加弹窗
/// 上次更新: 修复添加防抖锁机制+手动指南区分设备不支持/API失败+推送数据按钮
/// ============================================================
import 'dart:async';
@@ -14,6 +14,7 @@ import '../../../core/theme/app_theme.dart';
import '../../../core/theme/app_spacing.dart';
import '../../../core/theme/app_typography.dart';
import '../../../core/theme/app_radius.dart';
import '../../../core/services/data/home_widget_service.dart';
import '../../../core/utils/platform/platform_helper.dart';
import '../../../shared/widgets/containers/glass_container.dart';
import '../../../shared/widgets/adaptive/responsive_layout.dart';
@@ -30,6 +31,8 @@ class WidgetManagementPage extends ConsumerStatefulWidget {
}
class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
bool _isAdding = false;
@override
void initState() {
super.initState();
@@ -143,6 +146,7 @@ class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
ext: ext,
type: type,
isInstalled: isInstalled,
isAdding: _isAdding,
onAdd: () => _handleAddWidget(type),
onPin: () => _handlePinWidget(type),
),
@@ -180,52 +184,78 @@ class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
};
void _handleAddWidget(WidgetType type) async {
final notifier = ref.read(wp.widgetProvider.notifier);
final wasInstalled = ref.read(wp.widgetProvider).installedWidgets.contains(type);
if (_isAdding) return;
_isAdding = true;
final pinSuccess = await notifier.requestPinWidget(type);
try {
final notifier = ref.read(wp.widgetProvider.notifier);
final wasInstalled = ref.read(wp.widgetProvider).installedWidgets.contains(type);
final result = await notifier.requestPinWidget(type);
if (!pinSuccess) {
if (!mounted) return;
_showManualAddGuide(type);
return;
}
await Future<void>.delayed(const Duration(seconds: 2));
if (result == wp.PinWidgetResult.unsupported) {
_showManualAddGuide(type, isUnsupported: true);
return;
}
if (!mounted) return;
await notifier.loadInstalledWidgets();
if (result == wp.PinWidgetResult.failed) {
_showManualAddGuide(type, isUnsupported: false);
return;
}
final nowInstalled = ref.read(wp.widgetProvider).installedWidgets.contains(type);
if (nowInstalled && !wasInstalled) {
AppToast.showInfo('${type.title} 已添加到桌面');
} else if (!nowInstalled) {
_showManualAddGuide(type);
await Future<void>.delayed(const Duration(seconds: 2));
if (!mounted) return;
await notifier.loadInstalledWidgets();
final nowInstalled = ref.read(wp.widgetProvider).installedWidgets.contains(type);
if (nowInstalled && !wasInstalled) {
AppToast.showInfo('${type.title} 已添加到桌面');
} else if (!nowInstalled) {
_showManualAddGuide(type, isUnsupported: false);
}
} finally {
_isAdding = false;
}
}
void _handlePinWidget(WidgetType type) async {
final notifier = ref.read(wp.widgetProvider.notifier);
final pinSuccess = await notifier.requestPinWidget(type);
if (_isAdding) return;
_isAdding = true;
try {
final notifier = ref.read(wp.widgetProvider.notifier);
final result = await notifier.requestPinWidget(type);
if (!pinSuccess) {
if (!mounted) return;
_showManualAddGuide(type);
return;
}
await Future<void>.delayed(const Duration(seconds: 2));
if (result == wp.PinWidgetResult.unsupported) {
_showManualAddGuide(type, isUnsupported: true);
return;
}
if (!mounted) return;
await notifier.loadInstalledWidgets();
if (result == wp.PinWidgetResult.failed) {
_showManualAddGuide(type, isUnsupported: false);
return;
}
final nowInstalled = ref.read(wp.widgetProvider).installedWidgets.contains(type);
if (!nowInstalled) {
_showManualAddGuide(type);
await Future<void>.delayed(const Duration(seconds: 2));
if (!mounted) return;
await notifier.loadInstalledWidgets();
final nowInstalled = ref.read(wp.widgetProvider).installedWidgets.contains(type);
if (!nowInstalled) {
_showManualAddGuide(type, isUnsupported: false);
}
} finally {
_isAdding = false;
}
}
void _showManualAddGuide(WidgetType type) {
void _showManualAddGuide(WidgetType type, {required bool isUnsupported}) {
final ext = AppTheme.ext(context);
final steps = PlatformHelper.isHarmonyOS
? [
@@ -248,6 +278,10 @@ class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
'4⃣ 选择「${type.title}」并添加',
];
final guideReason = isUnsupported
? '当前设备不支持快捷添加,请按以下步骤手动添加到桌面:'
: '快捷添加失败,请按以下步骤手动添加到桌面:';
showCupertinoDialog<void>(
context: context,
builder: (ctx) => CupertinoAlertDialog(
@@ -266,7 +300,7 @@ class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'当前设备不支持快捷添加,请按以下步骤手动添加到桌面:',
guideReason,
style: AppTypography.footnote.copyWith(
color: ext.textSecondary,
),
@@ -284,6 +318,14 @@ class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
],
),
actions: [
CupertinoDialogAction(
child: const Text('推送数据'),
onPressed: () {
Navigator.of(ctx).pop();
ref.read(wp.widgetProvider.notifier).pushDataToWidget(type);
AppToast.showInfo('已推送数据到${type.title}小部件');
},
),
CupertinoDialogAction(
isDefaultAction: true,
child: const Text('知道了'),
@@ -488,12 +530,14 @@ class _WidgetCard extends StatelessWidget {
required this.ext,
required this.type,
required this.isInstalled,
required this.isAdding,
required this.onAdd,
required this.onPin,
});
final AppThemeExtension ext;
final WidgetType type;
final bool isInstalled;
final bool isAdding;
final VoidCallback onAdd;
final VoidCallback onPin;
@@ -571,14 +615,16 @@ class _WidgetCard extends StatelessWidget {
minimumSize: Size.zero,
borderRadius: AppRadius.pillBorder,
color: ext.accent,
onPressed: onAdd,
child: Text(
'添加',
style: AppTypography.caption2.copyWith(
color: CupertinoColors.white,
fontWeight: FontWeight.w600,
),
),
onPressed: isAdding ? null : onAdd,
child: isAdding
? const CupertinoActivityIndicator(radius: 8)
: Text(
'添加',
style: AppTypography.caption2.copyWith(
color: CupertinoColors.white,
fontWeight: FontWeight.w600,
),
),
),
],
),
@@ -590,6 +636,8 @@ class _WidgetCard extends StatelessWidget {
_DeepLinkBadge(ext: ext, route: type.deepLinkRoute),
],
),
const SizedBox(height: AppSpacing.sm),
_WidgetDataPreview(ext: ext, type: type),
],
),
);
@@ -703,3 +751,149 @@ class _DeepLinkBadge extends StatelessWidget {
);
}
}
class _WidgetDataPreview extends StatefulWidget {
const _WidgetDataPreview({required this.ext, required this.type});
final AppThemeExtension ext;
final WidgetType type;
@override
State<_WidgetDataPreview> createState() => _WidgetDataPreviewState();
}
class _WidgetDataPreviewState extends State<_WidgetDataPreview> {
Map<String, dynamic> _data = {};
bool _loading = true;
@override
void initState() {
super.initState();
_loadPreview();
}
Future<void> _loadPreview() async {
try {
final data = await HomeWidgetService.instance.debugGetAllData();
if (mounted) {
setState(() {
_data = data;
_loading = false;
});
}
} catch (_) {
if (mounted) setState(() => _loading = false);
}
}
Future<void> _refreshData() async {
setState(() => _loading = true);
try {
await HomeWidgetService.instance.updateWidget(widget.type);
await _loadPreview();
} catch (_) {
if (mounted) setState(() => _loading = false);
}
}
String _getPreviewText() {
return switch (widget.type) {
WidgetType.dailySentence =>
'${_data['daily_sentence'] ?? '暂无数据'}',
WidgetType.readlater =>
'未读 ${_data['readlater_count'] ?? 0}',
WidgetType.dailyFortune =>
'${_data['fortune_text'] ?? '暂无数据'}',
WidgetType.countdown =>
'${_data['countdown_title'] ?? '暂无数据'}',
WidgetType.pomodoro =>
'剩余 ${_data['pomodoro_remaining'] ?? 0}s',
WidgetType.solarTerm =>
'${_data['solar_term_name'] ?? '暂无数据'}',
WidgetType.checkin =>
'连续 ${_data['checkin_days'] ?? 0}',
WidgetType.dailyWithCharacter =>
'${_data['daily_with_character_content'] ?? '暂无数据'}',
WidgetType.dailyCard =>
'日签卡片',
};
}
@override
Widget build(BuildContext context) {
final ext = widget.ext;
return Container(
padding: const EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: ext.bgSecondary.withValues(alpha: 0.5),
borderRadius: AppRadius.mdBorder,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
CupertinoIcons.eye_fill,
size: 12,
color: ext.textSecondary,
),
const SizedBox(width: 4),
Text(
'数据预览',
style: AppTypography.caption2.copyWith(
color: ext.textSecondary,
fontWeight: FontWeight.w600,
),
),
const Spacer(),
GestureDetector(
onTap: _loading ? null : _refreshData,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
CupertinoIcons.refresh,
size: 12,
color: ext.accent,
),
const SizedBox(width: 2),
Text(
'刷新',
style: AppTypography.caption2.copyWith(
color: ext.accent,
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
const SizedBox(height: AppSpacing.xs),
if (_loading)
const CupertinoActivityIndicator(radius: 6)
else
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.sm,
vertical: AppSpacing.xs,
),
decoration: BoxDecoration(
color: ext.bgCard.withValues(alpha: 0.6),
borderRadius: AppRadius.smBorder,
),
child: Text(
_getPreviewText(),
style: AppTypography.footnote.copyWith(
color: ext.textPrimary,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
}

View File

@@ -1,13 +1,14 @@
/// ============================================================
/// ============================================================
/// 闲言APP — 桌面小部件状态管理
/// 创建时间: 2026-05-19
/// 更新时间: 2026-05-19
/// 更新时间: 2026-05-24
/// 作用: 管理小部件安装状态、数据推送和交互
/// 上次更新: requestPinWidget返回bool修复Android/鸿蒙端添加小部件无反应
/// 上次更新: requestPinWidget返回PinWidgetResult枚举鸿蒙端直接返回unsupported
/// ============================================================
import 'dart:ui';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:home_widget/home_widget.dart';
import 'package:xianyan/core/utils/platform/platform_utils.dart' as pu;
@@ -16,6 +17,8 @@ import '../../../core/services/data/home_widget_service.dart';
import '../../../core/utils/logger.dart';
import '../models/widget_type.dart';
enum PinWidgetResult { success, unsupported, failed }
class WidgetState {
final Set<WidgetType> installedWidgets;
final bool isLoading;
@@ -90,30 +93,28 @@ class WidgetNotifier extends Notifier<WidgetState> {
Log.i('WidgetNotifier: 移除小部件 ${type.title}');
}
Future<bool> requestPinWidget(WidgetType type) async {
Future<PinWidgetResult> requestPinWidget(WidgetType type) async {
if (pu.isOhos) {
Log.w('WidgetNotifier: 鸿蒙端不支持快捷添加');
return PinWidgetResult.unsupported;
}
try {
await HomeWidget.requestPinWidget(
qualifiedAndroidName: type.qualifiedAndroidName,
androidName: type.androidProviderName,
);
// 鸿蒙端:本地包版本的 HomeWidget.requestPinWidget 有 ohosName 参数
// 官方SDK下不支持使用 dynamic 调用绕过编译检查
if (pu.isOhos) {
try {
const dynamic requestPin = HomeWidget.requestPinWidget;
// ignore: avoid_dynamic_calls
await requestPin(
qualifiedAndroidName: type.qualifiedAndroidName,
androidName: type.androidProviderName,
ohosName: type.ohosFormName,
);
} catch (_) {}
}
Log.i('WidgetNotifier: 请求固定小部件 ${type.title}');
return true;
return PinWidgetResult.success;
} on PlatformException catch (e) {
Log.e('WidgetNotifier: 请求固定小部件 PlatformException', e);
if (e.code == 'not_supported' || e.code == 'ACTION_APPWIDGET_BIND') {
return PinWidgetResult.unsupported;
}
return PinWidgetResult.failed;
} catch (e) {
Log.e('WidgetNotifier: 请求固定小部件失败', e);
return false;
return PinWidgetResult.failed;
}
}