重构
This commit is contained in:
@@ -131,3 +131,4 @@ All notable changes to this project will be documented in this file.
|
||||
### 开发进度
|
||||
- 🏗️ **HarmonyOS桌面小组件** - 开发中,包含2x2布局、天气显示、诗句展示等功能
|
||||
- 优先级:3
|
||||
getx 进入
|
||||
@@ -24,7 +24,7 @@ class FavoritesPage extends StatefulWidget {
|
||||
class _FavoritesPageState extends State<FavoritesPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
final List<String> _categories = ['全部', '点赞', '笔记', '推送', '关注进度'];
|
||||
final List<String> _categories = ['全部', '点赞', '笔记', '推送', '每日一句'];
|
||||
final TextEditingController _searchBarController = TextEditingController();
|
||||
bool _isGridView = true;
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ class CopyUtils {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'受隐私权限管理,写入剪切板需告知用户',
|
||||
'受隐私权限约束,频繁写入剪切板需告知用户',
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
@@ -14,6 +14,7 @@ import '../../../utils/audio_manager.dart';
|
||||
import 'home_part.dart';
|
||||
import 'home_components.dart';
|
||||
import 'home-load.dart';
|
||||
import '../profile/guide/tongji.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
@@ -123,6 +124,15 @@ class _HomePageState extends State<HomePage>
|
||||
final response = await PoetryApi.getRandomPoetry();
|
||||
|
||||
if (mounted && response.data != null) {
|
||||
// 记录浏览统计
|
||||
try {
|
||||
await StatisticsManager().recordView();
|
||||
await StatisticsManager().recordFirstUse();
|
||||
await StatisticsManager().recordTotalView();
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_poetryData = response.data;
|
||||
_keywordList = PoetryDataUtils.extractKeywords(response.data);
|
||||
@@ -160,6 +170,15 @@ class _HomePageState extends State<HomePage>
|
||||
final poetryData = await offlineDataManager.getNextPoetry();
|
||||
|
||||
if (mounted && poetryData != null) {
|
||||
// 记录浏览统计
|
||||
try {
|
||||
await StatisticsManager().recordView();
|
||||
await StatisticsManager().recordFirstUse();
|
||||
await StatisticsManager().recordTotalView();
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_poetryData = poetryData;
|
||||
_keywordList = PoetryDataUtils.extractKeywords(poetryData);
|
||||
@@ -243,6 +262,15 @@ class _HomePageState extends State<HomePage>
|
||||
final response = await PoetryApi.getPoetryById(poetryId);
|
||||
|
||||
if (mounted && response.data != null) {
|
||||
// 记录浏览统计
|
||||
try {
|
||||
await StatisticsManager().recordView();
|
||||
await StatisticsManager().recordFirstUse();
|
||||
await StatisticsManager().recordTotalView();
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_poetryData = response.data;
|
||||
_keywordList = PoetryDataUtils.extractKeywords(response.data);
|
||||
@@ -339,6 +367,10 @@ class _HomePageState extends State<HomePage>
|
||||
if (_isLiked) {
|
||||
// 添加到点赞列表
|
||||
await HistoryController.addToLiked(_poetryData!.toJson());
|
||||
// 记录今日点赞
|
||||
await StatisticsManager().recordTodayLike();
|
||||
// 记录累计点赞
|
||||
await StatisticsManager().recordTotalLike();
|
||||
} else {
|
||||
// 从点赞列表移除
|
||||
await HistoryController.removeLikedPoetry(_poetryData!.id.toString());
|
||||
@@ -529,6 +561,15 @@ class _HomePageState extends State<HomePage>
|
||||
|
||||
// 模拟分步加载过程
|
||||
Future<void> _simulateSectionLoading(PoetryData newPoetryData) async {
|
||||
// 记录浏览统计
|
||||
try {
|
||||
await StatisticsManager().recordView();
|
||||
await StatisticsManager().recordFirstUse();
|
||||
await StatisticsManager().recordTotalView();
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
// 1. 加载标题区域
|
||||
setState(() {
|
||||
_sectionLoadingStates['title'] = false;
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:flutter/services.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
import '../../../utils/http/poetry_api.dart';
|
||||
import '../../../utils/flutter_compatibility_fix.dart';
|
||||
import '../../../utils/audio_manager.dart';
|
||||
import 'home_components.dart';
|
||||
|
||||
|
||||
@@ -19,12 +19,14 @@ class MainNavigation extends StatefulWidget {
|
||||
|
||||
class _MainNavigationState extends State<MainNavigation> {
|
||||
int _currentIndex = 0;
|
||||
final GlobalKey<State<ProfilePage>> _profileKey =
|
||||
GlobalKey<State<ProfilePage>>();
|
||||
|
||||
final List<Widget> _pages = [
|
||||
late final List<Widget> _pages = [
|
||||
const HomePage(),
|
||||
const DiscoverPage(),
|
||||
const FavoritesPage(),
|
||||
const ProfilePage(),
|
||||
ProfilePage(key: _profileKey),
|
||||
];
|
||||
|
||||
final List<BottomNavigationBarItem> _bottomNavItems = [
|
||||
@@ -73,6 +75,13 @@ class _MainNavigationState extends State<MainNavigation> {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
});
|
||||
// 切换到个人页面时刷新数据
|
||||
if (index == 3) {
|
||||
final profileState = _profileKey.currentState;
|
||||
if (profileState != null && profileState.mounted) {
|
||||
(profileState as dynamic).refreshData();
|
||||
}
|
||||
}
|
||||
},
|
||||
type: BottomNavigationBarType.fixed,
|
||||
selectedItemColor: AppConstants.primaryColor,
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
|
||||
class PopMenu extends StatelessWidget {
|
||||
final VoidCallback? onRefresh;
|
||||
final VoidCallback? onEdit;
|
||||
final VoidCallback? onScanQr;
|
||||
final VoidCallback? onDarkMode;
|
||||
final VoidCallback? onSettings;
|
||||
|
||||
const PopMenu({
|
||||
super.key,
|
||||
this.onRefresh,
|
||||
this.onEdit,
|
||||
this.onScanQr,
|
||||
this.onDarkMode,
|
||||
this.onSettings,
|
||||
});
|
||||
|
||||
static Future<void> shareApp(BuildContext context) async {
|
||||
try {
|
||||
const String shareText = '诗词学习App - 一款优雅的诗词学习应用,包含丰富的诗词内容和答题功能';
|
||||
|
||||
final result = await Share.shareWithResult(shareText, subject: '诗词学习App');
|
||||
|
||||
if (result.status == ShareResultStatus.success) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('分享成功!')));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('该平台暂不支持分享功能')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void show(
|
||||
BuildContext context, {
|
||||
VoidCallback? onRefresh,
|
||||
VoidCallback? onEdit,
|
||||
VoidCallback? onScanQr,
|
||||
VoidCallback? onDarkMode,
|
||||
VoidCallback? onSettings,
|
||||
}) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => PopMenu(
|
||||
onRefresh: onRefresh,
|
||||
onEdit: onEdit,
|
||||
onScanQr: onScanQr,
|
||||
onDarkMode: onDarkMode,
|
||||
onSettings: onSettings,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 顶部拖拽条
|
||||
Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
margin: const EdgeInsets.only(top: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300]!,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
// 选项列表
|
||||
_buildBottomSheetItem(context, '刷新数据', Icons.refresh, onRefresh),
|
||||
_buildBottomSheetItem(context, '分享软件', Icons.share, () {
|
||||
shareApp(context);
|
||||
}),
|
||||
_buildBottomSheetItem(
|
||||
context,
|
||||
'扫描二维码',
|
||||
Icons.qr_code_scanner,
|
||||
onScanQr,
|
||||
),
|
||||
_buildBottomSheetItem(context, '夜间模式', Icons.dark_mode, onDarkMode),
|
||||
const SizedBox(height: 20),
|
||||
_buildBottomSheetItem(context, '设置', Icons.settings, onSettings),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBottomSheetItem(
|
||||
BuildContext context,
|
||||
String title,
|
||||
IconData icon,
|
||||
VoidCallback? onTap,
|
||||
) {
|
||||
return ListTile(
|
||||
leading: Icon(icon, color: AppConstants.primaryColor),
|
||||
title: Text(title),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
HapticFeedback.lightImpact();
|
||||
if (onTap != null) {
|
||||
onTap();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/// 时间: 2026-03-31
|
||||
/// 功能: 统计数据管理
|
||||
/// 介绍: 管理应用的各种统计数据,包括浏览次数、答题次数、点赞数等
|
||||
/// 最新变化: 新建文件
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class StatisticsManager {
|
||||
static const String _todayViewsKey = 'today_views';
|
||||
static const String _weekViewsKey = 'week_views';
|
||||
static const String _firstUseTimeKey = 'first_use_time';
|
||||
static const String _todayLikesKey = 'today_likes';
|
||||
static const String _todayQuestionsKey = 'today_questions';
|
||||
static const String _lastViewDateKey = 'last_view_date';
|
||||
static const String _lastWeekKey = 'last_week_start';
|
||||
static const String _lastQuestionDateKey = 'last_question_date';
|
||||
|
||||
static StatisticsManager? _instance;
|
||||
|
||||
StatisticsManager._internal();
|
||||
|
||||
factory StatisticsManager() {
|
||||
_instance ??= StatisticsManager._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
/// 记录一次浏览
|
||||
Future<void> recordView() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final now = DateTime.now();
|
||||
final today = _formatDate(now);
|
||||
final weekStart = _getWeekStart(now);
|
||||
|
||||
final lastViewDate = prefs.getString(_lastViewDateKey);
|
||||
final lastWeekStart = prefs.getString(_lastWeekKey);
|
||||
|
||||
if (lastViewDate != today) {
|
||||
await prefs.setInt(_todayViewsKey, 0);
|
||||
await prefs.setString(_lastViewDateKey, today);
|
||||
}
|
||||
|
||||
if (lastWeekStart != weekStart) {
|
||||
await prefs.setInt(_weekViewsKey, 0);
|
||||
await prefs.setString(_lastWeekKey, weekStart);
|
||||
}
|
||||
|
||||
final todayViews = prefs.getInt(_todayViewsKey) ?? 0;
|
||||
final weekViews = prefs.getInt(_weekViewsKey) ?? 0;
|
||||
|
||||
await prefs.setInt(_todayViewsKey, todayViews + 1);
|
||||
await prefs.setInt(_weekViewsKey, weekViews + 1);
|
||||
}
|
||||
|
||||
/// 获取今日浏览次数
|
||||
Future<int> getTodayViews() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final today = _formatDate(DateTime.now());
|
||||
final lastViewDate = prefs.getString(_lastViewDateKey);
|
||||
|
||||
if (lastViewDate != today) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return prefs.getInt(_todayViewsKey) ?? 0;
|
||||
}
|
||||
|
||||
/// 获取本周浏览次数
|
||||
Future<int> getWeekViews() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final now = DateTime.now();
|
||||
final weekStart = _getWeekStart(now);
|
||||
final lastWeekStart = prefs.getString(_lastWeekKey);
|
||||
|
||||
if (lastWeekStart != weekStart) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return prefs.getInt(_weekViewsKey) ?? 0;
|
||||
}
|
||||
|
||||
/// 记录首次使用时间
|
||||
Future<void> recordFirstUse() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (!prefs.containsKey(_firstUseTimeKey)) {
|
||||
await prefs.setString(_firstUseTimeKey, DateTime.now().toIso8601String());
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取首次使用时间
|
||||
Future<String> getFirstUseTime() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final timeStr = prefs.getString(_firstUseTimeKey);
|
||||
if (timeStr == null) {
|
||||
return '未记录';
|
||||
}
|
||||
final date = DateTime.parse(timeStr);
|
||||
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
/// 记录今日点赞
|
||||
Future<void> recordTodayLike() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final now = DateTime.now();
|
||||
final today = _formatDate(now);
|
||||
final lastLikeDate = prefs.getString('last_like_date');
|
||||
|
||||
if (lastLikeDate != today) {
|
||||
await prefs.setInt(_todayLikesKey, 0);
|
||||
await prefs.setString('last_like_date', today);
|
||||
}
|
||||
|
||||
final todayLikes = prefs.getInt(_todayLikesKey) ?? 0;
|
||||
await prefs.setInt(_todayLikesKey, todayLikes + 1);
|
||||
}
|
||||
|
||||
/// 获取今日点赞数
|
||||
Future<int> getTodayLikes() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final today = _formatDate(DateTime.now());
|
||||
final lastLikeDate = prefs.getString('last_like_date');
|
||||
|
||||
if (lastLikeDate != today) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return prefs.getInt(_todayLikesKey) ?? 0;
|
||||
}
|
||||
|
||||
/// 获取数据占用空间
|
||||
Future<String> getDataSize() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final keys = prefs.getKeys();
|
||||
int totalBytes = 0;
|
||||
|
||||
for (final key in keys) {
|
||||
final value = prefs.get(key);
|
||||
if (value != null) {
|
||||
totalBytes += value.toString().length * 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalBytes < 1024) {
|
||||
return '$totalBytes B';
|
||||
} else if (totalBytes < 1024 * 1024) {
|
||||
return '${(totalBytes / 1024).toStringAsFixed(1)} KB';
|
||||
} else {
|
||||
return '${(totalBytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
||||
}
|
||||
} catch (e) {
|
||||
return '计算失败';
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取累计浏览数
|
||||
Future<int> getTotalViews() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getInt('total_views') ?? 0;
|
||||
}
|
||||
|
||||
/// 记录累计浏览
|
||||
Future<void> recordTotalView() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final totalViews = prefs.getInt('total_views') ?? 0;
|
||||
await prefs.setInt('total_views', totalViews + 1);
|
||||
}
|
||||
|
||||
/// 获取累计点赞数
|
||||
Future<int> getTotalLikes() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getInt('total_likes') ?? 0;
|
||||
}
|
||||
|
||||
/// 记录累计点赞
|
||||
Future<void> recordTotalLike() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final totalLikes = prefs.getInt('total_likes') ?? 0;
|
||||
await prefs.setInt('total_likes', totalLikes + 1);
|
||||
}
|
||||
|
||||
/// 获取累计答题数
|
||||
Future<int> getTotalQuestions() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getInt('totalQuestions') ?? 0;
|
||||
}
|
||||
|
||||
/// 记录今日答题
|
||||
Future<void> recordTodayQuestion() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final now = DateTime.now();
|
||||
final today = _formatDate(now);
|
||||
final lastQuestionDate = prefs.getString(_lastQuestionDateKey);
|
||||
|
||||
if (lastQuestionDate != today) {
|
||||
await prefs.setInt(_todayQuestionsKey, 0);
|
||||
await prefs.setString(_lastQuestionDateKey, today);
|
||||
}
|
||||
|
||||
final todayQuestions = prefs.getInt(_todayQuestionsKey) ?? 0;
|
||||
await prefs.setInt(_todayQuestionsKey, todayQuestions + 1);
|
||||
}
|
||||
|
||||
/// 获取今日答题数
|
||||
Future<int> getTodayQuestions() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final today = _formatDate(DateTime.now());
|
||||
final lastQuestionDate = prefs.getString(_lastQuestionDateKey);
|
||||
|
||||
if (lastQuestionDate != today) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return prefs.getInt(_todayQuestionsKey) ?? 0;
|
||||
}
|
||||
|
||||
/// 获取使用天数
|
||||
Future<int> getUseDays() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final timeStr = prefs.getString(_firstUseTimeKey);
|
||||
if (timeStr == null) {
|
||||
return 1;
|
||||
}
|
||||
try {
|
||||
final firstUseDate = DateTime.parse(timeStr);
|
||||
final today = DateTime.now();
|
||||
final difference = today.difference(firstUseDate);
|
||||
return difference.inDays + 1;
|
||||
} catch (e) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// 格式化日期为 YYYY-MM-DD
|
||||
String _formatDate(DateTime date) {
|
||||
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
/// 获取本周开始日期(周一)
|
||||
String _getWeekStart(DateTime date) {
|
||||
final monday = date.subtract(Duration(days: date.weekday - 1));
|
||||
return _formatDate(monday);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../constants/app_constants.dart';
|
||||
import '../../../controllers/shared_preferences_storage_controller.dart';
|
||||
import '../guide/tongji.dart';
|
||||
import 'level-jilu.dart';
|
||||
import 'flow-anim.dart';
|
||||
import 'distinguish.dart';
|
||||
@@ -231,6 +232,13 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
// 记录今日答题
|
||||
try {
|
||||
await StatisticsManager().recordTodayQuestion();
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
setState(() {
|
||||
if (result.success) {
|
||||
_isAnswerCorrect = result.isCorrect;
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../constants/app_constants.dart';
|
||||
import '../../services/network_listener_service.dart';
|
||||
import 'guide/tongji.dart';
|
||||
|
||||
/// 时间: 2026-03-25
|
||||
/// 功能: 个人信息卡片组件
|
||||
@@ -28,20 +29,42 @@ class PersonalCard extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
State<PersonalCard> createState() => _PersonalCardState();
|
||||
State<PersonalCard> createState() => PersonalCardState();
|
||||
}
|
||||
|
||||
class _PersonalCardState extends State<PersonalCard> {
|
||||
class PersonalCardState extends State<PersonalCard> {
|
||||
late bool _isExpanded;
|
||||
late String _currentTip;
|
||||
bool _isOnline = true;
|
||||
|
||||
// 累计数据
|
||||
int _totalViews = 0;
|
||||
int _totalLikes = 0;
|
||||
int _totalQuestions = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_isExpanded = widget.isExpanded ?? false;
|
||||
_currentTip = _getRandomTip();
|
||||
_loadOnlineStatus();
|
||||
_loadTotalStats();
|
||||
}
|
||||
|
||||
Future<void> _loadTotalStats() async {
|
||||
final views = await StatisticsManager().getTotalViews();
|
||||
final likes = await StatisticsManager().getTotalLikes();
|
||||
final questions = await StatisticsManager().getTotalQuestions();
|
||||
setState(() {
|
||||
_totalViews = views;
|
||||
_totalLikes = likes;
|
||||
_totalQuestions = questions;
|
||||
});
|
||||
}
|
||||
|
||||
// 刷新数据(公共方法,供外部调用)
|
||||
Future<void> refreshData() async {
|
||||
await _loadTotalStats();
|
||||
}
|
||||
|
||||
Future<void> _loadOnlineStatus() async {
|
||||
@@ -378,19 +401,42 @@ class _PersonalCardState extends State<PersonalCard> {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// 统计信息
|
||||
Row(
|
||||
// 累计统计卡片
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildStatItem('收藏', widget.userData['favorites'] ?? 0),
|
||||
const SizedBox(width: 12),
|
||||
_buildStatItem('点赞', widget.userData['likes'] ?? 0),
|
||||
const SizedBox(width: 12),
|
||||
_buildStatItem('浏览', widget.userData['views'] ?? 0),
|
||||
Text(
|
||||
'累计',
|
||||
style: const TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildStatItem('浏览', _totalViews),
|
||||
const SizedBox(width: 16),
|
||||
_buildStatItem('点赞', _totalLikes),
|
||||
const SizedBox(width: 16),
|
||||
_buildStatItem('答题', _totalQuestions),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
// 头像在右边
|
||||
AnimatedContainer(
|
||||
|
||||
@@ -22,10 +22,12 @@ import 'app-info.dart';
|
||||
import 'level/poetry.dart';
|
||||
import 'guide/permission.dart';
|
||||
import 'guide/app-data.dart';
|
||||
import 'guide/tongji.dart';
|
||||
import 'theme/app-diy.dart';
|
||||
import 'expand/vote.dart';
|
||||
import 'expand/manu-script.dart';
|
||||
import 'components/bug_list_page.dart';
|
||||
import 'components/pop-menu.dart';
|
||||
|
||||
class ProfilePage extends StatefulWidget {
|
||||
const ProfilePage({super.key});
|
||||
@@ -35,20 +37,35 @@ class ProfilePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ProfilePageState extends State<ProfilePage>
|
||||
with TickerProviderStateMixin {
|
||||
with TickerProviderStateMixin, WidgetsBindingObserver {
|
||||
late PageController _pageController;
|
||||
late TabController _tabController;
|
||||
int _currentPage = 1; // 默认显示第2页(设置)
|
||||
bool _isCardExpanded = false; // 个人卡片展开状态
|
||||
bool _isStatsHidden = false; // 统计数据隐藏状态
|
||||
double _startY = 0.0;
|
||||
// 历史记录相关
|
||||
List<Map<String, dynamic>> _poetryHistory = [];
|
||||
|
||||
// PersonalCard 的 Key,用于调用其刷新方法
|
||||
final GlobalKey<PersonalCardState> _personalCardKey =
|
||||
GlobalKey<PersonalCardState>();
|
||||
|
||||
// 答题统计数据
|
||||
int _correctAnswers = 0;
|
||||
int _todayQuestions = 0;
|
||||
int _weekQuestions = 0;
|
||||
|
||||
// 统计数据
|
||||
int _todayViews = 0;
|
||||
int _weekViews = 0;
|
||||
String _firstUseTime = '未记录';
|
||||
int _useDays = 1;
|
||||
String _dataSize = '0 B';
|
||||
int _noteCount = 0;
|
||||
int _totalQuestions = 0;
|
||||
int _todayQuestions = 0;
|
||||
int _todayLikes = 0;
|
||||
|
||||
// 模拟用户数据
|
||||
final Map<String, dynamic> _userData = {
|
||||
'avatar': '👤', // 使用emoji代替网络图片
|
||||
@@ -67,19 +84,41 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_pageController = PageController(initialPage: 1);
|
||||
_tabController = TabController(length: 3, vsync: this);
|
||||
_loadPoetryHistory();
|
||||
_loadPoetryStatistics();
|
||||
_loadStatistics();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_pageController.dispose();
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
// 应用恢复时刷新数据
|
||||
refreshData();
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新所有数据(公共方法,供外部调用)
|
||||
Future<void> refreshData() async {
|
||||
await _loadPoetryStatistics();
|
||||
await _loadStatistics();
|
||||
// 同时刷新个人卡片的数据
|
||||
final personalCardState = _personalCardKey.currentState;
|
||||
if (personalCardState != null && personalCardState.mounted) {
|
||||
await personalCardState.refreshData();
|
||||
}
|
||||
}
|
||||
|
||||
void _onPageChanged(int page) {
|
||||
setState(() {
|
||||
_currentPage = page;
|
||||
@@ -146,6 +185,33 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
}
|
||||
}
|
||||
|
||||
// === 加载统计数据 ===
|
||||
Future<void> _loadStatistics() async {
|
||||
try {
|
||||
// 加载浏览统计
|
||||
_todayViews = await StatisticsManager().getTodayViews();
|
||||
_weekViews = await StatisticsManager().getWeekViews();
|
||||
_firstUseTime = await StatisticsManager().getFirstUseTime();
|
||||
_useDays = await StatisticsManager().getUseDays();
|
||||
_dataSize = await StatisticsManager().getDataSize();
|
||||
_todayLikes = await StatisticsManager().getTodayLikes();
|
||||
_todayQuestions = await StatisticsManager().getTodayQuestions();
|
||||
|
||||
// 加载笔记总数
|
||||
_noteCount = await HistoryController.getNotesCount();
|
||||
|
||||
// 加载累计答题数
|
||||
_totalQuestions = await SharedPreferencesStorageController.getInt(
|
||||
'totalQuestions',
|
||||
defaultValue: 0,
|
||||
);
|
||||
|
||||
setState(() {});
|
||||
} catch (e) {
|
||||
// 加载失败
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -222,6 +288,7 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
Widget _buildProfileHeader() {
|
||||
// === 个人信息头部:可收起/张开的个人卡片 ===
|
||||
return PersonalCard(
|
||||
key: _personalCardKey,
|
||||
userData: _userData,
|
||||
isExpanded: _isCardExpanded,
|
||||
onExpandChanged: (value) {
|
||||
@@ -277,28 +344,58 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'统计*隐藏',
|
||||
'统计',
|
||||
style: TextStyle(
|
||||
color: AppConstants.primaryColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
_isStatsHidden
|
||||
? Icons.visibility_off_outlined
|
||||
: Icons.visibility_outlined,
|
||||
color: Colors.grey[600],
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_isStatsHidden = !_isStatsHidden;
|
||||
});
|
||||
HapticFeedback.lightImpact();
|
||||
},
|
||||
tooltip: _isStatsHidden ? '显示数据' : '隐藏数据',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// === 信息列表 ===
|
||||
_buildInfoItem('用户ID', '123123'),
|
||||
_buildInfoItem('注册时间', '2023-03-21'),
|
||||
_buildInfoItem('会员等级', 'VIP会员'),
|
||||
_buildInfoItem('积分余额', '2,580'),
|
||||
_buildInfoItem('创作诗词', '156首'),
|
||||
_buildInfoItem('获赞总数', '2,560'),
|
||||
if (!_isStatsHidden) ...[
|
||||
_buildInfoItem('今日浏览', '$_todayViews 条'),
|
||||
_buildInfoItem('本周浏览', '$_weekViews 条'),
|
||||
_buildInfoItem('天数', '$_useDays 天'),
|
||||
_buildInfoItem('数据', _dataSize),
|
||||
_buildInfoItem('笔记', '$_noteCount 个'),
|
||||
_buildInfoItem('今日答题', '$_todayQuestions 题'),
|
||||
_buildInfoItem('今日点赞', '$_todayLikes 个'),
|
||||
] else
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Text(
|
||||
'数据已隐藏',
|
||||
style: TextStyle(color: Colors.grey[500], fontSize: 14),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// === 互动统计卡片 ===
|
||||
if (!_isStatsHidden)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(20),
|
||||
@@ -362,7 +459,10 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text('开始诗词答题', style: TextStyle(fontSize: 16)),
|
||||
child: const Text(
|
||||
'开始诗词答题',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -577,7 +677,7 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'标签',
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF9E9E9E), // 使用具体颜色值代替Colors.grey[600]
|
||||
@@ -768,51 +868,13 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
}
|
||||
|
||||
void _showMoreOptions() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 顶部拖拽条
|
||||
Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
margin: const EdgeInsets.only(top: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300]!,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
// 选项列表
|
||||
_buildBottomSheetItem(
|
||||
'分享主页',
|
||||
Icons.share,
|
||||
() => _showSnackBar('分享主页'),
|
||||
),
|
||||
_buildBottomSheetItem(
|
||||
'编辑资料',
|
||||
Icons.edit,
|
||||
() => _showSnackBar('编辑资料'),
|
||||
),
|
||||
_buildBottomSheetItem(
|
||||
'扫描二维码',
|
||||
Icons.qr_code_scanner,
|
||||
() => _showSnackBar('扫描二维码'),
|
||||
),
|
||||
_buildBottomSheetItem(
|
||||
'夜间模式',
|
||||
Icons.dark_mode,
|
||||
() => _showSnackBar('夜间模式'),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildBottomSheetItem('设置', Icons.settings, () {
|
||||
// 延迟执行,确保底部弹窗完全关闭
|
||||
PopMenu.show(
|
||||
context,
|
||||
onRefresh: () => refreshData(),
|
||||
onEdit: () => _showSnackBar('编辑资料'),
|
||||
onScanQr: () => _showSnackBar('扫描二维码'),
|
||||
onDarkMode: () => _showSnackBar('夜间模式'),
|
||||
onSettings: () {
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
if (mounted && _pageController.hasClients) {
|
||||
_pageController.animateToPage(
|
||||
@@ -822,26 +884,6 @@ class _ProfilePageState extends State<ProfilePage>
|
||||
);
|
||||
}
|
||||
});
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBottomSheetItem(
|
||||
String title,
|
||||
IconData icon,
|
||||
VoidCallback onTap,
|
||||
) {
|
||||
// === 底部弹窗选项项 ===
|
||||
return ListTile(
|
||||
leading: Icon(icon, color: AppConstants.primaryColor),
|
||||
title: Text(title),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
HapticFeedback.lightImpact();
|
||||
onTap();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_linux
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
@@ -8,11 +8,13 @@ import Foundation
|
||||
import audioplayers_darwin
|
||||
import device_info_plus
|
||||
import path_provider_foundation
|
||||
import share_plus
|
||||
import shared_preferences_foundation
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
}
|
||||
|
||||
58
pubspec.lock
58
pubspec.lock
@@ -121,6 +121,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.0.9"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.3.5+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -316,10 +324,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "1.0.6"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -410,6 +418,20 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "packages/flutter_plus_plugins/packages/share_plus/share_plus"
|
||||
relative: true
|
||||
source: path
|
||||
version: "7.2.0"
|
||||
share_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: "packages/flutter_plus_plugins/packages/share_plus/share_plus_platform_interface"
|
||||
relative: true
|
||||
source: path
|
||||
version: "3.3.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -553,6 +575,38 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -47,7 +47,8 @@ dependencies:
|
||||
url: https://gitcode.com/openharmony-sig/flutter_audioplayers.git
|
||||
path: packages/audioplayers
|
||||
|
||||
|
||||
share_plus:
|
||||
path: packages/flutter_plus_plugins/packages/share_plus/share_plus
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -7,8 +7,14 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_windows
|
||||
share_plus
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
Reference in New Issue
Block a user