This commit is contained in:
Developer
2026-03-31 05:42:47 +08:00
parent 888363785b
commit d6ac0ed1e4
18 changed files with 751 additions and 170 deletions

View File

@@ -131,3 +131,4 @@ All notable changes to this project will be documented in this file.
### 开发进度 ### 开发进度
- 🏗️ **HarmonyOS桌面小组件** - 开发中包含2x2布局、天气显示、诗句展示等功能 - 🏗️ **HarmonyOS桌面小组件** - 开发中包含2x2布局、天气显示、诗句展示等功能
- 优先级3 - 优先级3
getx 进入

View File

@@ -24,7 +24,7 @@ class FavoritesPage extends StatefulWidget {
class _FavoritesPageState extends State<FavoritesPage> class _FavoritesPageState extends State<FavoritesPage>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late TabController _tabController; late TabController _tabController;
final List<String> _categories = ['全部', '点赞', '笔记', '推送', '关注进度']; final List<String> _categories = ['全部', '点赞', '笔记', '推送', '每日一句'];
final TextEditingController _searchBarController = TextEditingController(); final TextEditingController _searchBarController = TextEditingController();
bool _isGridView = true; bool _isGridView = true;

View File

@@ -230,7 +230,7 @@ class CopyUtils {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'受隐私权限管理,写入剪切板需告知用户', '受隐私权限约束,频繁写入剪切板需告知用户',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),

View File

@@ -14,6 +14,7 @@ import '../../../utils/audio_manager.dart';
import 'home_part.dart'; import 'home_part.dart';
import 'home_components.dart'; import 'home_components.dart';
import 'home-load.dart'; import 'home-load.dart';
import '../profile/guide/tongji.dart';
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {
const HomePage({super.key}); const HomePage({super.key});
@@ -123,6 +124,15 @@ class _HomePageState extends State<HomePage>
final response = await PoetryApi.getRandomPoetry(); final response = await PoetryApi.getRandomPoetry();
if (mounted && response.data != null) { if (mounted && response.data != null) {
// 记录浏览统计
try {
await StatisticsManager().recordView();
await StatisticsManager().recordFirstUse();
await StatisticsManager().recordTotalView();
} catch (e) {
// 忽略错误
}
setState(() { setState(() {
_poetryData = response.data; _poetryData = response.data;
_keywordList = PoetryDataUtils.extractKeywords(response.data); _keywordList = PoetryDataUtils.extractKeywords(response.data);
@@ -160,6 +170,15 @@ class _HomePageState extends State<HomePage>
final poetryData = await offlineDataManager.getNextPoetry(); final poetryData = await offlineDataManager.getNextPoetry();
if (mounted && poetryData != null) { if (mounted && poetryData != null) {
// 记录浏览统计
try {
await StatisticsManager().recordView();
await StatisticsManager().recordFirstUse();
await StatisticsManager().recordTotalView();
} catch (e) {
// 忽略错误
}
setState(() { setState(() {
_poetryData = poetryData; _poetryData = poetryData;
_keywordList = PoetryDataUtils.extractKeywords(poetryData); _keywordList = PoetryDataUtils.extractKeywords(poetryData);
@@ -243,6 +262,15 @@ class _HomePageState extends State<HomePage>
final response = await PoetryApi.getPoetryById(poetryId); final response = await PoetryApi.getPoetryById(poetryId);
if (mounted && response.data != null) { if (mounted && response.data != null) {
// 记录浏览统计
try {
await StatisticsManager().recordView();
await StatisticsManager().recordFirstUse();
await StatisticsManager().recordTotalView();
} catch (e) {
// 忽略错误
}
setState(() { setState(() {
_poetryData = response.data; _poetryData = response.data;
_keywordList = PoetryDataUtils.extractKeywords(response.data); _keywordList = PoetryDataUtils.extractKeywords(response.data);
@@ -339,6 +367,10 @@ class _HomePageState extends State<HomePage>
if (_isLiked) { if (_isLiked) {
// 添加到点赞列表 // 添加到点赞列表
await HistoryController.addToLiked(_poetryData!.toJson()); await HistoryController.addToLiked(_poetryData!.toJson());
// 记录今日点赞
await StatisticsManager().recordTodayLike();
// 记录累计点赞
await StatisticsManager().recordTotalLike();
} else { } else {
// 从点赞列表移除 // 从点赞列表移除
await HistoryController.removeLikedPoetry(_poetryData!.id.toString()); await HistoryController.removeLikedPoetry(_poetryData!.id.toString());
@@ -529,6 +561,15 @@ class _HomePageState extends State<HomePage>
// 模拟分步加载过程 // 模拟分步加载过程
Future<void> _simulateSectionLoading(PoetryData newPoetryData) async { Future<void> _simulateSectionLoading(PoetryData newPoetryData) async {
// 记录浏览统计
try {
await StatisticsManager().recordView();
await StatisticsManager().recordFirstUse();
await StatisticsManager().recordTotalView();
} catch (e) {
// 忽略错误
}
// 1. 加载标题区域 // 1. 加载标题区域
setState(() { setState(() {
_sectionLoadingStates['title'] = false; _sectionLoadingStates['title'] = false;

View File

@@ -8,7 +8,6 @@ import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart'; import '../../../constants/app_constants.dart';
import '../../../utils/http/poetry_api.dart'; import '../../../utils/http/poetry_api.dart';
import '../../../utils/flutter_compatibility_fix.dart';
import '../../../utils/audio_manager.dart'; import '../../../utils/audio_manager.dart';
import 'home_components.dart'; import 'home_components.dart';

View File

@@ -19,12 +19,14 @@ class MainNavigation extends StatefulWidget {
class _MainNavigationState extends State<MainNavigation> { class _MainNavigationState extends State<MainNavigation> {
int _currentIndex = 0; int _currentIndex = 0;
final GlobalKey<State<ProfilePage>> _profileKey =
GlobalKey<State<ProfilePage>>();
final List<Widget> _pages = [ late final List<Widget> _pages = [
const HomePage(), const HomePage(),
const DiscoverPage(), const DiscoverPage(),
const FavoritesPage(), const FavoritesPage(),
const ProfilePage(), ProfilePage(key: _profileKey),
]; ];
final List<BottomNavigationBarItem> _bottomNavItems = [ final List<BottomNavigationBarItem> _bottomNavItems = [
@@ -73,6 +75,13 @@ class _MainNavigationState extends State<MainNavigation> {
setState(() { setState(() {
_currentIndex = index; _currentIndex = index;
}); });
// 切换到个人页面时刷新数据
if (index == 3) {
final profileState = _profileKey.currentState;
if (profileState != null && profileState.mounted) {
(profileState as dynamic).refreshData();
}
}
}, },
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
selectedItemColor: AppConstants.primaryColor, selectedItemColor: AppConstants.primaryColor,

View File

@@ -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();
}
},
);
}
}

View File

@@ -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);
}
}

View File

@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import '../../../constants/app_constants.dart'; import '../../../constants/app_constants.dart';
import '../../../controllers/shared_preferences_storage_controller.dart'; import '../../../controllers/shared_preferences_storage_controller.dart';
import '../guide/tongji.dart';
import 'level-jilu.dart'; import 'level-jilu.dart';
import 'flow-anim.dart'; import 'flow-anim.dart';
import 'distinguish.dart'; import 'distinguish.dart';
@@ -231,6 +232,13 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
if (!mounted) return; if (!mounted) return;
// 记录今日答题
try {
await StatisticsManager().recordTodayQuestion();
} catch (e) {
// 忽略错误
}
setState(() { setState(() {
if (result.success) { if (result.success) {
_isAnswerCorrect = result.isCorrect; _isAnswerCorrect = result.isCorrect;

View File

@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../../constants/app_constants.dart'; import '../../constants/app_constants.dart';
import '../../services/network_listener_service.dart'; import '../../services/network_listener_service.dart';
import 'guide/tongji.dart';
/// 时间: 2026-03-25 /// 时间: 2026-03-25
/// 功能: 个人信息卡片组件 /// 功能: 个人信息卡片组件
@@ -28,20 +29,42 @@ class PersonalCard extends StatefulWidget {
}); });
@override @override
State<PersonalCard> createState() => _PersonalCardState(); State<PersonalCard> createState() => PersonalCardState();
} }
class _PersonalCardState extends State<PersonalCard> { class PersonalCardState extends State<PersonalCard> {
late bool _isExpanded; late bool _isExpanded;
late String _currentTip; late String _currentTip;
bool _isOnline = true; bool _isOnline = true;
// 累计数据
int _totalViews = 0;
int _totalLikes = 0;
int _totalQuestions = 0;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_isExpanded = widget.isExpanded ?? false; _isExpanded = widget.isExpanded ?? false;
_currentTip = _getRandomTip(); _currentTip = _getRandomTip();
_loadOnlineStatus(); _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 { Future<void> _loadOnlineStatus() async {
@@ -378,15 +401,38 @@ class _PersonalCardState extends State<PersonalCard> {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
// 统计信息 // 累计统计卡片
Row( Container(
children: [ padding: const EdgeInsets.symmetric(
_buildStatItem('收藏', widget.userData['favorites'] ?? 0), horizontal: 12,
const SizedBox(width: 12), vertical: 10,
_buildStatItem('点赞', widget.userData['likes'] ?? 0), ),
const SizedBox(width: 12), decoration: BoxDecoration(
_buildStatItem('浏览', widget.userData['views'] ?? 0), color: Colors.white.withValues(alpha: 0.15),
], borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
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),
],
),
],
),
), ),
], ],
), ),

View File

@@ -22,10 +22,12 @@ import 'app-info.dart';
import 'level/poetry.dart'; import 'level/poetry.dart';
import 'guide/permission.dart'; import 'guide/permission.dart';
import 'guide/app-data.dart'; import 'guide/app-data.dart';
import 'guide/tongji.dart';
import 'theme/app-diy.dart'; import 'theme/app-diy.dart';
import 'expand/vote.dart'; import 'expand/vote.dart';
import 'expand/manu-script.dart'; import 'expand/manu-script.dart';
import 'components/bug_list_page.dart'; import 'components/bug_list_page.dart';
import 'components/pop-menu.dart';
class ProfilePage extends StatefulWidget { class ProfilePage extends StatefulWidget {
const ProfilePage({super.key}); const ProfilePage({super.key});
@@ -35,20 +37,35 @@ class ProfilePage extends StatefulWidget {
} }
class _ProfilePageState extends State<ProfilePage> class _ProfilePageState extends State<ProfilePage>
with TickerProviderStateMixin { with TickerProviderStateMixin, WidgetsBindingObserver {
late PageController _pageController; late PageController _pageController;
late TabController _tabController; late TabController _tabController;
int _currentPage = 1; // 默认显示第2页设置 int _currentPage = 1; // 默认显示第2页设置
bool _isCardExpanded = false; // 个人卡片展开状态 bool _isCardExpanded = false; // 个人卡片展开状态
bool _isStatsHidden = false; // 统计数据隐藏状态
double _startY = 0.0; double _startY = 0.0;
// 历史记录相关 // 历史记录相关
List<Map<String, dynamic>> _poetryHistory = []; List<Map<String, dynamic>> _poetryHistory = [];
// PersonalCard 的 Key用于调用其刷新方法
final GlobalKey<PersonalCardState> _personalCardKey =
GlobalKey<PersonalCardState>();
// 答题统计数据 // 答题统计数据
int _correctAnswers = 0; int _correctAnswers = 0;
int _todayQuestions = 0;
int _weekQuestions = 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 = { final Map<String, dynamic> _userData = {
'avatar': '👤', // 使用emoji代替网络图片 'avatar': '👤', // 使用emoji代替网络图片
@@ -67,19 +84,41 @@ class _ProfilePageState extends State<ProfilePage>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this);
_pageController = PageController(initialPage: 1); _pageController = PageController(initialPage: 1);
_tabController = TabController(length: 3, vsync: this); _tabController = TabController(length: 3, vsync: this);
_loadPoetryHistory(); _loadPoetryHistory();
_loadPoetryStatistics(); _loadPoetryStatistics();
_loadStatistics();
} }
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this);
_pageController.dispose(); _pageController.dispose();
_tabController.dispose(); _tabController.dispose();
super.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) { void _onPageChanged(int page) {
setState(() { setState(() {
_currentPage = page; _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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -222,6 +288,7 @@ class _ProfilePageState extends State<ProfilePage>
Widget _buildProfileHeader() { Widget _buildProfileHeader() {
// === 个人信息头部:可收起/张开的个人卡片 === // === 个人信息头部:可收起/张开的个人卡片 ===
return PersonalCard( return PersonalCard(
key: _personalCardKey,
userData: _userData, userData: _userData,
isExpanded: _isCardExpanded, isExpanded: _isCardExpanded,
onExpandChanged: (value) { onExpandChanged: (value) {
@@ -277,97 +344,130 @@ class _ProfilePageState extends State<ProfilePage>
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'统计*隐藏', '统计',
style: TextStyle( style: TextStyle(
color: AppConstants.primaryColor, color: AppConstants.primaryColor,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, 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), const SizedBox(height: 16),
// === 信息列表 === // === 信息列表 ===
_buildInfoItem('用户ID', '123123'), if (!_isStatsHidden) ...[
_buildInfoItem('注册时间', '2023-03-21'), _buildInfoItem('今日浏览', '$_todayViews'),
_buildInfoItem('会员等级', 'VIP会员'), _buildInfoItem('本周浏览', '$_weekViews'),
_buildInfoItem('积分余额', '2,580'), _buildInfoItem('天数', '$_useDays'),
_buildInfoItem('创作诗词', '156首'), _buildInfoItem('数据', _dataSize),
_buildInfoItem('获赞总数', '2,560'), _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), const SizedBox(height: 16),
// === 互动统计卡片 === // === 互动统计卡片 ===
Container( if (!_isStatsHidden)
width: double.infinity, Container(
padding: const EdgeInsets.all(20), width: double.infinity,
decoration: BoxDecoration( padding: const EdgeInsets.all(20),
color: Colors.white, decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), color: Colors.white,
boxShadow: [ borderRadius: BorderRadius.circular(16),
BoxShadow( boxShadow: [
color: Colors.black.withValues(alpha: 0.08), BoxShadow(
blurRadius: 10, color: Colors.black.withValues(alpha: 0.08),
offset: const Offset(0, 2), blurRadius: 10,
), offset: const Offset(0, 2),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.analytics_outlined,
color: AppConstants.primaryColor,
size: 20,
),
const SizedBox(width: 8),
Text(
'诗词挑战',
style: TextStyle(
color: AppConstants.primaryColor,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInteractionItem('今日答题', '$_todayQuestions'),
_buildInteractionItem('本周答题', '$_weekQuestions'),
_buildInteractionItem('答对次数', '$_correctAnswers'),
],
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const PoetryLevelPage(),
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('开始诗词答题', style: TextStyle(fontSize: 16)),
), ),
), ],
], ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.analytics_outlined,
color: AppConstants.primaryColor,
size: 20,
),
const SizedBox(width: 8),
Text(
'诗词挑战',
style: TextStyle(
color: AppConstants.primaryColor,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildInteractionItem('今日答题', '$_todayQuestions'),
_buildInteractionItem('本周答题', '$_weekQuestions'),
_buildInteractionItem('答对次数', '$_correctAnswers'),
],
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const PoetryLevelPage(),
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text(
'开始诗词答题',
style: TextStyle(fontSize: 16),
),
),
),
],
),
), ),
),
], ],
), ),
); );
@@ -577,7 +677,7 @@ class _ProfilePageState extends State<ProfilePage>
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'标签', label,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
color: Color(0xFF9E9E9E), // 使用具体颜色值代替Colors.grey[600] color: Color(0xFF9E9E9E), // 使用具体颜色值代替Colors.grey[600]
@@ -768,80 +868,22 @@ class _ProfilePageState extends State<ProfilePage>
} }
void _showMoreOptions() { void _showMoreOptions() {
showModalBottomSheet( PopMenu.show(
context: context, context,
backgroundColor: Colors.transparent, onRefresh: () => refreshData(),
builder: (context) => Container( onEdit: () => _showSnackBar('编辑资料'),
decoration: const BoxDecoration( onScanQr: () => _showSnackBar('扫描二维码'),
color: Colors.white, onDarkMode: () => _showSnackBar('夜间模式'),
borderRadius: BorderRadius.vertical(top: Radius.circular(20)), onSettings: () {
), Future.delayed(const Duration(milliseconds: 100), () {
child: Column( if (mounted && _pageController.hasClients) {
mainAxisSize: MainAxisSize.min, _pageController.animateToPage(
children: [ 1,
// 顶部拖拽条 duration: const Duration(milliseconds: 300),
Container( curve: Curves.easeInOut,
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, () {
// 延迟执行,确保底部弹窗完全关闭
Future.delayed(const Duration(milliseconds: 100), () {
if (mounted && _pageController.hasClients) {
_pageController.animateToPage(
1,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
});
}),
],
),
),
);
}
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();
}, },
); );
} }

View File

@@ -7,9 +7,13 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <audioplayers_linux/audioplayers_linux_plugin.h> #include <audioplayers_linux/audioplayers_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); 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);
} }

View File

@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_linux audioplayers_linux
url_launcher_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@@ -8,11 +8,13 @@ import Foundation
import audioplayers_darwin import audioplayers_darwin
import device_info_plus import device_info_plus
import path_provider_foundation import path_provider_foundation
import share_plus
import shared_preferences_foundation import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
} }

View File

@@ -121,6 +121,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "4.0.9" 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: crypto:
dependency: transitive dependency: transitive
description: description:
@@ -316,10 +324,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: mime name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.0.0" version: "1.0.6"
path: path:
dependency: transitive dependency: transitive
description: description:
@@ -410,6 +418,20 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.8" 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: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -553,6 +575,38 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.3.1" 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: uuid:
dependency: transitive dependency: transitive
description: description:

View File

@@ -47,7 +47,8 @@ dependencies:
url: https://gitcode.com/openharmony-sig/flutter_audioplayers.git url: https://gitcode.com/openharmony-sig/flutter_audioplayers.git
path: packages/audioplayers path: packages/audioplayers
share_plus:
path: packages/flutter_plus_plugins/packages/share_plus/share_plus
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -7,8 +7,14 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <audioplayers_windows/audioplayers_windows_plugin.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) { void RegisterPlugins(flutter::PluginRegistry* registry) {
AudioplayersWindowsPluginRegisterWithRegistrar( AudioplayersWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
SharePlusWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
} }

View File

@@ -4,6 +4,8 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_windows audioplayers_windows
share_plus
url_launcher_windows
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST