重构
This commit is contained in:
@@ -131,3 +131,4 @@ All notable changes to this project will be documented in this file.
|
|||||||
### 开发进度
|
### 开发进度
|
||||||
- 🏗️ **HarmonyOS桌面小组件** - 开发中,包含2x2布局、天气显示、诗句展示等功能
|
- 🏗️ **HarmonyOS桌面小组件** - 开发中,包含2x2布局、天气显示、诗句展示等功能
|
||||||
- 优先级:3
|
- 优先级:3
|
||||||
|
getx 进入
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 '../../../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;
|
||||||
|
|||||||
@@ -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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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();
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"))
|
||||||
}
|
}
|
||||||
|
|||||||
58
pubspec.lock
58
pubspec.lock
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user