Files
xianyan/lib/core/layout/overview_dashboard.dart
Developer f281e465bb chore: v6.6.6 版本迭代更新
主要变更:
1. 重构"国学"相关模块为"经典名句",统一命名规范
2. 重命名"阅读报告"为"使用报告",调整相关文案与配置
3. 修复iOS模拟器图片缓存兼容问题,优化图表渲染逻辑
4. 新增设备活跃状态前端兜底判断,修复在线计数异常
5. 完善登录/注册流程,新增忘记密码路由与账户编辑提示
6. 优化文件传输与字体导入逻辑,废弃过时的bytes属性使用
7. 添加Spotlight全局快捷键支持,更新隐私权限与通知配置
8. 补充数据库迁移脚本与部署文档,修复后端接口兼容问题
9. 调整部分UI交互细节,优化内存占用与应用稳定性
2026-06-07 06:56:52 +08:00

400 lines
15 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
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-29
/// 更新时间: 2026-06-01
/// 作用: 宽屏分屏右侧面板的空状态页面,显示概览信息
/// 上次更新: 最近浏览接入RecentRouteService展示真实浏览记录
/// ============================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../theme/app_theme.dart';
import '../theme/app_spacing.dart';
import '../../shared/widgets/containers/glass_container.dart';
import '../../features/home/providers/home_provider.dart';
import '../../features/home/providers/favorite_provider.dart';
import '../../features/home/providers/likes_provider.dart';
import '../../features/auth/providers/auth_provider.dart';
import '../../features/mine/signin/providers/signin_provider.dart';
import '../../features/home/providers/tool_center_recent_provider.dart';
import '../../features/home/presentation/home_tool_center.dart';
import '../router/app_routes.dart';
import '../router/app_nav_extension.dart';
import '../services/navigation/recent_route_service.dart';
class OverviewDashboard extends ConsumerWidget {
const OverviewDashboard({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return SingleChildScrollView(
padding: const EdgeInsets.all(AppSpacing.md),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildGreeting(context),
const SizedBox(height: AppSpacing.lg),
_buildTodayRecommend(context, ref),
const SizedBox(height: AppSpacing.lg),
_buildQuickActions(context),
const SizedBox(height: AppSpacing.lg),
_buildRecentHistory(context, ref),
const SizedBox(height: AppSpacing.lg),
_buildStats(context, ref),
const SizedBox(height: AppSpacing.xxl),
],
),
);
}
Widget _buildGreeting(BuildContext context) {
final ext = AppTheme.ext(context);
final hour = DateTime.now().hour;
final greeting = switch (hour) {
>= 6 && < 12 => '早上好 ☀️',
>= 12 && < 14 => '中午好 🌤️',
>= 14 && < 18 => '下午好 🌅',
>= 18 && < 22 => '晚上好 🌙',
_ => '夜深了 🌛',
};
return GlassContainer(
depth: GlassDepth.elevated,
padding: const EdgeInsets.all(AppSpacing.lg),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
greeting,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: ext.textPrimary,
),
),
const SizedBox(height: AppSpacing.xs),
Text(
'选择左侧内容查看详情',
style: TextStyle(fontSize: 14, color: ext.textSecondary),
),
],
),
),
],
),
);
}
Widget _buildTodayRecommend(BuildContext context, WidgetRef ref) {
final ext = AppTheme.ext(context);
final homeState = ref.watch(homeProvider);
final recommends = homeState.dailySentences;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'✨ 今日推荐',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ext.textPrimary,
),
),
const SizedBox(height: AppSpacing.sm),
SizedBox(
height: 120,
child: recommends.isEmpty
? GlassContainer(
padding: const EdgeInsets.all(AppSpacing.md),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('📭', style: TextStyle(fontSize: 28)),
const SizedBox(height: AppSpacing.xs),
Text(
'暂无推荐内容',
style: TextStyle(fontSize: 13, color: ext.textHint),
),
],
),
),
)
: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: recommends.length,
separatorBuilder: (_, __) =>
const SizedBox(width: AppSpacing.sm),
itemBuilder: (context, index) {
final sentence = recommends[index];
return GlassContainer(
width: 180,
padding: const EdgeInsets.all(AppSpacing.md),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
sentence.text,
style: TextStyle(
fontSize: 13,
color: ext.textPrimary,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const Spacer(),
Text(
sentence.author != null
? '—— ${sentence.author}'
: '—— 佚名',
style: TextStyle(fontSize: 11, color: ext.textHint),
),
],
),
);
},
),
),
],
);
}
Widget _buildQuickActions(BuildContext context) {
final ext = AppTheme.ext(context);
final actions = <({String emoji, String label, String route})>[
(emoji: '🔍', label: '搜索', route: AppRoutes.search),
(emoji: '', label: '收藏', route: AppRoutes.favorites),
(emoji: '📖', label: '稍后读', route: AppRoutes.readLater),
(emoji: '🕐', label: '历史', route: AppRoutes.history),
(emoji: '', label: '签到', route: AppRoutes.signin),
(emoji: '📊', label: '使用报告', route: AppRoutes.readingReport),
(emoji: '🌤️', label: '每日推荐', route: AppRoutes.dailyCard),
(emoji: '⚙️', label: '设置', route: AppRoutes.generalSettings),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'🚀 快捷操作',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ext.textPrimary,
),
),
const SizedBox(height: AppSpacing.sm),
AnimationLimiter(
child: Wrap(
spacing: AppSpacing.sm,
runSpacing: AppSpacing.sm,
children: actions.asMap().entries.map((entry) {
return AnimationConfiguration.staggeredList(
position: entry.key,
duration: const Duration(milliseconds: 375),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: GestureDetector(
onTap: () => context.appPush(entry.value.route),
child: GlassContainer(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(entry.value.emoji,
style: const TextStyle(fontSize: 16)),
const SizedBox(width: AppSpacing.xs),
Text(
entry.value.label,
style: TextStyle(
fontSize: 13, color: ext.textPrimary),
),
],
),
),
),
),
),
);
}).toList(),
),
),
],
);
}
Widget _buildRecentHistory(BuildContext context, WidgetRef ref) {
final ext = AppTheme.ext(context);
ref.watch(toolCenterRecentProvider);
final recentRoutes = RecentRouteService.getRecentRoutes();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'🕐 最近浏览',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ext.textPrimary,
),
),
const SizedBox(height: AppSpacing.sm),
recentRoutes.isEmpty
? GlassContainer(
padding: const EdgeInsets.all(AppSpacing.md),
child: Center(
child: Column(
children: [
const Text('📭', style: TextStyle(fontSize: 32)),
const SizedBox(height: AppSpacing.sm),
Text(
'暂无浏览记录',
style: TextStyle(fontSize: 13, color: ext.textHint),
),
],
),
),
)
: AnimationLimiter(
child: Column(
children: recentRoutes.take(6).toList().asMap().entries.map((entry) {
final String route = entry.value;
final String iconText = ToolCenterIconMap.getIconText(route);
final String name = ToolCenterIconMap.getName(route);
return AnimationConfiguration.staggeredList(
position: entry.key,
duration: const Duration(milliseconds: 375),
child: SlideAnimation(
verticalOffset: 30.0,
child: FadeInAnimation(
child: Padding(
padding: const EdgeInsets.only(bottom: AppSpacing.xs),
child: GestureDetector(
onTap: () => context.appPush(route),
child: GlassContainer(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
child: Row(
children: [
Text(iconText, style: const TextStyle(fontSize: 18)),
const SizedBox(width: AppSpacing.sm),
Expanded(
child: Text(
name,
style: TextStyle(
fontSize: 14,
color: ext.textPrimary,
),
),
),
Icon(
CupertinoIcons.chevron_right,
size: 14,
color: ext.textHint,
),
],
),
),
),
),
),
),
);
}).toList(),
),
),
],
);
}
Widget _buildStats(BuildContext context, WidgetRef ref) {
final ext = AppTheme.ext(context);
final authState = ref.watch(authProvider);
final favoriteState = ref.watch(favoriteProvider);
final likesState = ref.watch(likesProvider);
final signinState = ref.watch(signinProvider);
final readCount = authState.user?.signinDays ?? 0;
final favCount = favoriteState.total;
final likeCount = likesState.total;
final streakDays = signinState.continuous;
final stats = [
('📖', '阅读', '$readCount'),
('', '收藏', '$favCount'),
('👍', '点赞', '$likeCount'),
('🔥', '连续', '$streakDays天'),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'📊 数据统计',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ext.textPrimary,
),
),
const SizedBox(height: AppSpacing.sm),
AnimationLimiter(
child: Row(
children: stats.asMap().entries.map((entry) {
final stat = entry.value;
return AnimationConfiguration.staggeredList(
position: entry.key,
duration: const Duration(milliseconds: 375),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: Expanded(
child: GlassContainer(
padding: const EdgeInsets.all(AppSpacing.sm),
margin: const EdgeInsets.symmetric(
horizontal: AppSpacing.xs / 2,
),
child: Column(
children: [
Text(stat.$1, style: const TextStyle(fontSize: 20)),
const SizedBox(height: 4),
Text(
stat.$3,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: ext.textPrimary,
),
),
Text(
stat.$2,
style: TextStyle(fontSize: 11, color: ext.textHint),
),
],
),
),
),
),
),
);
}).toList(),
),
),
],
);
}
}