Files
xianyan/lib/features/widget/presentation/widget_management_page.dart
Developer ae1df22732 feat: v6.10.3 多语言翻译补全 + 17项功能修复
- 引导页协议多语言支持(languageId传递)
- 登录页双书名号修复 + 注册页协议勾选
- 个人中心页面多语言(18个翻译键)
- 网络断开提示增加关闭/刷新按钮
- 了解我们:新增秋叶qy开发者 + ayk签名修改 + 贡献者精简 + 微风暴微信搜索
- iOS快捷按钮重复修复(删除Info.plist静态定义)
- 测试账号123456警告提示
- 扫码登录自动跳转(HTTP轮询+WebSocket双通道)
- 登录页老用户按钮改次要色
- Syncfusion图表崩溃修复(DeferredBuilder+animationDuration:0)
- macOS标题栏跟随软件夜间模式
- 平台兼容分发渠道弹窗
- 软件著作权图片+交叉水印
- 桌面小部件平台兼容说明默认收起
- iOS/macOS图标更新+名称确认为闲言
- 12个语言文件补全roleNative+7个分发渠道翻译字段
2026-06-02 04:50:32 +08:00

1025 lines
32 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// ============================================================
/// 闲言APP — 桌面小部件管理页面
/// 创建时间: 2026-05-19
/// 更新时间: 2026-05-27
/// 作用: 管理桌面小部件的安装、数据推送、主题和平台兼容说明
/// 上次更新: 增加开发中弹窗提示,支持不再提醒
/// ============================================================
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/storage/kv_storage.dart';
import '../../../core/router/app_nav_extension.dart';
import '../../../core/router/app_routes.dart';
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';
import '../../../shared/widgets/feedback/app_toast.dart';
import '../models/widget_type.dart';
import '../providers/widget_provider.dart' as wp;
class WidgetManagementPage extends ConsumerStatefulWidget {
const WidgetManagementPage({super.key});
@override
ConsumerState<WidgetManagementPage> createState() =>
_WidgetManagementPageState();
}
class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
bool _isAdding = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(wp.widgetProvider.notifier).loadInstalledWidgets();
_showDevDialogIfNeeded();
});
}
/// 开发中弹窗提示,支持"不再提醒"
void _showDevDialogIfNeeded() {
final dismissed = KvStorage.getBool('widget_dev_dismissed') ?? false;
if (dismissed) return;
showCupertinoDialog<void>(
context: context,
builder: (ctx) {
bool dontRemind = false;
return StatefulBuilder(
builder: (ctx, setDialogState) => CupertinoAlertDialog(
title: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
CupertinoIcons.hammer,
size: 20,
color: CupertinoColors.systemOrange.resolveFrom(ctx),
),
const SizedBox(width: 8),
const Text('Beta'),
],
),
content: Padding(
padding: const EdgeInsets.only(top: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'桌面小部件功能正在积极开发中,部分功能可能尚未完善或存在不稳定情况。\n\n'
'当前支持:基础小部件显示与数据推送\n'
'即将支持:更多小部件样式、交互操作、跨平台同步',
),
const SizedBox(height: 12),
GestureDetector(
onTap: () => setDialogState(() => dontRemind = !dontRemind),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
dontRemind
? CupertinoIcons.checkmark_square_fill
: CupertinoIcons.square,
size: 20,
color: dontRemind
? CupertinoColors.systemGreen.resolveFrom(ctx)
: CupertinoColors.systemGrey.resolveFrom(ctx),
),
const SizedBox(width: 8),
const Text('不再提醒', style: TextStyle(fontSize: 14)),
],
),
),
],
),
),
actions: [
CupertinoDialogAction(
child: const Text('查看实验功能'),
onPressed: () {
Navigator.pop(ctx);
context.appPush(AppRoutes.experimentalFeatures);
},
),
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () {
if (dontRemind) {
KvStorage.setBool('widget_dev_dismissed', true);
}
Navigator.pop(ctx);
},
child: const Text('我知道了'),
),
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
final ext = AppTheme.ext(context);
final wp.WidgetState widgetState = ref.watch(wp.widgetProvider);
final grouped = <int, List<WidgetType>>{};
for (final t in WidgetTypeX.all) {
grouped.putIfAbsent(t.priority, () => []).add(t);
}
return CupertinoPageScaffold(
backgroundColor: ext.bgPrimary,
child: ResponsiveMaxWidth(
maxWidth: 900,
child: SafeArea(
bottom: false,
child: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: Row(
children: [
GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: ext.bgSecondary,
borderRadius: AppRadius.mdBorder,
),
child: Icon(
CupertinoIcons.back,
size: 18,
color: ext.accent,
),
),
),
const SizedBox(width: AppSpacing.sm),
Text(
'桌面小部件',
style: AppTypography.title1.copyWith(
color: ext.textPrimary,
),
),
const Spacer(),
_ThemeToggle(ext: ext),
],
),
),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: _PlatformCompatibilityCard(ext: ext),
),
),
for (final entry in grouped.entries) ...[
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.fromLTRB(
AppSpacing.md,
AppSpacing.md,
AppSpacing.md,
AppSpacing.xs,
),
child: Row(
children: [
_PriorityDot(priority: entry.key),
const SizedBox(width: 6),
Text(
_priorityLabel(entry.key),
style: AppTypography.title3.copyWith(
color: ext.textPrimary,
),
),
],
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
final type = entry.value[index];
final isInstalled = widgetState.installedWidgets.contains(
type,
);
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.xs,
),
child: _WidgetCard(
ext: ext,
type: type,
isInstalled: isInstalled,
isAdding: _isAdding,
onAdd: () => _handleAddWidget(type),
onPin: () => _handlePinWidget(type),
),
);
}, childCount: entry.value.length),
),
],
if (widgetState.error != null)
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(AppSpacing.md),
child: Text(
'错误: ${widgetState.error}',
style: AppTypography.footnote.copyWith(
color: CupertinoColors.systemRed,
),
),
),
),
const SliverToBoxAdapter(child: SizedBox(height: 120)),
],
),
),
),
);
}
String _priorityLabel(int p) => switch (p) {
0 => '核心小部件',
1 => '推荐小部件',
2 => '实用小部件',
_ => '趣味小部件',
};
void _handleAddWidget(WidgetType type) async {
if (_isAdding) return;
_isAdding = true;
try {
final notifier = ref.read(wp.widgetProvider.notifier);
final wasInstalled = ref.read(wp.widgetProvider).installedWidgets.contains(type);
final result = await notifier.requestPinWidget(type);
if (!mounted) return;
if (result == wp.PinWidgetResult.unsupported) {
_showManualAddGuide(type, isUnsupported: true);
return;
}
if (result == wp.PinWidgetResult.failed) {
_showManualAddGuide(type, isUnsupported: false);
return;
}
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 {
if (_isAdding) return;
_isAdding = true;
try {
final notifier = ref.read(wp.widgetProvider.notifier);
final result = await notifier.requestPinWidget(type);
if (!mounted) return;
if (result == wp.PinWidgetResult.unsupported) {
_showManualAddGuide(type, isUnsupported: true);
return;
}
if (result == wp.PinWidgetResult.failed) {
_showManualAddGuide(type, isUnsupported: false);
return;
}
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, {required bool isUnsupported}) {
final ext = AppTheme.ext(context);
final steps = PlatformHelper.isHarmonyOS
? [
'1⃣ 长按桌面空白处',
'2⃣ 选择「服务卡片」',
'3⃣ 找到「闲言」',
'4⃣ 选择「${type.title}」并添加到桌面',
]
: PlatformHelper.isAndroid
? [
'1⃣ 长按桌面空白处',
'2⃣ 选择「小部件」',
'3⃣ 找到「闲言」',
'4⃣ 选择「${type.title}」拖动到桌面',
]
: [
'1⃣ 向右滑动到今日视图',
'2⃣ 滚动到底部点击「编辑」',
'3⃣ 找到「闲言」',
'4⃣ 选择「${type.title}」并添加',
];
final guideReason = isUnsupported
? '当前设备不支持快捷添加,请按以下步骤手动添加到桌面:'
: '快捷添加失败,请按以下步骤手动添加到桌面:';
showCupertinoDialog<void>(
context: context,
builder: (ctx) => CupertinoAlertDialog(
title: Padding(
padding: const EdgeInsets.only(bottom: AppSpacing.sm),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(CupertinoIcons.square_grid_2x2_fill, size: 20, color: ext.accent),
const SizedBox(width: AppSpacing.xs),
Text('添加「${type.title}'),
],
),
),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
guideReason,
style: AppTypography.footnote.copyWith(
color: ext.textSecondary,
),
),
const SizedBox(height: AppSpacing.md),
...steps.map((s) => Padding(
padding: const EdgeInsets.only(bottom: AppSpacing.xs),
child: Text(
s,
style: AppTypography.subhead.copyWith(
color: ext.textPrimary,
),
),
)),
],
),
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('知道了'),
onPressed: () => Navigator.of(ctx).pop(),
),
],
),
);
}
}
class _ThemeToggle extends StatelessWidget {
const _ThemeToggle({required this.ext});
final AppThemeExtension ext;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
final notifier = ProviderScope.containerOf(
context,
).read(wp.widgetProvider.notifier);
notifier.pushThemeToAllWidgets();
AppToast.showInfo('已推送当前主题到小部件');
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.sm,
vertical: 4,
),
decoration: BoxDecoration(
color: ext.bgSecondary,
borderRadius: AppRadius.pillBorder,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(CupertinoIcons.paintbrush_fill, size: 14, color: ext.accent),
const SizedBox(width: 4),
Text(
'同步主题',
style: AppTypography.caption1.copyWith(
color: ext.accent,
fontWeight: FontWeight.w600,
),
),
],
),
),
);
}
}
class _PriorityDot extends StatelessWidget {
const _PriorityDot({required this.priority});
final int priority;
@override
Widget build(BuildContext context) {
final color = switch (priority) {
0 => CupertinoColors.systemRed,
1 => CupertinoColors.systemOrange,
2 => CupertinoColors.systemBlue,
_ => CupertinoColors.systemGrey,
};
return Container(
width: 8,
height: 8,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
);
}
}
class _PlatformCompatibilityCard extends StatefulWidget {
const _PlatformCompatibilityCard({required this.ext});
final AppThemeExtension ext;
@override
State<_PlatformCompatibilityCard> createState() =>
_PlatformCompatibilityCardState();
}
class _PlatformCompatibilityCardState
extends State<_PlatformCompatibilityCard> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
final ext = widget.ext;
return GlassContainer(
depth: GlassDepth.elevated,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () => setState(() => _isExpanded = !_isExpanded),
behavior: HitTestBehavior.opaque,
child: Row(
children: [
Icon(
CupertinoIcons.info_circle_fill,
size: 18,
color: ext.accent,
),
const SizedBox(width: AppSpacing.sm),
Expanded(
child: Text(
'平台兼容说明',
style: AppTypography.subhead.copyWith(
color: ext.textPrimary,
fontWeight: FontWeight.w600,
),
),
),
AnimatedRotation(
turns: _isExpanded ? 0.0 : -0.25,
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
child: Icon(
CupertinoIcons.chevron_down,
size: 16,
color: ext.textHint,
),
),
],
),
),
AnimatedCrossFade(
firstChild: const SizedBox.shrink(),
secondChild: Padding(
padding: const EdgeInsets.only(top: AppSpacing.sm),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_PlatformRow(
ext: ext,
icon: '🤖',
name: 'Android',
desc: '功能不完整,原生侧存在通信问题',
),
const SizedBox(height: 4),
_PlatformRow(
ext: ext,
icon: '🍎',
name: 'iOS',
desc: 'WidgetKit + SwiftUI交互需 iOS 17+',
),
const SizedBox(height: 4),
_PlatformRow(
ext: ext,
icon: '🔴',
name: '鸿蒙',
desc: 'FormExtension + ArkUI能力受限系统限制刷新频率',
),
const SizedBox(height: AppSpacing.sm),
Container(
padding: const EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: ext.accent.withValues(alpha: 0.06),
borderRadius: AppRadius.smBorder,
),
child: Row(
children: [
Icon(
CupertinoIcons.lightbulb_fill,
size: 14,
color: ext.accent,
),
const SizedBox(width: AppSpacing.xs),
Expanded(
child: Text(
'点击「同步主题」可将当前深色/浅色模式推送到所有已安装小部件',
style: AppTypography.footnote.copyWith(
color: ext.textSecondary,
),
),
),
],
),
),
],
),
),
crossFadeState: _isExpanded
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
duration: const Duration(milliseconds: 250),
sizeCurve: Curves.easeInOut,
),
],
),
);
}
}
class _PlatformRow extends StatelessWidget {
const _PlatformRow({
required this.ext,
required this.icon,
required this.name,
required this.desc,
});
final AppThemeExtension ext;
final String icon;
final String name;
final String desc;
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(icon, style: const TextStyle(fontSize: 16)),
const SizedBox(width: AppSpacing.sm),
Expanded(
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: '$name ',
style: AppTypography.subhead.copyWith(
color: ext.textPrimary,
fontWeight: FontWeight.w600,
),
),
TextSpan(
text: desc,
style: AppTypography.footnote.copyWith(
color: ext.textSecondary,
),
),
],
),
),
),
],
);
}
}
class _WidgetCard extends StatelessWidget {
const _WidgetCard({
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;
@override
Widget build(BuildContext context) {
return GlassContainer(
depth: GlassDepth.elevated,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: ext.accent.withValues(alpha: 0.12),
borderRadius: AppRadius.mdBorder,
),
child: Icon(type.icon, size: 22, color: ext.accent),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
type.title,
style: AppTypography.subhead.copyWith(
color: ext.textPrimary,
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: AppSpacing.xs),
_PriorityBadge(priority: type.priority),
],
),
const SizedBox(height: 2),
Text(
type.subtitle,
style: AppTypography.footnote.copyWith(
color: ext.textSecondary,
),
),
],
),
),
const SizedBox(width: AppSpacing.sm),
if (isInstalled)
Container(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.sm,
vertical: 4,
),
decoration: BoxDecoration(
color: CupertinoColors.systemGreen.withValues(alpha: 0.15),
borderRadius: AppRadius.pillBorder,
),
child: Text(
'已安装',
style: AppTypography.caption2.copyWith(
color: CupertinoColors.systemGreen,
fontWeight: FontWeight.w600,
),
),
)
else
CupertinoButton(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.sm,
vertical: 4,
),
minimumSize: Size.zero,
borderRadius: AppRadius.pillBorder,
color: ext.accent,
onPressed: isAdding ? null : onAdd,
child: isAdding
? const CupertinoActivityIndicator(radius: 8)
: Text(
'添加',
style: AppTypography.caption2.copyWith(
color: CupertinoColors.white,
fontWeight: FontWeight.w600,
),
),
),
],
),
const SizedBox(height: AppSpacing.xs),
Row(
children: [
_PlatformBadges(ext: ext, type: type),
const Spacer(),
_DeepLinkBadge(ext: ext, route: type.deepLinkRoute),
],
),
const SizedBox(height: AppSpacing.sm),
_WidgetDataPreview(ext: ext, type: type),
],
),
);
}
}
class _PriorityBadge extends StatelessWidget {
const _PriorityBadge({required this.priority});
final int priority;
@override
Widget build(BuildContext context) {
final label = switch (priority) {
0 => 'P0',
1 => 'P1',
2 => 'P2',
_ => 'P3',
};
final color = switch (priority) {
0 => CupertinoColors.systemRed,
1 => CupertinoColors.systemOrange,
2 => CupertinoColors.systemBlue,
_ => CupertinoColors.systemGrey,
};
return Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.12),
borderRadius: AppRadius.xsBorder,
),
child: Text(
label,
style: AppTypography.caption2.copyWith(
color: color,
fontWeight: FontWeight.w700,
fontSize: 9,
),
),
);
}
}
class _PlatformBadges extends StatelessWidget {
const _PlatformBadges({required this.ext, required this.type});
final AppThemeExtension ext;
final WidgetType type;
@override
Widget build(BuildContext context) {
return Row(
children: [
const _Badge(label: 'Android', supported: true),
const SizedBox(width: 4),
const _Badge(label: 'iOS', supported: true),
const SizedBox(width: 4),
_Badge(label: '鸿蒙', supported: type.supportsOhos),
],
);
}
}
class _Badge extends StatelessWidget {
const _Badge({required this.label, required this.supported});
final String label;
final bool supported;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: supported
? CupertinoColors.systemGreen.withValues(alpha: 0.1)
: CupertinoColors.systemGrey.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(3),
),
child: Text(
supported ? label : '$label',
style: AppTypography.caption2.copyWith(
color: supported
? CupertinoColors.systemGreen
: CupertinoColors.systemGrey,
fontSize: 9,
fontWeight: FontWeight.w500,
),
),
);
}
}
class _DeepLinkBadge extends StatelessWidget {
const _DeepLinkBadge({required this.ext, required this.route});
final AppThemeExtension ext;
final String route;
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(CupertinoIcons.link, size: 10, color: ext.textSecondary),
const SizedBox(width: 2),
Text(
route,
style: AppTypography.caption2.copyWith(
color: ext.textSecondary,
fontSize: 9,
),
),
],
);
}
}
class _WidgetDataPreview extends ConsumerStatefulWidget {
const _WidgetDataPreview({required this.ext, required this.type});
final AppThemeExtension ext;
final WidgetType type;
@override
ConsumerState<_WidgetDataPreview> createState() => _WidgetDataPreviewState();
}
class _WidgetDataPreviewState extends ConsumerState<_WidgetDataPreview> {
Map<String, dynamic> _data = {};
bool _loading = true;
@override
void initState() {
super.initState();
_loadPreview();
}
Future<void> _loadPreview() async {
try {
final data = await ref.read(homeWidgetServiceProvider).debugGetAllData();
if (mounted) {
setState(() {
_data = data;
_loading = false;
});
}
} catch (_) {
if (mounted) setState(() => _loading = false);
}
}
Future<void> _refreshData() async {
setState(() => _loading = true);
try {
await ref.read(homeWidgetServiceProvider).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,
),
),
],
),
);
}
}