Files
wushu/lib/views/profile/settings/user-plan.dart
Developer 2f785d6279 release
2026-03-31 23:31:38 +08:00

703 lines
23 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 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart';
/// 时间: 2026-03-26
/// 功能: 用户体验计划页面
/// 介绍: 展示用户体验计划的收集信息、权益说明,支持加入/退出
/// 最新变化: 新建页面
class UserPlanPage extends StatefulWidget {
const UserPlanPage({super.key});
@override
State<UserPlanPage> createState() => _UserPlanPageState();
}
class _UserPlanPageState extends State<UserPlanPage> {
bool _isJoined = false;
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadUserPlanStatus();
}
Future<void> _loadUserPlanStatus() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
_isJoined = prefs.getBool('user_plan_joined') ?? false;
_isLoading = false;
});
}
Future<void> _toggleUserPlan(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('user_plan_joined', value);
setState(() {
_isJoined = value;
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(value ? '已加入用户体验计划' : '已退出用户体验计划'),
backgroundColor: value ? AppConstants.primaryColor : Colors.grey[600],
),
);
}
}
void _showJoinDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: Row(
children: [
Icon(Icons.volunteer_activism, color: AppConstants.primaryColor),
const SizedBox(width: 8),
const Text('加入用户体验计划'),
],
),
content: const Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'加入后我们将收集以下信息以改善产品体验:',
style: TextStyle(fontWeight: FontWeight.w500),
),
SizedBox(height: 12),
Text('• 软件使用时长'),
Text('• 页面停留时间'),
Text('• 个人喜爱诗词'),
Text('• 分享次数'),
Text('• 设备信息IP归属地、设备型号'),
SizedBox(height: 12),
Text(
'所有数据将匿名化保存,不会公开。',
style: TextStyle(color: Colors.grey, fontSize: 12),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
_toggleUserPlan(true);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.primaryColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('同意并加入'),
),
],
),
);
}
void _showInfoPopup(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: Row(
children: [
Icon(Icons.info, color: AppConstants.primaryColor),
const SizedBox(width: 8),
const Text('温馨提示'),
],
),
content: const Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'即使不加入用户体验计划,部分信息仍可能被收集:',
style: TextStyle(fontWeight: FontWeight.w500),
),
SizedBox(height: 12),
Text('• 运营商可能收集网络连接信息'),
Text('• 手机厂商可能收集软件使用数据'),
Text('• 应用商店可能收集下载安装信息'),
Text('• VPN、抓包等软件可能收集匿名统计数据'),
SizedBox(height: 12),
Text(
'未加入用户体验计划,部分联网数据也会被动上传到软件服务器。',
style: TextStyle(color: Colors.grey, fontSize: 12),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('我知道了'),
),
],
),
);
}
void _showExitDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: const Row(
children: [
Icon(Icons.exit_to_app, color: Colors.red),
SizedBox(width: 8),
Text('退出用户体验计划'),
],
),
content: const Text('退出后将不再收集您的使用数据,部分功能可能会受限。确定要退出吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
_toggleUserPlan(false);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('确定退出'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
appBar: AppBar(
title: Text(
'用户体验计划',
style: TextStyle(
color: AppConstants.primaryColor,
fontWeight: FontWeight.bold,
),
),
backgroundColor: Colors.white,
elevation: 0,
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: AppConstants.primaryColor),
onPressed: () => Navigator.of(context).pop(),
),
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildStatusCard(),
const SizedBox(height: 16),
_buildCollectInfoSection(),
const SizedBox(height: 16),
_buildBenefitsSection(),
const SizedBox(height: 16),
_buildPrivacyNotice(),
const SizedBox(height: 24),
_buildActionButton(),
const SizedBox(height: 16),
_buildBottomIndicator(),
],
),
);
}
Widget _buildStatusCard() {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppConstants.primaryColor,
AppConstants.primaryColor.withBlue(180),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: AppConstants.primaryColor.withValues(alpha: 0.3),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: [
Icon(
_isJoined ? Icons.verified : Icons.volunteer_activism_outlined,
size: 48,
color: Colors.white,
),
const SizedBox(height: 12),
Text(
_isJoined ? '已加入用户体验计划' : '加入用户体验计划',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
_isJoined ? '感谢您的支持,让我们一起变得更好' : '参与产品改进,享受更多权益',
style: TextStyle(
fontSize: 14,
color: Colors.white.withValues(alpha: 0.9),
),
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_isJoined ? Icons.check_circle : Icons.info_outline,
size: 16,
color: Colors.white,
),
const SizedBox(width: 6),
Text(
_isJoined ? '当前状态:已加入' : '当前状态:未加入',
style: const TextStyle(fontSize: 13, color: Colors.white),
),
],
),
),
],
),
);
}
Widget _buildCollectInfoSection() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.data_usage,
color: Colors.orange[700],
size: 20,
),
),
const SizedBox(width: 12),
const Text(
'收集的信息',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
),
const Divider(height: 1),
_buildInfoItem(Icons.timer, '软件使用时长', '统计每日使用时长及时段'),
_buildInfoItem(Icons.visibility, '不同页面停留时间', '了解用户关注的页面'),
_buildInfoItem(Icons.favorite, '个人喜爱诗词', '推荐更精准的内容'),
_buildInfoItem(Icons.error, '错误信息', '优化软件功能体验'),
_buildInfoItem(Icons.location_on, 'IP归属地', '群体用户画像生成分析'),
_buildInfoItem(Icons.devices, '设备信息', '适配更多设备型号'),
Padding(
padding: const EdgeInsets.all(16),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.05),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(Icons.security, size: 16, color: Colors.blue[700]),
const SizedBox(width: 8),
Expanded(
child: Text(
'所有数据匿名化保存,不会公开,且定期删除',
style: TextStyle(fontSize: 12, color: Colors.blue[700]),
),
),
],
),
),
),
],
),
);
}
Widget _buildInfoItem(IconData icon, String title, String subtitle) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Icon(icon, size: 20, color: Colors.grey[600]),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 2),
Text(
subtitle,
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
),
],
),
),
],
),
);
}
Widget _buildBenefitsSection() {
final benefits = [
{'icon': Icons.how_to_vote, 'title': '参与投票', 'desc': '对产品功能进行投票'},
{'icon': Icons.bar_chart, 'title': '查看统计数据', 'desc': '查看全站使用统计'},
{'icon': Icons.edit_note, 'title': '开放投稿', 'desc': '投稿您的诗词作品'},
{'icon': Icons.science, 'title': '体验Beta功能', 'desc': '内测版本抢先体验'},
{'icon': Icons.bug_report, 'title': '刷新次数', 'desc': '获得更多api调用次数'},
{'icon': Icons.card_giftcard, 'title': '专属显示', 'desc': '获得不同的显示效果'},
];
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.green.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.card_membership,
color: Colors.green[700],
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'加入后的权益',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2),
Row(
children: [
Text(
'体验更多受限制的功能',
style: TextStyle(
fontSize: 11,
color: Colors.grey[500],
),
),
const SizedBox(width: 4),
GestureDetector(
onTap: () => _showInfoPopup(context),
child: Icon(
Icons.info_outline,
size: 14,
color: Colors.grey[500],
),
),
],
),
],
),
),
],
),
),
const Divider(height: 1),
Padding(
padding: const EdgeInsets.all(12),
child: GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 2.5,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: benefits.length,
itemBuilder: (context, index) {
final benefit = benefits[index];
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Colors.grey.withValues(alpha: 0.2),
),
),
child: Row(
children: [
Icon(
benefit['icon'] as IconData,
size: 18,
color: AppConstants.primaryColor,
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
benefit['title'] as String,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
Text(
benefit['desc'] as String,
style: TextStyle(
fontSize: 10,
color: Colors.grey[500],
),
),
],
),
),
],
),
);
},
),
),
],
),
);
}
Widget _buildPrivacyNotice() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.purple.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.privacy_tip,
color: Colors.purple[700],
size: 20,
),
),
const SizedBox(width: 12),
const Text(
'隐私保护说明',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 16),
_buildPrivacyItem('🔒 数据匿名化处理,无法追溯到个人'),
_buildPrivacyItem('🛡️ 数据仅用于产品改进,不会出售'),
_buildPrivacyItem('📋 您可以随时退出计划'),
_buildPrivacyItem('🗑️ 部分数据将被删除'),
],
),
);
}
Widget _buildPrivacyItem(String text) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
children: [
const SizedBox(width: 4),
Expanded(
child: Text(
text,
style: TextStyle(
fontSize: 13,
color: Colors.grey[700],
height: 1.4,
),
),
),
],
),
);
}
Widget _buildActionButton() {
return Container(
width: double.infinity,
height: 50,
decoration: BoxDecoration(
gradient: _isJoined
? null
: LinearGradient(
colors: [
AppConstants.primaryColor,
AppConstants.primaryColor.withBlue(180),
],
),
color: _isJoined ? Colors.grey[300] : null,
borderRadius: BorderRadius.circular(12),
boxShadow: _isJoined
? null
: [
BoxShadow(
color: AppConstants.primaryColor.withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: ElevatedButton(
onPressed: () {
if (_isJoined) {
_showExitDialog();
} else {
_showJoinDialog();
}
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
_isJoined ? Icons.exit_to_app : Icons.volunteer_activism,
color: _isJoined ? Colors.grey[600] : Colors.white,
),
const SizedBox(width: 8),
Text(
_isJoined ? '退出用户体验计划' : '加入用户体验计划',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _isJoined ? Colors.grey[600] : Colors.white,
),
),
],
),
),
);
}
Widget _buildBottomIndicator() {
return Container(
padding: const EdgeInsets.symmetric(vertical: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width: 40, height: 1, color: Colors.grey[300]),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'到底了',
style: TextStyle(fontSize: 12, color: Colors.grey[400]),
),
),
Container(width: 40, height: 1, color: Colors.grey[300]),
],
),
);
}
}