This commit is contained in:
Developer
2026-04-03 03:26:06 +08:00
parent 3063deb34c
commit cba04235c8
49 changed files with 3955 additions and 1421 deletions

View File

@@ -97,9 +97,13 @@ class CategoryPage extends StatelessWidget {
) {
return ListView.separated(
padding: const EdgeInsets.symmetric(vertical: 8),
itemCount: data.keys.length,
itemCount: data.keys.length + 1,
separatorBuilder: (context, index) => const SizedBox(height: 8),
itemBuilder: (context, index) {
if (index == data.keys.length) {
return _buildEndIndicator(isDark, themeColor);
}
final category = data.keys.elementAt(index);
final items = data[category]!;
@@ -178,6 +182,49 @@ class CategoryPage extends StatelessWidget {
);
}
Widget _buildEndIndicator(bool isDark, Color themeColor) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
padding: const EdgeInsets.symmetric(vertical: 24),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 40,
height: 1,
color: isDark ? Colors.grey[700] : Colors.grey[300],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'到底了',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[500] : Colors.grey[400],
fontWeight: FontWeight.w500,
),
),
),
Container(
width: 40,
height: 1,
color: isDark ? Colors.grey[700] : Colors.grey[300],
),
],
),
const SizedBox(height: 8),
Icon(
Icons.expand_more,
size: 20,
color: isDark ? Colors.grey[600] : Colors.grey[400],
),
],
),
);
}
Widget _buildCategoryChip(
String label,
String categoryType,

View File

@@ -25,10 +25,12 @@ class DiscoverPage extends StatefulWidget {
}
class _DiscoverPageState extends State<DiscoverPage>
with SingleTickerProviderStateMixin {
with TickerProviderStateMixin {
late TabController _tabController;
final controller = Get.put(DiscoverController());
late ThemeController _themeController;
late AnimationController _tipsAnimationController;
late Animation<double> _tipsAnimation;
@override
void initState() {
@@ -40,6 +42,17 @@ class _DiscoverPageState extends State<DiscoverPage>
vsync: this,
);
_tipsAnimationController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
_tipsAnimation = CurvedAnimation(
parent: _tipsAnimationController,
curve: Curves.easeInOut,
);
_tipsAnimationController.value = 1.0;
_tabController.addListener(() {
setState(() {});
});
@@ -48,6 +61,7 @@ class _DiscoverPageState extends State<DiscoverPage>
@override
void dispose() {
_tabController.dispose();
_tipsAnimationController.dispose();
super.dispose();
}
@@ -130,43 +144,52 @@ class _DiscoverPageState extends State<DiscoverPage>
}
Widget _buildTopicChips(DiscoverController controller, bool isDark) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
color: isDark ? const Color(0xFF1E1E1E) : Colors.grey[50],
child: Row(
children: [
Expanded(
child: Text(
'💡 探索更多精彩内容,发现你感兴趣的诗词世界',
style: TextStyle(
color: _themeController.currentThemeColor,
fontSize: 14,
fontWeight: FontWeight.w400,
return SizeTransition(
sizeFactor: _tipsAnimation,
axisAlignment: -1.0,
child: FadeTransition(
opacity: _tipsAnimation,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
color: isDark ? const Color(0xFF1E1E1E) : Colors.grey[50],
child: Row(
children: [
Expanded(
child: Text(
'💡 探索更多精彩内容,发现你感兴趣的诗词世界',
style: TextStyle(
color: _themeController.currentThemeColor,
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
),
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () async {
_tipsAnimationController.reverse();
await Future.delayed(const Duration(milliseconds: 300));
controller.toggleTips();
},
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: isDark
? Colors.grey[800]
: Colors.grey.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
size: 16,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
),
],
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
controller.toggleTips();
},
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: isDark
? Colors.grey[800]
: Colors.grey.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
size: 16,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
),
],
),
),
);
}
@@ -430,7 +453,7 @@ class _DiscoverPageState extends State<DiscoverPage>
),
const SizedBox(height: 8),
Text(
'展示诗词的浏览量和点赞数排行,包括总榜、日榜、月榜三种类型。帮助您发现最受欢迎的诗词作品',
'统计开源:来自本软件各个平台的数据,安卓 鸿蒙 web 小程序等平台',
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[600],

View File

@@ -30,7 +30,7 @@ class _CarePageState extends State<CarePage> {
backgroundColor: isDark ? const Color(0xFF121212) : Colors.white,
appBar: AppBar(
title: Text(
'关怀',
'关怀 Beta',
style: TextStyle(
color: isDark ? Colors.white : primaryColor,
fontWeight: FontWeight.bold,
@@ -183,27 +183,31 @@ class _CarePageState extends State<CarePage> {
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: _buildUserTypeButton(
'儿童',
_careController.userType == '儿童',
isDark,
primaryColor,
Obx(() {
final themeController = Get.find<ThemeController>();
final currentPrimaryColor = themeController.currentThemeColor;
return Row(
children: [
Expanded(
child: _buildUserTypeButton(
'儿童',
_careController.userType == '儿童',
isDark,
currentPrimaryColor,
),
),
),
const SizedBox(width: 16),
Expanded(
child: _buildUserTypeButton(
'学生',
_careController.userType == '学生',
isDark,
primaryColor,
const SizedBox(width: 16),
Expanded(
child: _buildUserTypeButton(
'学生',
_careController.userType == '学生',
isDark,
currentPrimaryColor,
),
),
),
],
),
],
);
}),
const SizedBox(height: 16),
Container(
width: double.infinity,

View File

@@ -10,6 +10,7 @@ import '../../../services/get/theme_controller.dart';
import '../../../services/get/care_controller.dart';
import '../../../constants/app_constants.dart';
import '../home_part.dart';
import '../components/skeleton_widgets.dart';
import 'care_widgets.dart';
import 'pinyin_helper.dart';
@@ -75,15 +76,18 @@ class _CarePoetryPageState extends State<CarePoetryPage>
final isDark = themeController.isDarkModeRx.value;
final primaryColor = themeController.currentThemeColor;
final poetryData = homeController.poetryData.value;
final isLoadingNext = homeController.isLoadingNext.value;
// 当诗词数据变化时,重新播放动画
if (!isLoadingNext) {
_animationController.reset();
_animationController.forward();
}
if (poetryData == null) {
return const Center(child: CircularProgressIndicator());
}
// 当诗词数据变化时,重新播放动画
_animationController.reset();
_animationController.forward();
return Scaffold(
backgroundColor: isDark ? const Color(0xFF121212) : Colors.grey[50],
body: SafeArea(
@@ -144,7 +148,12 @@ class _CarePoetryPageState extends State<CarePoetryPage>
const SizedBox(height: 16),
// 出处
if (careController.selectedOptions.contains('出处'))
_buildPoetrySource(poetryData, isDark, primaryColor),
_buildPoetrySource(
poetryData,
isDark,
primaryColor,
isLoadingNext,
),
if (careController.selectedOptions.contains('出处'))
const SizedBox(height: 24),
if (!careController.selectedOptions.contains('问候语') &&
@@ -152,7 +161,12 @@ class _CarePoetryPageState extends State<CarePoetryPage>
const SizedBox(height: 24),
// 诗词标题
if (careController.selectedOptions.contains('诗词'))
_buildPoetryTitle(poetryData, isDark, primaryColor),
_buildPoetryTitle(
poetryData,
isDark,
primaryColor,
isLoadingNext,
),
const SizedBox(height: 24),
@@ -170,6 +184,7 @@ class _CarePoetryPageState extends State<CarePoetryPage>
poetryData,
isDark,
primaryColor,
isLoadingNext,
),
),
@@ -186,13 +201,23 @@ class _CarePoetryPageState extends State<CarePoetryPage>
// 译文
if (careController.selectedOptions.contains('译文'))
_buildPoetryTranslation(poetryData, isDark, primaryColor),
_buildPoetryTranslation(
poetryData,
isDark,
primaryColor,
isLoadingNext,
),
const SizedBox(height: 24),
// 更多
if (careController.selectedOptions.contains('更多'))
_buildPoetryMore(poetryData, isDark, primaryColor),
_buildPoetryMore(
poetryData,
isDark,
primaryColor,
isLoadingNext,
),
const SizedBox(height: 48),
],
@@ -209,22 +234,48 @@ class _CarePoetryPageState extends State<CarePoetryPage>
dynamic poetryData,
bool isDark,
Color primaryColor,
bool isLoading,
) {
final baseColor = isDark ? const Color(0xFF3A3A3A) : Colors.grey[300];
final highlightColor = isDark ? const Color(0xFF4A4A4A) : Colors.grey[100];
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.grey[100],
borderRadius: BorderRadius.circular(12),
),
child: Text(
poetryData.name ?? '未知标题',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: primaryColor,
),
textAlign: TextAlign.center,
),
child: isLoading
? Center(
child: Column(
children: [
SkeletonContainer(
width: 180,
height: 32,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 8),
SkeletonContainer(
width: 120,
height: 20,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
],
),
)
: Text(
poetryData.name ?? '未知标题',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: primaryColor,
),
textAlign: TextAlign.center,
),
);
}
@@ -232,6 +283,7 @@ class _CarePoetryPageState extends State<CarePoetryPage>
dynamic poetryData,
bool isDark,
Color primaryColor,
bool isLoading,
) {
final themeController = Get.find<ThemeController>();
final careController = Get.find<CareController>();
@@ -239,6 +291,8 @@ class _CarePoetryPageState extends State<CarePoetryPage>
final content = poetryData.name ?? '';
final lines = content.split('\n');
final showPinyin = careController.pinyinEnabled;
final baseColor = isDark ? const Color(0xFF3A3A3A) : Colors.grey[300];
final highlightColor = isDark ? const Color(0xFF4A4A4A) : Colors.grey[100];
return Container(
padding: const EdgeInsets.all(16),
@@ -248,95 +302,128 @@ class _CarePoetryPageState extends State<CarePoetryPage>
: accentColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: lines.asMap().entries.map<Widget>((entry) {
int index = entry.key;
String line = entry.value;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
child: isLoading
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (showPinyin && line.trim().isNotEmpty)
Wrap(
alignment: WrapAlignment.center,
children: PinyinHelper.convertToCharPinyinList(line)
.asMap()
.entries
.map<Widget>((charEntry) {
int charIndex = charEntry.key;
var item = charEntry.value;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: AnimatedOpacity(
opacity: 1.0,
duration: Duration(
milliseconds: 300 + charIndex * 100,
),
curve: Curves.easeOut,
child: Column(
children: [
Text(
item['pinyin'] ?? '',
style: TextStyle(
fontSize: 12,
color: isDark
? Colors.grey[400]
: Colors.grey[600],
height: 1.2,
),
textAlign: TextAlign.center,
),
Text(
item['char'] ?? '',
style: TextStyle(
fontSize: 20,
color: primaryColor,
height: 1.6,
),
textAlign: TextAlign.center,
),
],
),
),
);
})
.toList(),
),
if (!showPinyin || line.trim().isEmpty)
Wrap(
alignment: WrapAlignment.center,
children: line.split('').asMap().entries.map<Widget>((
charEntry,
) {
int charIndex = charEntry.key;
String char = charEntry.value;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: AnimatedOpacity(
opacity: 1.0,
duration: Duration(
milliseconds: 300 + charIndex * 100,
),
curve: Curves.easeOut,
child: Text(
char,
style: TextStyle(
fontSize: 20,
color: primaryColor,
height: 1.6,
),
textAlign: TextAlign.center,
),
),
);
}).toList(),
),
SkeletonContainer(
width: double.infinity,
height: 28,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 12),
SkeletonContainer(
width: double.infinity,
height: 28,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 12),
SkeletonContainer(
width: 200,
height: 28,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: lines.asMap().entries.map<Widget>((entry) {
int index = entry.key;
String line = entry.value;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
children: [
if (showPinyin && line.trim().isNotEmpty)
Wrap(
alignment: WrapAlignment.center,
children: PinyinHelper.convertToCharPinyinList(line)
.asMap()
.entries
.map<Widget>((charEntry) {
int charIndex = charEntry.key;
var item = charEntry.value;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 4,
),
child: AnimatedOpacity(
opacity: 1.0,
duration: Duration(
milliseconds: 300 + charIndex * 100,
),
curve: Curves.easeOut,
child: Column(
children: [
Text(
item['pinyin'] ?? '',
style: TextStyle(
fontSize: 12,
color: isDark
? Colors.grey[400]
: Colors.grey[600],
height: 1.2,
),
textAlign: TextAlign.center,
),
Text(
item['char'] ?? '',
style: TextStyle(
fontSize: 20,
color: primaryColor,
height: 1.6,
),
textAlign: TextAlign.center,
),
],
),
),
);
})
.toList(),
),
if (!showPinyin || line.trim().isEmpty)
Wrap(
alignment: WrapAlignment.center,
children: line.split('').asMap().entries.map<Widget>((
charEntry,
) {
int charIndex = charEntry.key;
String char = charEntry.value;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 4,
),
child: AnimatedOpacity(
opacity: 1.0,
duration: Duration(
milliseconds: 300 + charIndex * 100,
),
curve: Curves.easeOut,
child: Text(
char,
style: TextStyle(
fontSize: 20,
color: primaryColor,
height: 1.6,
),
textAlign: TextAlign.center,
),
),
);
}).toList(),
),
],
),
);
}).toList(),
),
);
}).toList(),
),
);
}
@@ -344,8 +431,11 @@ class _CarePoetryPageState extends State<CarePoetryPage>
dynamic poetryData,
bool isDark,
Color primaryColor,
bool isLoading,
) {
final url = poetryData.url ?? '未知作者';
final baseColor = isDark ? const Color(0xFF3A3A3A) : Colors.grey[300];
final highlightColor = isDark ? const Color(0xFF4A4A4A) : Colors.grey[100];
return Container(
padding: const EdgeInsets.all(16),
@@ -353,15 +443,25 @@ class _CarePoetryPageState extends State<CarePoetryPage>
color: isDark ? const Color(0xFF2A2A2A) : Colors.grey[100],
borderRadius: BorderRadius.circular(12),
),
child: Text(
url,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: primaryColor,
),
textAlign: TextAlign.center,
),
child: isLoading
? Center(
child: SkeletonContainer(
width: 200,
height: 24,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
)
: Text(
url,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: primaryColor,
),
textAlign: TextAlign.center,
),
);
}
@@ -369,8 +469,11 @@ class _CarePoetryPageState extends State<CarePoetryPage>
dynamic poetryData,
bool isDark,
Color primaryColor,
bool isLoading,
) {
final translation = poetryData.introduce ?? '暂无译文';
final baseColor = isDark ? const Color(0xFF3A3A3A) : Colors.grey[300];
final highlightColor = isDark ? const Color(0xFF4A4A4A) : Colors.grey[100];
return Container(
padding: const EdgeInsets.all(16),
@@ -378,46 +481,118 @@ class _CarePoetryPageState extends State<CarePoetryPage>
color: isDark ? const Color(0xFF2A2A2A) : Colors.grey[100],
borderRadius: BorderRadius.circular(12),
),
child: Text(
translation,
style: TextStyle(
fontSize: 16,
color: isDark ? Colors.grey[300] : Colors.grey[700],
height: 1.6,
),
textAlign: TextAlign.center,
),
child: isLoading
? Column(
children: [
SkeletonContainer(
width: 100,
height: 20,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 12),
SkeletonContainer(
width: double.infinity,
height: 16,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 8),
SkeletonContainer(
width: double.infinity,
height: 16,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 8),
SkeletonContainer(
width: 200,
height: 16,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
],
)
: Text(
translation,
style: TextStyle(
fontSize: 16,
color: isDark ? Colors.grey[300] : Colors.grey[700],
height: 1.6,
),
textAlign: TextAlign.center,
),
);
}
Widget _buildPoetryMore(dynamic poetryData, bool isDark, Color primaryColor) {
Widget _buildPoetryMore(
dynamic poetryData,
bool isDark,
Color primaryColor,
bool isLoading,
) {
final baseColor = isDark ? const Color(0xFF3A3A3A) : Colors.grey[300];
final highlightColor = isDark ? const Color(0xFF4A4A4A) : Colors.grey[100];
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.grey[100],
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Text(
'更多信息',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: primaryColor,
child: isLoading
? Column(
children: [
SkeletonContainer(
width: 120,
height: 22,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 12),
SkeletonContainer(
width: double.infinity,
height: 14,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 8),
SkeletonContainer(
width: 250,
height: 14,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
],
)
: Column(
children: [
Text(
'更多信息',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: primaryColor,
),
),
const SizedBox(height: 12),
Text(
'诗词解析、背景知识等内容将在此显示',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[300] : Colors.grey[700],
),
textAlign: TextAlign.center,
),
],
),
),
const SizedBox(height: 12),
Text(
'诗词解析、背景知识等内容将在此显示',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[300] : Colors.grey[700],
),
textAlign: TextAlign.center,
),
],
),
);
}

View File

@@ -61,30 +61,30 @@ class CareModeToggle extends StatelessWidget {
final themeController = Get.find<ThemeController>();
final primaryColor = themeController.currentThemeColor;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
margin: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(isDark ? 40 : 20),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(Icons.people, color: primaryColor, size: 20),
const SizedBox(width: 12),
GestureDetector(
onTap: () => Get.to(() => const CarePage()),
child: Text(
return GestureDetector(
onTap: () => Get.to(() => const CarePage()),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
margin: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(isDark ? 40 : 20),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(Icons.people, color: primaryColor, size: 20),
const SizedBox(width: 12),
Text(
'关怀',
style: TextStyle(
fontSize: 16,
@@ -92,16 +92,21 @@ class CareModeToggle extends StatelessWidget {
color: isDark ? Colors.white : Colors.black,
),
),
],
),
GestureDetector(
onTap: onToggle,
child: Switch(
value: isEnabled,
onChanged: (_) => onToggle(),
activeColor: primaryColor,
inactiveTrackColor: isDark
? Colors.grey[700]
: Colors.grey[300],
),
],
),
Switch(
value: isEnabled,
onChanged: (_) => onToggle(),
activeColor: primaryColor,
inactiveTrackColor: isDark ? Colors.grey[700] : Colors.grey[300],
),
],
),
],
),
),
);
}

View File

@@ -25,31 +25,24 @@ class HomePage extends StatefulWidget {
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
class _HomePageState extends State<HomePage> {
final GlobalKey repaintKey = GlobalKey();
final SecondaryButtonsManager _secondaryButtonsManager =
SecondaryButtonsManager();
final FloatingButtonsVisibilityManager _floatingButtonsVisibilityManager =
FloatingButtonsVisibilityManager();
final CareController _careController = Get.put(CareController());
late AnimationController _animationController;
@override
void initState() {
super.initState();
_secondaryButtonsManager.init();
_floatingButtonsVisibilityManager.init();
_animationController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
}
@override
void dispose() {
_floatingButtonsVisibilityManager.dispose();
_animationController.dispose();
super.dispose();
}
@@ -63,35 +56,14 @@ class _HomePageState extends State<HomePage>
return Obx(() {
final isDark = themeController.isDarkModeRx.value;
// 触发动画
if (_animationController.status != AnimationStatus.forward) {
_animationController.forward(from: 0);
}
// 关怀模式开启时显示关怀页面,添加过渡动画
// 关怀模式开启时显示关怀页面
if (careController.isCareModeEnabled) {
return FadeTransition(
opacity: Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
),
),
child: const CarePoetryPage(),
);
return const CarePoetryPage();
}
return FadeTransition(
opacity: Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
),
),
child: Scaffold(
backgroundColor: isDark ? const Color(0xFF121212) : Colors.grey[50],
body: SafeArea(child: _buildBody(controller, isDark)),
),
return Scaffold(
backgroundColor: isDark ? const Color(0xFF121212) : Colors.grey[50],
body: SafeArea(child: _buildBody(controller, isDark)),
);
});
}
@@ -168,10 +140,16 @@ class _HomePageState extends State<HomePage>
top: 60,
left: 16,
right: 16,
child: CareModeToggle(
isEnabled: _careController.isCareModeEnabled,
onToggle: _careController.toggleCareMode,
isDark: isDark,
child: GestureDetector(
onTap: () => _careController.resetAutoHideTimer(),
child: CareModeToggle(
isEnabled: _careController.isCareModeEnabled,
onToggle: () {
_careController.resetAutoHideTimer();
_careController.toggleCareMode();
},
isDark: isDark,
),
),
)
: const SizedBox.shrink(),

View File

@@ -11,6 +11,7 @@ import '../../../services/get/theme_controller.dart';
import '../../../utils/http/poetry_api.dart';
import '../../../utils/audio_manager.dart';
import 'set/home_components.dart';
import 'components/skeleton_widgets.dart';
/// 诗词卡片组件 - 优化版本,防止拉伸和处理文本溢出
class PoetryCard extends StatefulWidget {
@@ -130,65 +131,61 @@ class _PoetryCardState extends State<PoetryCard> {
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildTimeBar(isDark, themeColor),
Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
_buildTitleSection(isDark, themeColor),
if (widget.poetryData.drtime.isNotEmpty) ...[
_buildContentSection(context, isDark, themeColor),
const SizedBox(height: 3),
],
Align(
alignment: Alignment.centerRight,
child: Container(
margin: EdgeInsets.zero,
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 2,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'精选诗句',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: themeColor,
),
),
const SizedBox(width: 4),
Icon(
Icons.format_quote,
color: themeColor,
size: 14,
),
],
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 20,
right: 20,
bottom: 20,
top: 50,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
_buildTitleSection(isDark, themeColor),
if (widget.poetryData.drtime.isNotEmpty) ...[
_buildContentSection(context, isDark, themeColor),
const SizedBox(height: 3),
],
Align(
alignment: Alignment.centerRight,
child: Container(
margin: EdgeInsets.zero,
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 2,
),
_buildNameSection(isDark, themeColor),
const SizedBox(height: 12),
if (widget.keywordList.isNotEmpty) ...[
_buildKeywordSection(isDark, themeColor),
const SizedBox(height: 16),
],
if (widget.poetryData.introduce.isNotEmpty) ...[
_buildIntroductionSection(context, isDark, themeColor),
],
],
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'精选诗句',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: themeColor,
),
),
const SizedBox(width: 4),
Icon(Icons.format_quote, color: themeColor, size: 14),
],
),
),
),
),
],
_buildNameSection(isDark, themeColor),
const SizedBox(height: 12),
if (widget.keywordList.isNotEmpty) ...[
_buildKeywordSection(isDark, themeColor),
const SizedBox(height: 16),
],
if (widget.poetryData.introduce.isNotEmpty) ...[
_buildIntroductionSection(context, isDark, themeColor),
],
],
),
),
),
_buildTimeBar(isDark, themeColor),
_buildCopyTip(isDark, themeColor),
],
),
@@ -201,52 +198,57 @@ class _PoetryCardState extends State<PoetryCard> {
}
Widget _buildTimeBar(bool isDark, Color themeColor) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
_showRecommendation = !_showRecommendation;
});
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [themeColor.withAlpha(204), themeColor],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
return Positioned(
top: 16,
left: 16,
right: 16,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
_showRecommendation = !_showRecommendation;
});
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [themeColor.withAlpha(204), themeColor],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
),
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
),
child: Row(
children: [
Icon(
_showRecommendation ? Icons.lightbulb : Icons.wb_sunny,
color: Colors.white,
size: 20,
),
const SizedBox(width: 8),
Expanded(
child: Text(
_showRecommendation
? _getRecommendation()
: _getTimeOfDayGreeting(),
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
child: Row(
children: [
Icon(
_showRecommendation ? Icons.lightbulb : Icons.wb_sunny,
color: Colors.white,
size: 20,
),
),
Icon(
_showRecommendation ? Icons.expand_less : Icons.expand_more,
color: Colors.white70,
size: 20,
),
],
const SizedBox(width: 8),
Expanded(
child: Text(
_showRecommendation
? _getRecommendation()
: _getTimeOfDayGreeting(),
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
Icon(
_showRecommendation ? Icons.expand_less : Icons.expand_more,
color: Colors.white70,
size: 20,
),
],
),
),
),
);
@@ -258,7 +260,7 @@ class _PoetryCardState extends State<PoetryCard> {
}
return Positioned(
top: 56,
top: 80,
right: 24,
child: GestureDetector(
onTap: () => setState(() => _showCopyTip = false),
@@ -299,6 +301,8 @@ class _PoetryCardState extends State<PoetryCard> {
Widget _buildTitleSection(bool isDark, Color themeColor) {
final isLoading = widget.sectionLoadingStates?['title'] ?? false;
final baseColor = isDark ? const Color(0xFF3A3A3A) : Colors.grey[300];
final highlightColor = isDark ? const Color(0xFF4A4A4A) : Colors.grey[100];
return SizedBox(
height: 28,
@@ -314,30 +318,12 @@ class _PoetryCardState extends State<PoetryCard> {
isDark: isDark,
),
child: isLoading
? Row(
children: [
SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
themeColor,
),
),
),
const SizedBox(width: 8),
Text(
'出处加载中...',
style: TextStyle(
fontSize: 14,
color: isDark
? Colors.grey[400]
: Colors.grey[600],
fontStyle: FontStyle.italic,
),
),
],
? SkeletonContainer(
width: double.infinity,
height: 20,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
)
: Text(
"出处: ${widget.poetryData.url}",
@@ -360,6 +346,8 @@ class _PoetryCardState extends State<PoetryCard> {
Widget _buildNameSection(bool isDark, Color themeColor) {
final isLoading = widget.sectionLoadingStates?['name'] ?? false;
final baseColor = isDark ? const Color(0xFF3A3A3A) : Colors.grey[300];
final highlightColor = isDark ? const Color(0xFF4A4A4A) : Colors.grey[100];
return Container(
width: double.infinity,
@@ -402,23 +390,20 @@ class _PoetryCardState extends State<PoetryCard> {
? Center(
child: Column(
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
themeColor,
),
),
SkeletonContainer(
width: 120,
height: 28,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 8),
Text(
'诗句加载中...',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
SkeletonContainer(
width: 80,
height: 16,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
],
),
@@ -454,6 +439,8 @@ class _PoetryCardState extends State<PoetryCard> {
Widget _buildKeywordSection(bool isDark, Color themeColor) {
final isLoading = widget.sectionLoadingStates?['keywords'] ?? false;
final baseColor = isDark ? const Color(0xFF3A3A3A) : Colors.grey[300];
final highlightColor = isDark ? const Color(0xFF4A4A4A) : Colors.grey[100];
return Column(
children: [
@@ -461,32 +448,33 @@ class _PoetryCardState extends State<PoetryCard> {
children: [
Expanded(
child: isLoading
? Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 16,
height: 8,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
themeColor,
),
),
),
const SizedBox(width: 4),
Text(
'关键词加载中...',
style: TextStyle(
fontSize: 12,
color: isDark
? Colors.grey[400]
: Colors.grey[600],
),
),
],
),
? Wrap(
spacing: 8,
runSpacing: 8,
alignment: WrapAlignment.center,
children: [
SkeletonContainer(
width: 60,
height: 24,
borderRadius: 12,
baseColor: baseColor,
highlightColor: highlightColor,
),
SkeletonContainer(
width: 80,
height: 24,
borderRadius: 12,
baseColor: baseColor,
highlightColor: highlightColor,
),
SkeletonContainer(
width: 70,
height: 24,
borderRadius: 12,
baseColor: baseColor,
highlightColor: highlightColor,
),
],
)
: Wrap(
spacing: 8,
@@ -602,6 +590,8 @@ class _PoetryCardState extends State<PoetryCard> {
Color themeColor,
) {
final isLoading = widget.sectionLoadingStates?['content'] ?? false;
final baseColor = isDark ? const Color(0xFF3A3A3A) : Colors.grey[300];
final highlightColor = isDark ? const Color(0xFF4A4A4A) : Colors.grey[100];
return Builder(
builder: (context) => GestureDetector(
@@ -622,28 +612,41 @@ class _PoetryCardState extends State<PoetryCard> {
borderRadius: BorderRadius.circular(8),
),
child: isLoading
? Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(themeColor),
),
),
const SizedBox(width: 8),
Text(
'时间加载中...',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
],
),
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SkeletonContainer(
width: 80,
height: 18,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 12),
SkeletonContainer(
width: double.infinity,
height: 16,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 8),
SkeletonContainer(
width: double.infinity,
height: 16,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
const SizedBox(height: 8),
SkeletonContainer(
width: 150,
height: 16,
borderRadius: 4,
baseColor: baseColor,
highlightColor: highlightColor,
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -30,8 +30,11 @@ class FloatingButtonsVisibilityManager {
Future<void> init() async {
final prefs = await SharedPreferences.getInstance();
_isVisible = prefs.getBool(_key) ?? true;
_visibleNotifier.value = _isVisible;
final visible = prefs.getBool(_key) ?? true;
WidgetsBinding.instance.addPostFrameCallback((_) {
_isVisible = visible;
_visibleNotifier.value = visible;
});
}
bool get isVisible => _isVisible;
@@ -59,10 +62,12 @@ class FloatingButtonsVisibilityManager {
Future<void> _restoreFromFlashing() async {
_cancelAllTimers();
_isFlashing = false;
_flashingNotifier.value = false;
_isVisible = true;
_visibleNotifier.value = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
_isFlashing = false;
_flashingNotifier.value = false;
_isVisible = true;
_visibleNotifier.value = true;
});
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, true);
}
@@ -107,8 +112,10 @@ class FloatingButtonsVisibilityManager {
void dispose() {
_cancelAllTimers();
_visibleNotifier.value = true;
_flashingNotifier.value = false;
WidgetsBinding.instance.addPostFrameCallback((_) {
_visibleNotifier.value = true;
_flashingNotifier.value = false;
});
}
}

View File

@@ -87,15 +87,17 @@ class _AppInfoPageState extends State<AppInfoPage> {
_isDeveloperMode = true;
});
_saveDeveloperMode(true);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('开发者模式激活'),
duration: Duration(seconds: 2),
),
);
}
_tapCount = 0;
final primaryColor = _themeController.currentThemeColor;
Get.snackbar(
'提示',
'开发者模式激活',
snackPosition: SnackPosition.BOTTOM,
colorText: primaryColor,
duration: const Duration(seconds: 2),
borderRadius: 8,
margin: const EdgeInsets.all(16),
);
}
}
@@ -125,11 +127,15 @@ class _AppInfoPageState extends State<AppInfoPage> {
IconButton(
icon: const Icon(Icons.bug_report, color: Colors.green),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('调试信息已激活'),
duration: Duration(seconds: 2),
),
final primaryColor = _themeController.currentThemeColor;
Get.snackbar(
'提示',
'调试信息已激活',
snackPosition: SnackPosition.BOTTOM,
colorText: primaryColor,
duration: const Duration(seconds: 2),
borderRadius: 8,
margin: const EdgeInsets.all(16),
);
},
),
@@ -149,8 +155,6 @@ class _AppInfoPageState extends State<AppInfoPage> {
_buildDeviceInfoCard(context, isDark, primaryColor),
const SizedBox(height: 16),
_buildUpdateLogCard(isDark),
const SizedBox(height: 16),
_buildDesignStyleCard(isDark),
const SizedBox(height: 24),
_buildBottomIndicator(isDark),
],
@@ -251,7 +255,7 @@ class _AppInfoPageState extends State<AppInfoPage> {
GestureDetector(
onTap: _onFrameworkTap,
child: const Text(
'框架 1.3',
'框架 1.4',
style: TextStyle(
fontSize: 12,
color: Colors.white,
@@ -270,16 +274,18 @@ class _AppInfoPageState extends State<AppInfoPage> {
: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(4),
),
child: Text(
'软件版本 ${AppConfig.appVersion}',
style: TextStyle(
fontSize: 10,
color: _isDeveloperMode
? Colors.green
: Colors.white,
fontWeight: _isDeveloperMode
? FontWeight.bold
: FontWeight.normal,
child: Obx(
() => Text(
'软件版本 ${AppConfig.appVersion}',
style: TextStyle(
fontSize: 10,
color: _isDeveloperMode
? Colors.green
: Colors.white,
fontWeight: _isDeveloperMode
? FontWeight.bold
: FontWeight.normal,
),
),
),
),
@@ -350,15 +356,15 @@ class _AppInfoPageState extends State<AppInfoPage> {
childAspectRatio: 2.2,
children: [
_buildTechStackItem(
'Flutter',
'跨平台UI框架',
'Dart',
'编程语言',
Icons.flutter_dash,
Colors.blue,
isDark,
),
_buildTechStackItem(
'Dart',
'编程语言',
'Getx',
'状态管理库',
Icons.code,
Colors.teal,
isDark,
@@ -456,19 +462,23 @@ class _AppInfoPageState extends State<AppInfoPage> {
),
),
Divider(height: 1, color: isDark ? Colors.grey[800] : null),
_buildCopyableItem(
context,
'版本号',
AppConfig.appVersion,
Icons.verified,
isDark,
Obx(
() => _buildCopyableItem(
context,
'版本号',
AppConfig.appVersion,
Icons.verified,
isDark,
),
),
_buildCopyableItem(
context,
'Builder version',
'26d03a26',
Icons.developer_mode,
isDark,
Obx(
() => _buildCopyableItem(
context,
'Builder version',
AppConfig.appVersionCode.toString(),
Icons.developer_mode,
isDark,
),
),
_buildInfoItem(
'打包时间',
@@ -542,6 +552,7 @@ class _AppInfoPageState extends State<AppInfoPage> {
{'name': 'Cupertino Icons', 'license': 'MIT'},
{'name': 'Shared Preferences', 'license': 'BSD 3-Clause'},
{'name': 'Dio', 'license': 'MIT'},
{'name': 'GetX', 'license': 'MIT'},
{'name': 'Platform Info', 'license': 'MIT'},
{'name': 'flutter_udid', 'license': 'MIT'},
];
@@ -979,26 +990,27 @@ class _AppInfoPageState extends State<AppInfoPage> {
],
),
),
// Divider(height: 1, color: isDark ? Colors.grey[800] : null),
// Padding(
// padding: const EdgeInsets.all(16),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// _buildUpdateItem('版本 1.2.40', '2026-03-27', [
// '新增:主题个性化页面',
// '新增:设计风格卡片',
// '优化:界面布局和响应式设计',
// ], isDark),
// const SizedBox(height: 16),
// _buildUpdateItem('版本 1.2.39', '2026-03-27', [
// '修复:引导页滑动问题',
// '优化:左侧进度条位置',
// '新增:协议内容焦点功能',
// ], isDark),
// ],
// ),
// ),
Divider(height: 1, color: isDark ? Colors.grey[800] : null),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildUpdateItem('版本 1.4.1', '2026-04-03', [
'新增:从 pubspec.yaml 动态获取版本号',
'新增:响应式版本号显示',
'优化:解决 package_info_plus 依赖冲突',
'修复:版本号不显示的问题',
], isDark),
const SizedBox(height: 16),
_buildUpdateItem('版本 1.3.59', '2026-04-03', [
'修复:出处字段被时间提示语遮挡的问题',
'修复:时间提示语布局遮挡问题',
'优化:诗词卡片内容布局',
], isDark),
],
),
),
],
),
);
@@ -1417,20 +1429,15 @@ class _AppInfoPageState extends State<AppInfoPage> {
void _copyToClipboard(BuildContext context, String text) {
Clipboard.setData(ClipboardData(text: text));
final primaryColor = _themeController.currentThemeColor;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const Icon(Icons.check_circle, color: Colors.white, size: 20),
const SizedBox(width: 8),
Text('$text 已复制到剪贴板'),
],
),
backgroundColor: primaryColor,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
duration: const Duration(seconds: 2),
),
Get.snackbar(
'复制成功',
'$text 已复制到剪贴板',
snackPosition: SnackPosition.BOTTOM,
colorText: primaryColor,
duration: const Duration(seconds: 2),
borderRadius: 8,
margin: const EdgeInsets.all(16),
icon: Icon(Icons.check_circle, color: primaryColor, size: 20),
);
}

View File

@@ -150,10 +150,7 @@ class _EntirePageState extends State<EntirePage>
backgroundColor: AppColors.surface,
elevation: 0,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: AppColors.primary,
),
icon: Icon(Icons.arrow_back_ios, color: AppColors.primary),
onPressed: () => Navigator.pop(context),
),
title: Text(
@@ -167,20 +164,14 @@ class _EntirePageState extends State<EntirePage>
centerTitle: true,
actions: [
IconButton(
icon: Icon(
Icons.info_outline,
color: AppColors.primary,
),
icon: Icon(Icons.info_outline, color: AppColors.primary),
onPressed: _showServerInfo,
tooltip: '服务器信息',
),
],
bottom: PreferredSize(
preferredSize: const Size.fromHeight(0.5),
child: Container(
height: 0.5,
color: AppColors.divider,
),
child: Container(height: 0.5, color: AppColors.divider),
),
);
}
@@ -417,10 +408,7 @@ class _EntirePageState extends State<EntirePage>
const SizedBox(height: 16),
Text(
_errorMessage ?? '加载失败',
style: TextStyle(
color: AppColors.tertiaryText,
fontSize: 14,
),
style: TextStyle(color: AppColors.tertiaryText, fontSize: 14),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
@@ -475,10 +463,7 @@ class _EntirePageState extends State<EntirePage>
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.primary,
AppColors.primary.withValues(alpha: 0.8),
],
colors: [AppColors.primary, AppColors.primary.withValues(alpha: 0.8)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
@@ -590,13 +575,6 @@ class _EntirePageState extends State<EntirePage>
Widget _buildCountGrid(bool isDark) {
final counts = [
{
'label': '项目',
'value': _statsData?['count_category'] ?? 0,
'icon': Icons.category,
'color': AppColors.iosBlue,
'showIcon': true,
},
{
'label': '收录诗句',
'value': _statsData?['count_site'] ?? 0,
@@ -604,6 +582,31 @@ class _EntirePageState extends State<EntirePage>
'color': AppColors.iosGreen,
'showIcon': false,
},
{
'label': '诗词题目',
'value': _statsData?['count_article_category'] ?? 0,
'icon': Icons.folder,
'color': AppColors.iosPink,
'showIcon': false,
},
{
'label': '分类标签',
'value': _statsData?['count_tags'] ?? 0,
'icon': Icons.label,
'color': const Color(0xFF64D2FF),
'showIcon': false,
},
{
'label': '项目',
'value': _statsData?['count_category'] ?? 0,
'icon': Icons.category,
'color': AppColors.iosBlue,
'showIcon': true,
},
{
'label': '审核中',
'value': _statsData?['count_apply'] ?? 0,
@@ -625,13 +628,7 @@ class _EntirePageState extends State<EntirePage>
'color': AppColors.iosPurple,
'showIcon': true,
},
{
'label': '文章分类',
'value': _statsData?['count_article_category'] ?? 0,
'icon': Icons.folder,
'color': AppColors.iosPink,
'showIcon': true,
},
{
'label': '推送',
'value': _statsData?['count_notice'] ?? 0,
@@ -646,13 +643,6 @@ class _EntirePageState extends State<EntirePage>
'color': AppColors.iosRed,
'showIcon': true,
},
{
'label': '分类标签',
'value': _statsData?['count_tags'] ?? 0,
'icon': Icons.label,
'color': const Color(0xFF64D2FF),
'showIcon': false,
},
];
return GridView.builder(
@@ -739,10 +729,7 @@ class _EntirePageState extends State<EntirePage>
child: Center(
child: Text(
label,
style: TextStyle(
fontSize: 12,
color: AppColors.secondaryText,
),
style: TextStyle(fontSize: 12, color: AppColors.secondaryText),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
@@ -974,11 +961,7 @@ class _EntirePageState extends State<EntirePage>
color: AppColors.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
Icons.cake,
color: AppColors.primary,
size: 20,
),
child: Icon(Icons.cake, color: AppColors.primary, size: 20),
),
const SizedBox(width: 12),
Expanded(

View File

@@ -801,7 +801,7 @@ class _AppDataPageState extends State<AppDataPage> {
),
_buildDivider(),
_buildActionButton(
'🔧 原生清理数据',
'🔧 系统级清理数据',
'删除应用数据目录所有文件',
Icons.system_security_update_warning,
Colors.orange,

View File

@@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart';
import '../../../services/get/theme_controller.dart';
import '../../../controllers/settings/suggestions.dart';
/// 时间: 2026-03-27
/// 功能: 权限管理页面
@@ -142,6 +143,8 @@ class _PermissionPageState extends State<PermissionPage> {
const SizedBox(height: 16),
_buildProjectSupplement(isDark),
const SizedBox(height: 24),
const SuggestionsCard(),
const SizedBox(height: 24),
_buildBottomTip(isDark),
],
),
@@ -674,14 +677,14 @@ class _PermissionPageState extends State<PermissionPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 4,
height: 4,
margin: const EdgeInsets.only(top: 8),
decoration: BoxDecoration(
color: _themeController.currentThemeColor,
borderRadius: BorderRadius.circular(2),
),
width: 4,
height: 4,
margin: const EdgeInsets.only(top: 8),
decoration: BoxDecoration(
color: _themeController.currentThemeColor,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(

View File

@@ -496,6 +496,8 @@ class HistoryPage extends StatelessWidget {
int index,
Map<String, dynamic> item,
) {
final themeController = Get.find<ThemeController>();
final primaryColor = themeController.currentThemeColor;
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) => CupertinoActionSheet(
@@ -512,14 +514,14 @@ class HistoryPage extends StatelessWidget {
CupertinoActionSheetAction(
onPressed: () {
Get.back();
Get.snackbar('提示', '分享功能开发中...');
Get.snackbar('提示', '分享功能开发中...', colorText: primaryColor);
},
child: const Text('分享'),
),
CupertinoActionSheetAction(
onPressed: () {
Get.back();
Get.snackbar('提示', '查看详情功能开发中...');
Get.snackbar('提示', '查看详情功能开发中...', colorText: primaryColor);
},
child: const Text('查看详情'),
),

View File

@@ -933,11 +933,15 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: isDark
? Colors.grey[800]
: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
color: Colors.black.withAlpha(
isDark ? 40 : 10,
),
blurRadius: 12,
offset: const Offset(0, 4),
),
@@ -953,10 +957,15 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.white,
Colors.grey[50]!,
],
colors: isDark
? [
Colors.grey[700]!,
Colors.grey[800]!,
]
: [
Colors.white,
Colors.grey[50]!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
@@ -964,7 +973,7 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
BorderRadius.circular(12),
border: Border.all(
color: primaryColor.withAlpha(
50,
isDark ? 80 : 50,
),
width: 1,
),
@@ -988,18 +997,19 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
children: [
Icon(
Icons.arrow_back,
color: AppConstants
.primaryColor,
color: primaryColor,
size: 20,
),
const SizedBox(width: 8),
const Text(
Text(
'上一题',
style: TextStyle(
fontSize: 14,
fontWeight:
FontWeight.w500,
color: Colors.black87,
color: isDark
? Colors.white
: Colors.black87,
),
),
],
@@ -1013,10 +1023,15 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.white,
Colors.grey[50]!,
],
colors: isDark
? [
Colors.grey[700]!,
Colors.grey[800]!,
]
: [
Colors.white,
Colors.grey[50]!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
@@ -1029,7 +1044,7 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
boxShadow: [
BoxShadow(
color: primaryColor.withAlpha(
30,
isDark ? 50 : 30,
),
blurRadius: 8,
offset: const Offset(0, 4),
@@ -1057,18 +1072,19 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
children: [
Icon(
Icons.lightbulb_outline,
color: AppConstants
.primaryColor,
color: primaryColor,
size: 20,
),
const SizedBox(width: 8),
const Text(
Text(
'提示',
style: TextStyle(
fontSize: 14,
fontWeight:
FontWeight.w600,
color: Colors.black87,
color: isDark
? Colors.white
: Colors.black87,
),
),
],
@@ -1093,9 +1109,9 @@ class _PoetryLevelPageState extends State<PoetryLevelPage>
BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: AppConstants
.primaryColor
.withAlpha(80),
color: primaryColor.withAlpha(
isDark ? 60 : 80,
),
blurRadius: 12,
offset: const Offset(0, 4),
),

View File

@@ -278,10 +278,12 @@ class PersonalCardState extends State<PersonalCard> {
HapticFeedback.lightImpact();
// 显示切换提示
final themeController = Get.find<ThemeController>();
Get.snackbar(
'头像切换',
'头像已切换为 ${_avatarEmojis[_currentAvatarIndex]}',
duration: const Duration(seconds: 1),
colorText: themeController.currentThemeColor,
);
}

View File

@@ -568,7 +568,7 @@ class ProfilePage extends StatelessWidget {
),
const SizedBox(height: 8),
Text(
'版本 ${AppConstants.appVersion}',
'版本 ${AppConfig.appVersion}',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey,

View File

@@ -51,6 +51,7 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
Future<void> _loadSettings() async {
final prefs = await SharedPreferences.getInstance();
await GlobalTipsManager().init();
await AudioManager().init(); // 确保 AudioManager 已初始化
if (mounted) {
setState(() {
_autoRefreshEnabled = prefs.getBool(_autoRefreshKey) ?? false;
@@ -62,6 +63,8 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
_hideSecondaryButtons =
prefs.getBool(_hideSecondaryButtonsKey) ?? false; // 加载隐藏次要按钮状态
});
// 同步到 AudioManager
AudioManager().setMuted(!_soundEnabled);
}
}
@@ -101,7 +104,7 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_soundEnabledKey, value);
// 更新 AudioManager 的静音状态
AudioManager().setMuted(!value);
await AudioManager().setMuted(!value);
if (mounted) {
setState(() {
_soundEnabled = value;
@@ -506,60 +509,53 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
final primaryColor = _themeController.currentThemeColor;
return Obx(() {
final currentIndex = _glassController.transparencyLevelIndex;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
child: Row(
return Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 12),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: primaryColor.withAlpha(10),
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.opacity, color: primaryColor, size: 20),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: primaryColor.withAlpha(10),
borderRadius: BorderRadius.circular(8),
Text(
'高透级别',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white : Colors.black,
),
child: Icon(Icons.opacity, color: primaryColor, size: 20),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'高透级别',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white : Colors.black,
),
),
Text(
'当前: ${_glassController.transparencyLevelLabel}',
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
],
Text(
'当前: ${_glassController.transparencyLevelLabel}',
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
],
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
child: Row(
children: [
_buildLevelButton('', 0, currentIndex == 0, isDark),
const SizedBox(width: 8),
_buildLevelButton('', 1, currentIndex == 1, isDark),
const SizedBox(width: 8),
_buildLevelButton('', 2, currentIndex == 2, isDark),
],
const SizedBox(width: 12),
Expanded(
child: Row(
children: [
_buildLevelButton('', 0, currentIndex == 0, isDark),
const SizedBox(width: 8),
_buildLevelButton('', 1, currentIndex == 1, isDark),
const SizedBox(width: 8),
_buildLevelButton('', 2, currentIndex == 2, isDark),
],
),
),
),
],
],
),
);
});
}

View File

@@ -212,30 +212,18 @@ class LearnUsPage extends StatelessWidget {
),
),
const SizedBox(height: 12),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF3A3A3A) : Colors.grey[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isDark
? Colors.grey[700]!.withAlpha(20)
: Colors.grey.withAlpha(20),
),
),
child: Row(
children: [
Icon(Icons.link, size: 16, color: primaryColor),
const SizedBox(width: 8),
Expanded(
child: Text(
'https://poe.vogov.cn/app.html',
style: TextStyle(fontSize: 14, color: primaryColor),
),
),
],
),
_buildWebsiteItem(
'https://poe.vogov.cn/app.html',
'官方APP页',
isDark,
primaryColor,
),
const SizedBox(height: 12),
_buildWebsiteItem(
'https://poe.vogov.cn/',
'情景诗词在线版',
isDark,
primaryColor,
),
],
),
@@ -245,6 +233,71 @@ class LearnUsPage extends StatelessWidget {
);
}
Widget _buildWebsiteItem(
String url,
String label,
bool isDark,
Color primaryColor,
) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF3A3A3A) : Colors.grey[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isDark
? Colors.grey[700]!.withAlpha(20)
: Colors.grey.withAlpha(20),
),
),
child: Row(
children: [
Icon(Icons.link, size: 16, color: primaryColor),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey,
),
),
Text(url, style: TextStyle(fontSize: 14, color: primaryColor)),
],
),
),
const SizedBox(width: 8),
GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: url));
Get.snackbar(
'复制成功',
'链接已复制到剪贴板',
duration: const Duration(seconds: 2),
colorText: primaryColor,
snackPosition: SnackPosition.BOTTOM,
borderRadius: 8,
margin: const EdgeInsets.all(16),
);
},
child: Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: primaryColor.withAlpha(10),
borderRadius: BorderRadius.circular(4),
),
child: Icon(Icons.content_copy, size: 16, color: primaryColor),
),
),
],
),
);
}
Widget _buildQQGroupCard(BuildContext context, bool isDark) {
final themeController = Get.find<ThemeController>();
final primaryColor = themeController.currentThemeColor;
@@ -352,20 +405,15 @@ class LearnUsPage extends StatelessWidget {
final primaryColor = themeController.currentThemeColor;
Clipboard.setData(const ClipboardData(text: '271129018'));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Row(
children: [
Icon(Icons.check_circle, color: Colors.white, size: 20),
SizedBox(width: 8),
Text('QQ群号已复制到剪贴板'),
],
),
backgroundColor: primaryColor,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
duration: const Duration(seconds: 2),
),
Get.snackbar(
'复制成功',
'QQ群号已复制到剪贴板',
duration: const Duration(seconds: 2),
colorText: primaryColor,
snackPosition: SnackPosition.BOTTOM,
borderRadius: 8,
margin: const EdgeInsets.all(16),
icon: Icon(Icons.check_circle, color: primaryColor, size: 20),
);
}
@@ -572,14 +620,21 @@ class LearnUsPage extends StatelessWidget {
const SizedBox(width: 8),
GestureDetector(
onTap: () {
final themeController =
Get.find<ThemeController>();
final primaryColor =
themeController.currentThemeColor;
Clipboard.setData(
const ClipboardData(text: '微风暴'),
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('已复制到剪贴板'),
duration: Duration(seconds: 2),
),
Get.snackbar(
'复制成功',
'已复制到剪贴板',
duration: const Duration(seconds: 2),
colorText: primaryColor,
snackPosition: SnackPosition.BOTTOM,
borderRadius: 8,
margin: const EdgeInsets.all(16),
);
},
child: Icon(
@@ -734,6 +789,8 @@ class LearnUsPage extends StatelessWidget {
}
Widget _buildIcpCard(BuildContext context, bool isDark) {
final themeController = Get.find<ThemeController>();
final primaryColor = themeController.currentThemeColor;
return Container(
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
@@ -794,6 +851,7 @@ class LearnUsPage extends StatelessWidget {
'复制成功',
'备案号已复制到剪贴板',
duration: const Duration(seconds: 1),
colorText: primaryColor,
);
},
child: Row(

View File

@@ -85,14 +85,12 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
) ??
[];
if (existingData.length >= 500) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('缓存已达上限500条请先清空缓存'),
backgroundColor: Colors.red,
),
);
}
final themeController = Get.find<ThemeController>();
Get.snackbar(
'提示',
'缓存已达上限500条请先清空缓存',
colorText: themeController.currentThemeColor,
);
return;
}
@@ -100,23 +98,24 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
if (_selectedCount == 100) {
final isUserPlanJoined = await _checkUserPlanStatus();
if (!isUserPlanJoined) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('100条下载需要加入用户体验计划'),
backgroundColor: Colors.orange,
action: SnackBarAction(
label: '去加入',
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const UserPlanPage()),
);
},
),
final themeController = Get.find<ThemeController>();
Get.snackbar(
'提示',
'100条下载需要加入用户体验计划',
colorText: themeController.currentThemeColor,
mainButton: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const UserPlanPage()),
);
},
child: Text(
'去加入',
style: TextStyle(color: themeController.currentThemeColor),
),
);
}
),
);
return;
}
}
@@ -149,14 +148,12 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
// 取消时保存已下载的数据
if (_downloadedCount > 0) {
await prefs.setStringList(dataKey, currentData);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('已保存 $_downloadedCount 条数据'),
backgroundColor: _themeController.currentThemeColor,
),
);
}
final themeController = Get.find<ThemeController>();
Get.snackbar(
'提示',
'已保存 $_downloadedCount 条数据',
colorText: themeController.currentThemeColor,
);
}
return;
}
@@ -240,17 +237,15 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
// 检查总缓存数量
if (currentData.length > 500) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('缓存将超过500条上限请先清空缓存'),
backgroundColor: Colors.red,
),
);
setState(() {
_isLoading = false;
});
}
final themeController = Get.find<ThemeController>();
Get.snackbar(
'提示',
'缓存将超过500条上限请先清空缓存',
colorText: themeController.currentThemeColor,
);
setState(() {
_isLoading = false;
});
return;
}
@@ -260,13 +255,12 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
_cachedCount = currentData.length;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'成功缓存 ${_selectedType == DownloadType.poetry ? '诗词' : '答题'} $_cachedCount 条数据',
),
backgroundColor: _themeController.currentThemeColor,
),
final themeController = Get.find<ThemeController>();
final themeController2 = Get.find<ThemeController>();
Get.snackbar(
'成功',
'成功缓存 ${_selectedType == DownloadType.poetry ? '诗词' : '答题'} $_cachedCount 条数据',
colorText: themeController2.currentThemeColor,
);
}
} catch (e) {
@@ -275,8 +269,11 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
_status = '下载失败: $e';
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('下载失败: $e'), backgroundColor: Colors.red),
final themeController = Get.find<ThemeController>();
Get.snackbar(
'错误',
'下载失败: $e',
colorText: themeController.currentThemeColor,
);
}
} finally {
@@ -297,14 +294,12 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
final quizCount = (prefs.getStringList('offline_quiz_data') ?? []).length;
if (poetryCount == 0 && quizCount == 0) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('暂无缓存数据'),
backgroundColor: Colors.orange,
),
);
}
final themeController = Get.find<ThemeController>();
Get.snackbar(
'提示',
'暂无缓存数据',
colorText: themeController.currentThemeColor,
);
return;
}
@@ -367,14 +362,12 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(key);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('已清空$typeName离线数据'),
backgroundColor: _themeController.currentThemeColor,
),
);
}
final themeController = Get.find<ThemeController>();
Get.snackbar(
'成功',
'已清空$typeName离线数据',
colorText: themeController.currentThemeColor,
);
// 更新对应的计数
if (key == 'offline_poetry_data') {
@@ -399,14 +392,12 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
await prefs.remove('offline_poetry_data');
await prefs.remove('offline_quiz_data');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('已清空所有离线数据'),
backgroundColor: _themeController.currentThemeColor,
),
);
}
final themeController = Get.find<ThemeController>();
Get.snackbar(
'成功',
'已清空所有离线数据',
colorText: themeController.currentThemeColor,
);
setState(() {
_poetryCount = 0;
@@ -455,55 +446,47 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
if (data['status'] == 'success') {
_displayServerInfoDialog(data);
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('服务器返回错误状态: ${data['status']}'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
);
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('解析服务器数据失败: $e'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
final themeController = Get.find<ThemeController>();
Get.snackbar(
'错误',
'服务器返回错误状态: ${data['status']}',
colorText: themeController.currentThemeColor,
duration: const Duration(seconds: 3),
);
}
}
} else {
if (mounted) {
final errorMsg =
'获取服务器信息失败\n'
'状态码: ${response.statusCode}\n'
'消息: ${response.message}';
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(errorMsg),
backgroundColor: Colors.red,
duration: const Duration(seconds: 5),
),
} catch (e) {
final themeController = Get.find<ThemeController>();
Get.snackbar(
'错误',
'解析服务器数据失败: $e',
colorText: themeController.currentThemeColor,
duration: const Duration(seconds: 3),
);
}
} else {
final errorMsg =
'获取服务器信息失败\n'
'状态码: ${response.statusCode}\n'
'消息: ${response.message}';
final themeController = Get.find<ThemeController>();
Get.snackbar(
'错误',
errorMsg,
colorText: themeController.currentThemeColor,
duration: const Duration(seconds: 5),
);
}
} catch (e) {
if (!mounted) return;
Navigator.of(context).pop();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('获取服务器信息异常: $e'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 5),
),
);
}
final themeController = Get.find<ThemeController>();
Get.snackbar(
'错误',
'获取服务器信息异常: $e',
colorText: themeController.currentThemeColor,
duration: const Duration(seconds: 5),
);
}
}

View File

@@ -23,6 +23,7 @@ class _AppDiyPageState extends State<AppDiyPage> {
bool _showGuideOnStartup = true;
int _cardSizeIndex = 1; // 0: 小, 1: 中, 2: 大
bool _enableSystemNavigation = false;
bool _showDevNotice = true; // 是否显示开发中提示对话框
// 滚动控制
final ScrollController _scrollController = ScrollController();
@@ -50,12 +51,16 @@ class _AppDiyPageState extends State<AppDiyPage> {
_startScrolling();
// 延迟显示开发中提示对话框
WidgetsBinding.instance.addPostFrameCallback((_) {
_showDevNoticeDialog();
if (_showDevNotice) {
_showDevNoticeDialog();
}
});
}
// 显示开发中提示对话框
void _showDevNoticeDialog() {
bool doNotShowAgain = false;
showDialog(
context: context,
barrierDismissible: false,
@@ -63,81 +68,119 @@ class _AppDiyPageState extends State<AppDiyPage> {
final primaryColor = _themeController.currentThemeColor;
final isDark = _themeController.isDarkMode;
return AlertDialog(
backgroundColor: isDark ? const Color(0xFF2A2A2A) : Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
title: Row(
children: [
Icon(Icons.construction, color: primaryColor),
const SizedBox(width: 8),
Text(
'开发中',
style: TextStyle(color: isDark ? Colors.white : Colors.black),
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
backgroundColor: isDark ? const Color(0xFF2A2A2A) : Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'个性化设置开发中',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: isDark ? Colors.white : Colors.black,
title: Row(
children: [
Icon(Icons.construction, color: primaryColor),
const SizedBox(width: 8),
Text(
'开发中',
style: TextStyle(
color: isDark ? Colors.white : Colors.black,
),
),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'个性化设置开发中',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: isDark ? Colors.white : Colors.black,
),
),
const SizedBox(height: 12),
Text(
'• ✅ 深色模式(已支持)',
style: TextStyle(
color: isDark ? Colors.grey[300] : Colors.black87,
),
),
Text(
'• ✅ 主题色彩(已支持)',
style: TextStyle(
color: isDark ? Colors.grey[300] : Colors.black87,
),
),
Text(
'• ⏳ 其他设置仅当前页面生效',
style: TextStyle(
color: isDark ? Colors.grey[300] : Colors.black87,
),
),
Text(
'• ⏳ 后续版本将陆续支持',
style: TextStyle(
color: isDark ? Colors.grey[300] : Colors.black87,
),
),
const SizedBox(height: 12),
Text(
'感谢您的耐心等待!',
style: TextStyle(
color: isDark ? Colors.grey[400] : Colors.grey[600],
fontSize: 12,
),
),
],
),
actions: [
Row(
children: [
Checkbox(
value: doNotShowAgain,
onChanged: (value) {
setState(() {
doNotShowAgain = value ?? false;
});
},
activeColor: primaryColor,
),
Text(
'不再提醒',
style: TextStyle(
color: isDark ? Colors.grey[300] : Colors.black87,
fontSize: 14,
),
),
const Spacer(),
ElevatedButton(
onPressed: () async {
if (doNotShowAgain) {
await _themeController.prefs?.setBool(
'showDevNotice',
false,
);
setState(() {
_showDevNotice = false;
});
}
Navigator.of(context).pop();
},
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('我知道了'),
),
],
),
),
const SizedBox(height: 12),
Text(
'• ✅ 深色模式(已支持)',
style: TextStyle(
color: isDark ? Colors.grey[300] : Colors.black87,
),
),
Text(
'• ✅ 主题色彩(已支持)',
style: TextStyle(
color: isDark ? Colors.grey[300] : Colors.black87,
),
),
Text(
'• ⏳ 其他设置仅当前页面生效',
style: TextStyle(
color: isDark ? Colors.grey[300] : Colors.black87,
),
),
Text(
'• ⏳ 后续版本将陆续支持',
style: TextStyle(
color: isDark ? Colors.grey[300] : Colors.black87,
),
),
const SizedBox(height: 12),
Text(
'感谢您的耐心等待!',
style: TextStyle(
color: isDark ? Colors.grey[400] : Colors.grey[600],
fontSize: 12,
),
),
],
),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('我知道了'),
),
],
],
);
},
);
},
);
@@ -187,6 +230,7 @@ class _AppDiyPageState extends State<AppDiyPage> {
_cardSizeIndex = _themeController.prefs?.getInt('cardSizeIndex') ?? 1;
_enableSystemNavigation =
_themeController.prefs?.getBool('enableSystemNavigation') ?? false;
_showDevNotice = _themeController.prefs?.getBool('showDevNotice') ?? true;
});
}
@@ -218,6 +262,16 @@ class _AppDiyPageState extends State<AppDiyPage> {
),
backgroundColor: isDark ? Colors.grey[900] : Colors.white,
elevation: 0,
actions: [
IconButton(
icon: Icon(
Icons.info_outline,
color: _themeController.currentThemeColor,
),
onPressed: () => _showDevNoticeDialog(),
tooltip: '显示开发中提示',
),
],
),
backgroundColor: isDark ? Colors.grey[900] : Colors.grey[50],
body: ListView(