Files
wushu/lib/views/profile/guide/sp-guide.dart
2026-04-02 07:06:55 +08:00

1227 lines
40 KiB
Dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart';
import '../../../config/app_config.dart';
import '../../../services/get/theme_controller.dart';
import '../settings/privacy.dart';
import '../settings/user-plan.dart';
import 'permission.dart';
/// 时间: 2026-03-27
/// 功能: 引导页
/// 介绍: 展示欢迎语、隐私政策、功能介绍,支持垂直滑动切换
/// 最新变化: 支持所有页面垂直滑动,左侧显示页面进度指示器,集成协议内容,添加协议同意状态
class SpGuidePage extends StatefulWidget {
final bool fromSettings;
const SpGuidePage({super.key, this.fromSettings = false});
@override
State<SpGuidePage> createState() => _SpGuidePageState();
}
class _SpGuidePageState extends State<SpGuidePage>
with TickerProviderStateMixin {
final PageController _pageController = PageController();
final ThemeController _themeController = Get.find<ThemeController>();
TabController? _tabController;
int _currentPage = 0;
bool _firstLaunch = true;
bool _showGuideOnStartup = false;
bool _agreementAccepted = false;
bool _userPlanJoined = false;
bool _isAgreementFocused = false;
final int _totalPages = 3;
final List<String> _pageTitles = ['欢迎使用', '双击中心区域查看协议', '了解软件功能'];
@override
void initState() {
super.initState();
_loadSettings();
}
@override
void dispose() {
_pageController.dispose();
_tabController?.dispose();
super.dispose();
}
void _initTabController() {
if (_tabController == null) {
_tabController = TabController(length: 2, vsync: this);
}
}
Future<void> _loadSettings() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_firstLaunch = prefs.getBool(AppConfig.keyFirstLaunch) ?? true;
_showGuideOnStartup =
prefs.getBool(AppConfig.keyShowGuideOnStartup) ?? false;
_agreementAccepted =
prefs.getBool(AppConfig.keyAgreementAccepted) ?? false;
_userPlanJoined = prefs.getBool(AppConfig.keyUserPlanJoined) ?? false;
});
}
Future<void> _toggleShowGuide(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(AppConfig.keyShowGuideOnStartup, value);
setState(() {
_showGuideOnStartup = value;
});
}
Future<void> _toggleUserPlan(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(AppConfig.keyUserPlanJoined, value);
setState(() {
_userPlanJoined = value;
});
}
Future<void> _acceptAgreement() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(AppConfig.keyAgreementAccepted, true);
await prefs.setBool(AppConfig.keyFirstLaunch, false);
setState(() {
_agreementAccepted = true;
_firstLaunch = false;
});
}
Future<void> _rejectAgreement() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(AppConfig.keyAgreementAccepted, false);
setState(() {
_agreementAccepted = false;
});
}
void _rejectAndExit() {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: Row(
children: [
Icon(Icons.exit_to_app, color: Colors.red[700]),
const SizedBox(width: 8),
const Text('退出确认'),
],
),
content: const Text(
'不同意协议将无法使用本软件,确定要退出吗?',
style: TextStyle(fontSize: 14, height: 1.5),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
exit(0);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('确定退出'),
),
],
),
);
}
void _nextPage() {
if (_currentPage < _totalPages - 1 && _pageController.hasClients) {
_pageController.nextPage(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
);
}
}
void _previousPage() {
if (_currentPage > 0 && _pageController.hasClients) {
_pageController.previousPage(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
);
}
}
void _showNeedAcceptAgreementDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: Row(
children: [
Icon(Icons.warning_amber, color: Colors.orange[700]),
const SizedBox(width: 8),
const Text('提示'),
],
),
content: const Text(
'不同意退出',
style: TextStyle(fontSize: 14, height: 1.5),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
_acceptAgreement();
_nextPage();
},
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('同意并继续'),
),
],
),
);
}
void _finishGuide() {
if (!_agreementAccepted) {
_showNeedAcceptAgreementDialog();
return;
}
if (widget.fromSettings) {
Navigator.pop(context);
} else {
Navigator.pushReplacementNamed(context, '/home');
}
}
@override
Widget build(BuildContext context) {
return Obx(() {
final isDark = _themeController.isDarkMode;
return WillPopScope(
onWillPop: () async {
if (widget.fromSettings) {
return true;
}
return false;
},
child: Scaffold(
backgroundColor: isDark ? const Color(0xFF1A1A1A) : Colors.white,
appBar: _buildAppBar(isDark),
body: Stack(
children: [
PageView(
controller: _pageController,
scrollDirection: Axis.vertical,
physics: const PageScrollPhysics(),
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
children: [
_buildWelcomePage(isDark),
_buildPrivacyPage(isDark),
_buildFeaturePage(isDark),
],
),
_buildPageIndicator(isDark),
_buildBottomNavigation(isDark),
],
),
),
);
});
}
Widget _buildPageIndicator(bool isDark) {
double alignmentY;
switch (_currentPage) {
case 0:
alignmentY = -0.7;
break;
case 1:
alignmentY = 0.0;
break;
case 2:
alignmentY = 0.7;
break;
default:
alignmentY = 0.0;
}
return Positioned(
left: 16,
top: 0,
bottom: 0,
child: Align(
alignment: Alignment(0, alignmentY),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
decoration: BoxDecoration(
color: (isDark ? const Color(0xFF2A2A2A) : Colors.white).withValues(
alpha: 0.9,
),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.1),
blurRadius: 8,
offset: const Offset(2, 0),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: List.generate(_totalPages, (index) {
final isActive = index == _currentPage;
final isCompleted = index < _currentPage;
return GestureDetector(
onTap: () {
if (_pageController.hasClients) {
_pageController.animateToPage(
index,
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
);
}
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 10),
width: isActive ? 16 : 14,
height: isActive ? 16 : 14,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isActive
? AppConstants.primaryColor
: isCompleted
? AppConstants.primaryColor.withValues(alpha: 0.5)
: (isDark ? Colors.grey[600] : Colors.grey[300]),
border: isActive
? Border.all(color: AppConstants.primaryColor, width: 3)
: null,
boxShadow: isActive
? [
BoxShadow(
color: AppConstants.primaryColor.withValues(
alpha: 0.3,
),
blurRadius: 8,
spreadRadius: 2,
),
]
: null,
),
),
);
}),
),
),
),
);
}
PreferredSizeWidget _buildAppBar(bool isDark) {
return AppBar(
title: Text(
_pageTitles[_currentPage],
style: TextStyle(
color: AppConstants.primaryColor,
fontWeight: FontWeight.bold,
),
),
backgroundColor: isDark ? const Color(0xFF2A2A2A) : Colors.white,
elevation: 0,
centerTitle: true,
leading: widget.fromSettings
? IconButton(
icon: Icon(Icons.arrow_back, color: AppConstants.primaryColor),
onPressed: () => Navigator.pop(context),
)
: null,
automaticallyImplyLeading: false,
);
}
Widget _buildWelcomePage(bool isDark) {
return Container(
padding: const EdgeInsets.all(32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 120,
height: 120,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppConstants.primaryColor.withValues(alpha: 0.1),
AppConstants.primaryColor.withValues(alpha: 0.05),
],
),
borderRadius: BorderRadius.circular(30),
),
child: const Center(
child: Text('📖', style: TextStyle(fontSize: 60)),
),
),
const SizedBox(height: 40),
Text(
'欢迎使用',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 16),
Text(
'情景诗词',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
color: AppConstants.primaryColor,
),
),
const SizedBox(height: 32),
Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.grey[50],
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
_buildWelcomeItem('🌟', '在诗词里旅行,在文化中生长', isDark),
const SizedBox(height: 16),
_buildWelcomeItem('📚', '海量诗词,随心阅读', isDark),
const SizedBox(height: 16),
_buildWelcomeItem('❤️', '收藏喜爱,记录感悟', isDark),
const SizedBox(height: 16),
_buildWelcomeItem('🌙', '每日推荐,发现美好', isDark),
],
),
),
const SizedBox(height: 40),
Container(
margin: const EdgeInsets.only(left: 16),
height: 1,
width: double.infinity,
color: isDark ? Colors.grey[700] : Colors.grey[300],
),
const SizedBox(height: 16),
Text(
'向上滑动继续 ↓',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[500] : Colors.grey[400],
),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton.icon(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const PermissionPage(),
),
);
},
icon: Icon(
Icons.security,
size: 18,
color: AppConstants.primaryColor,
),
label: Text(
'了解软件权限',
style: TextStyle(
color: AppConstants.primaryColor,
fontSize: 14,
),
),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side: BorderSide(
color: AppConstants.primaryColor.withValues(alpha: 0.3),
),
),
),
),
const SizedBox(width: 8),
],
),
],
),
);
}
Widget _buildWelcomeItem(String emoji, String text, bool isDark) {
return Row(
children: [
Text(emoji, style: const TextStyle(fontSize: 20)),
const SizedBox(width: 12),
Expanded(
child: Text(
text,
style: TextStyle(
fontSize: 15,
color: isDark ? Colors.white : Colors.black87,
),
),
),
],
);
}
void _showGuideStatusPopup(bool isDark) {
showDialog(
context: context,
builder: (context) => Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1A1A1A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.15),
blurRadius: 20,
offset: const Offset(0, 4),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Icon(
_showGuideOnStartup
? Icons.check_circle
: Icons.info_outline,
color: _showGuideOnStartup
? AppConstants.primaryColor
: (isDark ? Colors.grey[400] : Colors.grey[600]),
size: 24,
),
const SizedBox(width: 8),
Text(
'通知设置',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: _showGuideOnStartup
? AppConstants.primaryColor.withValues(alpha: 0.1)
: (isDark ? const Color(0xFF2A2A2A) : Colors.grey[50]),
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Icon(
_showGuideOnStartup
? Icons.notifications_active
: Icons.notifications_off,
color: _showGuideOnStartup
? AppConstants.primaryColor
: (isDark ? Colors.grey[400] : Colors.grey[500]),
size: 40,
),
const SizedBox(height: 12),
Text(
_showGuideOnStartup ? '已开启' : '已关闭',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: _showGuideOnStartup
? AppConstants.primaryColor
: (isDark ? Colors.white : Colors.grey[700]),
),
),
const SizedBox(height: 8),
Text(
_showGuideOnStartup ? '下次启动时将显示欢迎页' : '下次启动时将不显示欢迎页',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
textAlign: TextAlign.center,
),
],
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text('知道了'),
),
),
],
),
),
),
);
}
Widget _buildPrivacyPage(bool isDark) {
_initTabController();
return Stack(
children: [
Column(
children: [
Container(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
child: TabBar(
controller: _tabController!,
labelColor: AppConstants.primaryColor,
unselectedLabelColor: isDark
? Colors.grey[400]
: Colors.grey[600],
indicatorColor: AppConstants.primaryColor,
indicatorWeight: 2,
tabs: const [
Tab(text: '《隐私政策》'),
Tab(text: '《用户协议》'),
],
),
),
Expanded(
child: Stack(
children: [
TabBarView(
controller: _tabController!,
physics: _isAgreementFocused
? const AlwaysScrollableScrollPhysics()
: const NeverScrollableScrollPhysics(),
children: [
NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollEndNotification) {
final metrics = notification.metrics;
if (metrics.pixels <= metrics.minScrollExtent ||
metrics.pixels >= metrics.maxScrollExtent) {
setState(() {
_isAgreementFocused = false;
});
}
}
return false;
},
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState(() {
_isAgreementFocused = true;
});
},
child: const Padding(
padding: EdgeInsets.all(16),
child: PrivacyPolicyContent(),
),
),
),
),
NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollEndNotification) {
final metrics = notification.metrics;
if (metrics.pixels <= metrics.minScrollExtent ||
metrics.pixels >= metrics.maxScrollExtent) {
setState(() {
_isAgreementFocused = false;
});
}
}
return false;
},
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState(() {
_isAgreementFocused = true;
});
},
child: const Padding(
padding: EdgeInsets.all(16),
child: UserAgreementContent(),
),
),
),
),
],
),
if (_isAgreementFocused)
Positioned(
left: 0,
top: 0,
bottom: 0,
child: Container(
width: 3,
color: AppConstants.primaryColor,
),
),
if (_isAgreementFocused)
Positioned(
right: 0,
top: 0,
bottom: 0,
child: Container(
width: 3,
color: AppConstants.primaryColor,
),
),
],
),
),
],
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: GestureDetector(
onTap: () {
if (_isAgreementFocused) {
setState(() {
_isAgreementFocused = false;
});
}
},
child: Container(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, -2),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
height: 1,
width: double.infinity,
color: isDark ? Colors.grey[700] : Colors.grey[300],
),
const SizedBox(height: 16),
Row(
children: [
Checkbox(
value: _agreementAccepted,
onChanged: (value) {
if (value == true) {
_acceptAgreement();
} else {
_rejectAgreement();
}
},
activeColor: AppConstants.primaryColor,
),
Expanded(
child: GestureDetector(
onTap: () {
if (_agreementAccepted) {
_rejectAgreement();
} else {
_acceptAgreement();
}
},
child: Text(
'我已阅读并同意隐私政策和用户协议',
style: TextStyle(
fontSize: 14,
color: _agreementAccepted
? AppConstants.primaryColor
: (isDark
? Colors.grey[300]
: Colors.grey[700]),
),
),
),
),
],
),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _agreementAccepted
? _nextPage
: _rejectAndExit,
style: ElevatedButton.styleFrom(
backgroundColor: _agreementAccepted
? AppConstants.primaryColor
: (isDark ? Colors.grey[700] : Colors.grey[600]),
foregroundColor: Colors.white,
disabledBackgroundColor: isDark
? Colors.grey[800]
: Colors.grey[300],
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
_agreementAccepted ? '已同意,继续' : '不同意退出',
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
),
),
],
);
}
Widget _buildFeaturePage(bool isDark) {
return Padding(
padding: const EdgeInsets.fromLTRB(24, 16, 24, 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Text(
'探索丰富的诗词世界',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
),
const SizedBox(height: 16),
Column(
children: [
Row(
children: [
Expanded(
child: _buildFeatureCard(
'🏠 首页',
'每日推荐精选诗词',
Icons.home,
Colors.blue,
isDark,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildFeatureCard(
'🔍 发现',
'浏览排行榜、分类',
Icons.explore,
Colors.green,
isDark,
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: _buildFeatureCard(
'❤️ 收藏',
'点赞收藏、记笔记',
Icons.favorite,
Colors.red,
isDark,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildFeatureCard(
'👤 个人',
'管理数据、设置',
Icons.person,
Colors.purple,
isDark,
),
),
],
),
],
),
const SizedBox(height: 20),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.grey[50],
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(
Icons.volunteer_activism,
color: AppConstants.primaryColor,
size: 18,
),
const SizedBox(width: 8),
Expanded(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UserPlanPage(),
),
).then((_) {
_loadSettings();
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'用户体验计划',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 2),
Text(
'参与产品改进,享受更多权益',
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
),
),
],
),
),
),
Switch(
value: _userPlanJoined,
onChanged: _toggleUserPlan,
activeThumbColor: AppConstants.primaryColor,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
),
const SizedBox(height: 6),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: _agreementAccepted
? Colors.green.withValues(alpha: 0.1)
: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(
_agreementAccepted ? Icons.check_circle : Icons.info,
color: _agreementAccepted
? Colors.green[700]
: Colors.orange[700],
size: 18,
),
const SizedBox(width: 8),
Expanded(
child: Text(
_agreementAccepted ? '已同意软件协议' : '未同意软件协议',
style: TextStyle(
fontSize: 12,
color: _agreementAccepted
? Colors.green[700]
: Colors.orange[700],
),
),
),
],
),
),
const SizedBox(height: 6),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: _userPlanJoined
? Colors.blue.withValues(alpha: 0.1)
: Colors.grey.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(
_userPlanJoined
? Icons.volunteer_activism
: Icons.info_outline,
color: _userPlanJoined ? Colors.blue[700] : Colors.grey[600],
size: 18,
),
const SizedBox(width: 8),
Expanded(
child: Text(
_userPlanJoined ? '已加入体验计划' : '未加入体验计划',
style: TextStyle(
fontSize: 12,
color: _userPlanJoined
? Colors.blue[700]
: Colors.grey[600],
),
),
),
],
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
height: 44,
child: ElevatedButton(
onPressed: _finishGuide,
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 2,
),
child: const Text(
'开始使用',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
),
),
),
const SizedBox(height: 16),
],
),
);
}
Widget _buildFeatureCard(
String title,
String desc,
IconData icon,
Color color,
bool isDark,
) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isDark
? Colors.grey[700]!
: Colors.grey.withValues(alpha: 0.2),
),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.2)
: Colors.black.withValues(alpha: 0.03),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: color, size: 20),
),
const SizedBox(height: 8),
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 2),
Text(
desc,
style: TextStyle(
fontSize: 11,
color: isDark ? Colors.grey[400] : Colors.grey[600],
height: 1.3,
),
),
],
),
);
}
Widget _buildBottomNavigation(bool isDark) {
return Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_currentPage > 0
? _buildNavButton(
icon: Icons.arrow_back_ios,
label: '上一页',
onPressed: _previousPage,
isDark: isDark,
)
: const SizedBox(width: 100),
_currentPage < _totalPages - 1
? _buildNavButton(
icon: Icons.arrow_forward_ios,
label: '下一页',
onPressed: _currentPage == 1 && !_agreementAccepted
? null
: _nextPage,
isDark: isDark,
)
: _buildNavButton(
icon: Icons.check,
label: '完成',
onPressed: _agreementAccepted ? _finishGuide : null,
isDark: isDark,
),
],
),
),
);
}
Widget _buildNavButton({
required IconData icon,
required String label,
VoidCallback? onPressed,
required bool isDark,
}) {
return Container(
decoration: BoxDecoration(
color: onPressed != null
? AppConstants.primaryColor
: (isDark ? Colors.grey[700] : Colors.grey[300]),
borderRadius: BorderRadius.circular(12),
boxShadow: onPressed != null
? [
BoxShadow(
color: AppConstants.primaryColor.withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
]
: null,
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
color: onPressed != null
? Colors.white
: (isDark ? Colors.grey[400] : Colors.grey[500]),
size: 20,
),
const SizedBox(width: 8),
Text(
label,
style: TextStyle(
color: onPressed != null
? Colors.white
: (isDark ? Colors.grey[400] : Colors.grey[500]),
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
),
);
}
}