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:
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user