Files
wushu/lib/views/profile/guide/sp-guide.dart
Developer 888363785b 重构
2026-03-31 03:48:14 +08:00

1236 lines
40 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart';
import '../../../config/app_config.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();
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 WillPopScope(
// 拦截返回按钮,如果不是从设置页面进入,则阻止返回
onWillPop: () async {
if (widget.fromSettings) {
return true; // 允许返回
}
// 首次启动时,阻止返回,必须完成引导流程
return false;
},
child: Scaffold(
backgroundColor: Colors.white,
appBar: _buildAppBar(),
body: Stack(
children: [
PageView(
controller: _pageController,
scrollDirection: Axis.vertical,
physics: const PageScrollPhysics(),
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
children: [
_buildWelcomePage(),
_buildPrivacyPage(),
_buildFeaturePage(),
],
),
_buildPageIndicator(),
_buildBottomNavigation(),
],
),
),
);
}
Widget _buildPageIndicator() {
// 根据当前页面计算进度条位置
// 第1页屏幕上方靠近顶部
// 第2页屏幕中间不变
// 第3页屏幕下方靠近底部
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: Colors.white.withValues(alpha: 0.9),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: 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)
: 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() {
return AppBar(
title: Text(
_pageTitles[_currentPage],
style: TextStyle(
color: AppConstants.primaryColor,
fontWeight: FontWeight.bold,
),
),
backgroundColor: 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() {
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),
const Text(
'欢迎使用',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: 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: Colors.grey[50],
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
_buildWelcomeItem('🌟', '在诗词里旅行,在文化中生长'),
const SizedBox(height: 16),
_buildWelcomeItem('📚', '海量诗词,随心阅读'),
const SizedBox(height: 16),
_buildWelcomeItem('❤️', '收藏喜爱,记录感悟'),
const SizedBox(height: 16),
_buildWelcomeItem('🌙', '每日推荐,发现美好'),
],
),
),
const SizedBox(height: 40),
Container(
margin: const EdgeInsets.only(left: 16),
height: 1,
width: double.infinity,
color: Colors.grey[300],
),
const SizedBox(height: 16),
Text(
'向上滑动继续 ↓',
style: TextStyle(fontSize: 14, color: 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),
_buildShowGuideCheckbox(),
],
),
],
),
);
}
Widget _buildWelcomeItem(String emoji, String text) {
return Row(
children: [
Text(emoji, style: const TextStyle(fontSize: 20)),
const SizedBox(width: 12),
Expanded(
child: Text(
text,
style: const TextStyle(fontSize: 15, color: Colors.black87),
),
),
],
);
}
Widget _buildShowGuideCheckbox() {
return GestureDetector(
onTap: () {
_toggleShowGuide(!_showGuideOnStartup);
_showGuideStatusPopup();
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Icon(
_showGuideOnStartup ? Icons.check_box : Icons.check_box_outline_blank,
color: _showGuideOnStartup
? AppConstants.primaryColor
: Colors.grey[400],
size: 24,
),
),
);
}
void _showGuideStatusPopup() {
showDialog(
context: context,
builder: (context) => Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: 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
: Colors.grey[600],
size: 24,
),
const SizedBox(width: 8),
const Text(
'通知设置',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: _showGuideOnStartup
? AppConstants.primaryColor.withValues(alpha: 0.1)
: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Icon(
_showGuideOnStartup
? Icons.notifications_active
: Icons.notifications_off,
color: _showGuideOnStartup
? AppConstants.primaryColor
: Colors.grey[500],
size: 40,
),
const SizedBox(height: 12),
Text(
_showGuideOnStartup ? '已开启' : '已关闭',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: _showGuideOnStartup
? AppConstants.primaryColor
: Colors.grey[700],
),
),
const SizedBox(height: 8),
Text(
_showGuideOnStartup ? '下次启动时将显示欢迎页' : '下次启动时将不显示欢迎页',
style: TextStyle(fontSize: 14, color: 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() {
_initTabController();
return Stack(
children: [
Column(
children: [
Container(
color: Colors.white,
child: TabBar(
controller: _tabController!,
labelColor: AppConstants.primaryColor,
unselectedLabelColor: 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 监听滚动
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 监听滚动
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: Colors.white,
boxShadow: [
BoxShadow(
color: 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: 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
: Colors.grey[700],
),
),
),
),
],
),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _agreementAccepted
? _nextPage
: _rejectAndExit,
style: ElevatedButton.styleFrom(
backgroundColor: _agreementAccepted
? AppConstants.primaryColor
: Colors.grey[600],
foregroundColor: Colors.white,
disabledBackgroundColor: 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() {
return Padding(
padding: const EdgeInsets.fromLTRB(24, 16, 24, 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Center(
// child: Container(
// width: 80,
// height: 80,
// decoration: BoxDecoration(
// color: Colors.purple.withValues(alpha: 0.1),
// borderRadius: BorderRadius.circular(20),
// ),
// child: const Center(
// child: Text('✨', style: TextStyle(fontSize: 40)),
// ),
// ),
// ),
// const SizedBox(height: 24),
// const Center(
// child: Text(
// '功能介绍',
// style: TextStyle(
// fontSize: 26,
// fontWeight: FontWeight.bold,
// color: Colors.black87,
// ),
// ),
// ),
// const SizedBox(height: 8),
Center(
child: Text(
'探索丰富的诗词世界',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
),
const SizedBox(height: 16),
Column(
children: [
Row(
children: [
Expanded(
child: _buildFeatureCard(
'🏠 首页',
'每日推荐精选诗词',
Icons.home,
Colors.blue,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildFeatureCard(
'🔍 发现',
'浏览排行榜、分类',
Icons.explore,
Colors.green,
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: _buildFeatureCard(
'❤️ 收藏',
'点赞收藏、记笔记',
Icons.favorite,
Colors.red,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildFeatureCard(
'👤 个人',
'管理数据、设置',
Icons.person,
Colors.purple,
),
),
],
),
],
),
const SizedBox(height: 20),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
color: 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,
activeColor: 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,
) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.withValues(alpha: 0.2)),
boxShadow: [
BoxShadow(
color: 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: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
const SizedBox(height: 2),
Text(
desc,
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
height: 1.3,
),
),
],
),
);
}
Widget _buildBottomNavigation() {
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,
)
: const SizedBox(width: 100),
_currentPage < _totalPages - 1
? _buildNavButton(
icon: Icons.arrow_forward_ios,
label: '下一页',
onPressed: _currentPage == 1 && !_agreementAccepted
? null
: _nextPage,
)
: _buildNavButton(
icon: Icons.check,
label: '完成',
onPressed: _agreementAccepted ? _finishGuide : null,
),
],
),
),
);
}
Widget _buildNavButton({
required IconData icon,
required String label,
VoidCallback? onPressed,
}) {
return Container(
decoration: BoxDecoration(
color: onPressed != null ? AppConstants.primaryColor : 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 : Colors.grey[500],
size: 20,
),
const SizedBox(width: 8),
Text(
label,
style: TextStyle(
color: onPressed != null ? Colors.white : Colors.grey[500],
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
),
);
}
}