Initial commit: Flutter 无书应用项目
This commit is contained in:
421
lib/views/profile/settings/app_fun.dart
Normal file
421
lib/views/profile/settings/app_fun.dart
Normal file
@@ -0,0 +1,421 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
import './widgets.dart';
|
||||
import '../../home/home-load.dart';
|
||||
|
||||
/// 时间: 2026-03-26
|
||||
/// 功能: 功能设置页面
|
||||
/// 介绍: 应用功能开关和设置项管理
|
||||
/// 最新变化: 添加自动刷新功能
|
||||
|
||||
class AppFunSettingsPage extends StatefulWidget {
|
||||
const AppFunSettingsPage({super.key});
|
||||
|
||||
@override
|
||||
State<AppFunSettingsPage> createState() => _AppFunSettingsPageState();
|
||||
}
|
||||
|
||||
class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
|
||||
bool _autoRefreshEnabled = false;
|
||||
bool _debugInfoEnabled = false;
|
||||
bool _soundEnabled = true;
|
||||
bool _vibrationEnabled = true;
|
||||
bool _darkModeEnabled = false;
|
||||
bool _notificationEnabled = true;
|
||||
int _cacheSize = 128;
|
||||
|
||||
static const String _autoRefreshKey = 'auto_refresh_enabled';
|
||||
static const String _debugInfoKey = 'debug_info_enabled';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSettings();
|
||||
}
|
||||
|
||||
Future<void> _loadSettings() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_autoRefreshEnabled = prefs.getBool(_autoRefreshKey) ?? false;
|
||||
_debugInfoEnabled = prefs.getBool(_debugInfoKey) ?? false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _setAutoRefresh(bool value) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_autoRefreshKey, value);
|
||||
await AutoRefreshManager().setEnabled(value);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_autoRefreshEnabled = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _setDebugInfo(bool value) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_debugInfoKey, value);
|
||||
await DebugInfoManager().setEnabled(value);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_debugInfoEnabled = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@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: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
_buildSettingsGroup('基础功能', [
|
||||
_buildSwitchItem(
|
||||
'自动刷新',
|
||||
'首页诗句自动刷新(5s) ',
|
||||
Icons.refresh,
|
||||
_autoRefreshEnabled,
|
||||
_setAutoRefresh,
|
||||
),
|
||||
_buildSwitchItem(
|
||||
'调式信息',
|
||||
'开启后可加载更多信息',
|
||||
Icons.bug_report,
|
||||
_debugInfoEnabled,
|
||||
_setDebugInfo,
|
||||
),
|
||||
_buildSwitchItem(
|
||||
'预加载',
|
||||
'开启后优先使用本地缓存,\n减少与服务器的通信次数',
|
||||
//关闭后,优先使用云端数据,无延迟,但刷新缓慢
|
||||
Icons.notifications_active,
|
||||
_notificationEnabled,
|
||||
(value) => setState(() => _notificationEnabled = value),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSettingsGroup('显示设置', [
|
||||
_buildSwitchItem(
|
||||
'Tap沉浸光感',
|
||||
'开启后底栏显示类iOS26 风格\nbeta实验功能',
|
||||
Icons.dark_mode,
|
||||
_darkModeEnabled,
|
||||
(value) => setState(() => _darkModeEnabled = value),
|
||||
),
|
||||
_buildFontSliderItem(),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSettingsGroup('交互反馈', [
|
||||
_buildSwitchItem(
|
||||
'全局Tips开关',
|
||||
'显示一些使用技巧',
|
||||
Icons.volume_up,
|
||||
_soundEnabled,
|
||||
(value) => setState(() => _soundEnabled = value),
|
||||
),
|
||||
_buildSwitchItem(
|
||||
'声音反馈',
|
||||
'操作时播放提示音',
|
||||
Icons.volume_up,
|
||||
_soundEnabled,
|
||||
(value) => setState(() => _soundEnabled = value),
|
||||
),
|
||||
_buildSwitchItem(
|
||||
'震动反馈',
|
||||
'操作时震动提示',
|
||||
Icons.vibration,
|
||||
_vibrationEnabled,
|
||||
(value) => setState(() => _vibrationEnabled = value),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
_buildSettingsGroup('高级设置', [
|
||||
_buildActionItem(
|
||||
'Beta开关 关闭软件',
|
||||
'开发者选项',
|
||||
Icons.developer_mode,
|
||||
() => _showSnackBar('调试模式开发中'),
|
||||
),
|
||||
_buildActionItem(
|
||||
'重置设置',
|
||||
'恢复默认设置',
|
||||
Icons.restore,
|
||||
() => _showResetDialog(),
|
||||
),
|
||||
_buildActionItem(
|
||||
'调试模式',
|
||||
'开发者选项',
|
||||
Icons.developer_mode,
|
||||
() => _showSnackBar('调试模式开发中'),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 32),
|
||||
_buildVersionInfo(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSettingsGroup(String title, List<Widget> items) {
|
||||
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.fromLTRB(16, 16, 16, 8),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
...items,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSwitchItem(
|
||||
String title,
|
||||
String subtitle,
|
||||
IconData icon,
|
||||
bool value,
|
||||
ValueChanged<bool> onChanged,
|
||||
) {
|
||||
return ListTile(
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(icon, color: AppConstants.primaryColor, size: 20),
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
subtitle,
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
trailing: Switch(
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
activeThumbColor: AppConstants.primaryColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFontSliderItem() {
|
||||
return ListTile(
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(Icons.widgets, color: AppConstants.primaryColor, size: 20),
|
||||
),
|
||||
title: const Text(
|
||||
'桌面卡片',
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
'使用帮助',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
trailing: Icon(Icons.chevron_right, color: Colors.grey[400]),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const WidgetsPage()),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCacheItem() {
|
||||
return ListTile(
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(Icons.storage, color: AppConstants.primaryColor, size: 20),
|
||||
),
|
||||
title: const Text(
|
||||
'缓存大小',
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
'$_cacheSize MB',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
trailing: Icon(Icons.chevron_right, color: Colors.grey[400]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionItem(
|
||||
String title,
|
||||
String subtitle,
|
||||
IconData icon,
|
||||
VoidCallback onTap,
|
||||
) {
|
||||
return ListTile(
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(icon, color: AppConstants.primaryColor, size: 20),
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
subtitle,
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
trailing: Icon(Icons.chevron_right, color: Colors.grey[400]),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildVersionInfo() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
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(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.settings_suggest,
|
||||
size: 40,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
AppConstants.appName,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'版本 ${AppConstants.appVersion}',
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey[600]),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSnackBar(String message) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
void _showClearCacheDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('清除缓存'),
|
||||
content: const Text('确定要清除所有缓存数据吗?这不会影响您的笔记和收藏。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
setState(() {
|
||||
_cacheSize = 0;
|
||||
});
|
||||
_showSnackBar('缓存已清除');
|
||||
},
|
||||
child: Text('确定', style: TextStyle(color: Colors.red[400])),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showResetDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('重置设置'),
|
||||
content: const Text('确定要恢复默认设置吗?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
await _setAutoRefresh(false);
|
||||
await _setDebugInfo(false);
|
||||
setState(() {
|
||||
_soundEnabled = true;
|
||||
_vibrationEnabled = true;
|
||||
_darkModeEnabled = false;
|
||||
_notificationEnabled = true;
|
||||
});
|
||||
_showSnackBar('已恢复默认设置');
|
||||
},
|
||||
child: Text('确定', style: TextStyle(color: Colors.red[400])),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
574
lib/views/profile/settings/learn-us.dart
Normal file
574
lib/views/profile/settings/learn-us.dart
Normal file
@@ -0,0 +1,574 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
|
||||
/// 时间: 2026-03-26
|
||||
/// 功能: 了解我们页面
|
||||
/// 介绍: 展示开发者信息、团队信息、官网和备案号
|
||||
/// 最新变化: 新建页面
|
||||
|
||||
class LearnUsPage extends StatelessWidget {
|
||||
const LearnUsPage({super.key});
|
||||
|
||||
@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: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
_buildHeaderCard(),
|
||||
const SizedBox(height: 16),
|
||||
_buildOfficialSiteCard(),
|
||||
const SizedBox(height: 16),
|
||||
_buildDeveloperCard(),
|
||||
const SizedBox(height: 16),
|
||||
_buildTeamCard(),
|
||||
const SizedBox(height: 16),
|
||||
_buildIcpCard(context),
|
||||
const SizedBox(height: 24),
|
||||
_buildBottomIndicator(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeaderCard() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: const [
|
||||
Color.fromARGB(255, 143, 73, 228),
|
||||
Color(0xFF6200EE),
|
||||
Color(0xFF3700B3),
|
||||
],
|
||||
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: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 70,
|
||||
height: 70,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: const Center(
|
||||
child: Text('📖', style: TextStyle(fontSize: 36)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'情景诗词',
|
||||
style: TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'在诗词里旅行,在文化中生长',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withValues(alpha: 0.85),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.code,
|
||||
size: 14,
|
||||
color: Colors.white.withValues(alpha: 0.9),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'版本 ${AppConstants.appVersion}',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOfficialSiteCard() {
|
||||
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.blue.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.language,
|
||||
color: Colors.blue[700],
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
'官方网站',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'访问我们的官方网站了解更多信息',
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[50],
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: Colors.grey.withValues(alpha: 0.2),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.link,
|
||||
size: 16,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'https://*****.github.io',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDeveloperCard() {
|
||||
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.business,
|
||||
color: Colors.green[700],
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
'开发者',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppConstants.primaryColor.withValues(alpha: 0.1),
|
||||
AppConstants.primaryColor.withValues(alpha: 0.05),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Center(
|
||||
child: Text('🏢', style: TextStyle(fontSize: 24)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'微风暴工作室',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'专注诗词文化传播',
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey[600]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTeamCard() {
|
||||
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.purple.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(Icons.group, color: Colors.purple[700], size: 20),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
'团队信息(Team)',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
_buildTeamMember('💻', '程序设计', '无书的书🤡', '尽毕生所学,取天下之诗集,只为逗她一笑'),
|
||||
_buildTeamMember('🎨', 'UI/UX/Testing', 'Ayk', '....'),
|
||||
_buildTeamMember('⚙️', '后端', '伯乐不相马', '真的吗,还是做不到吗?'),
|
||||
_buildTeamMember('🔧', '技术支持', '闲言app', '闲言app原班人马打造'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTeamMember(
|
||||
String emoji,
|
||||
String role,
|
||||
String name,
|
||||
String signature,
|
||||
) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(emoji, style: const TextStyle(fontSize: 20)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
role,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
name,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
signature,
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIcpCard(BuildContext context) {
|
||||
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.verified_user,
|
||||
color: Colors.orange[700],
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
'ICP备案信息',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'APP核准备案号',
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
InkWell(
|
||||
onTap: () => _copyIcpNumber(context),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[50],
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: AppConstants.primaryColor.withValues(alpha: 0.3),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.content_copy,
|
||||
size: 16,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'滇ICP备2022000863号-15A',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'💡 点击备案号可复制',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _copyIcpNumber(BuildContext context) {
|
||||
Clipboard.setData(const ClipboardData(text: '滇ICP备2022000863号-15A'));
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: const Row(
|
||||
children: [
|
||||
Icon(Icons.check_circle, color: Colors.white, size: 20),
|
||||
SizedBox(width: 8),
|
||||
Text('备案号已复制到剪贴板'),
|
||||
],
|
||||
),
|
||||
backgroundColor: AppConstants.primaryColor,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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]),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
1048
lib/views/profile/settings/offline-data.dart
Normal file
1048
lib/views/profile/settings/offline-data.dart
Normal file
File diff suppressed because it is too large
Load Diff
532
lib/views/profile/settings/privacy.dart
Normal file
532
lib/views/profile/settings/privacy.dart
Normal file
@@ -0,0 +1,532 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
|
||||
/// 时间: 2026-03-26
|
||||
/// 功能: 隐私政策与软件协议页面
|
||||
/// 介绍: 展示应用的隐私政策和用户协议
|
||||
/// 最新变化: 将协议内容抽取成公共组件,支持其他页面调用
|
||||
|
||||
/// 公共协议内容组件 - 隐私政策
|
||||
class PrivacyPolicyContent extends StatelessWidget {
|
||||
const PrivacyPolicyContent({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('关于情景诗词与隐私的声明'),
|
||||
_buildUpdateDate('2026.3.26'),
|
||||
const SizedBox(height: 24),
|
||||
_buildParagraph(
|
||||
'情景诗词 是由 *****工作室 (以下简称"我们")为您提供的,用于在诗词里旅行,在文化中生长的应用。本隐私声明由我们为处理您的个人信息而制定。',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'我们非常重视您的个人信息和隐私保护,将会按照法律要求和业界成熟的安全标准,为您的个人信息提供相应的安全保护措施。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('1. 我们如何收集和使用您的个人信息'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'我们仅在有合法性基础的情形下才会使用您的个人信息。根据适用的法律,我们可能会基于您的同意、为履行/订立您与我们的合同所必需、履行法定义务所必需等合法性基础,使用您的个人信息。',
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildSubSectionTitle('1.1 基于履行法定义务或其他法律法规规定的情形'),
|
||||
const SizedBox(height: 12),
|
||||
_buildParagraph('为了实现应用功能,在获取您的同意后我们需要收集您的以下信息:'),
|
||||
const SizedBox(height: 12),
|
||||
_buildBulletPoint('本地笔记数据,用于保存您创建的诗词笔记'),
|
||||
_buildBulletPoint('点赞记录,用于展示您收藏的诗词'),
|
||||
_buildBulletPoint('历史记录,用于记录您浏览过的诗词'),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('2. 设备权限调用'),
|
||||
const SizedBox(height: 16),
|
||||
_buildPermissionItem('存储权限', '用于保存和读取您的笔记、收藏等本地数据'),
|
||||
_buildPermissionItem('网络权限', '用于获取诗词内容和更新应用信息'),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('3. 管理您的个人信息'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'如您对您的数据主体权利有进一步要求或存在任何疑问、意见或建议,可通过本声明中"如何联系我们"章节中所述方式与我们取得联系,并行使您的相关权利。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('4. 信息存储地点及期限'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph('4.1 我们承诺,除法律法规另有规定外,我们对您的信息的保存期限应当为实现处理目的所必要的最短时间。'),
|
||||
const SizedBox(height: 12),
|
||||
_buildParagraph('4.2 上述信息将会传输并保存至中国境内的服务器。'),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('5. 如何联系我们'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph('您可通过以下方式联系我们,并行使您的相关权利,我们会尽快回复。'),
|
||||
const SizedBox(height: 12),
|
||||
_buildContactInfo('开发者', '*****工作室'),
|
||||
_buildContactInfo('地址', '云南昆明'),
|
||||
_buildContactInfo('邮箱', '********@outlook.com'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'如果您对我们的回复不满意,特别是当个人信息处理行为损害了您的合法权益时,您还可以通过向有管辖权的人民法院提起诉讼、向行业自律协会或政府相关管理机构投诉等外部途径进行解决。您也可以向我们了解可能适用的相关投诉途径的信息。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildEffectiveDate('2026年3月26日'),
|
||||
_buildBottomIndicator(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubSectionTitle(String title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildParagraph(String text) {
|
||||
return Text(
|
||||
text,
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey[700], height: 1.6),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBulletPoint(String text) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 6,
|
||||
height: 6,
|
||||
margin: const EdgeInsets.only(top: 6, left: 8, right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey[700],
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPermissionItem(String title, String desc) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.grey.withValues(alpha: 0.2)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(desc, style: TextStyle(fontSize: 13, color: Colors.grey[600])),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContactInfo(String label, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 80,
|
||||
child: Text(
|
||||
'$label:',
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey[700]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildUpdateDate(String date) {
|
||||
return Text(
|
||||
'更新日期:$date',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEffectiveDate(String date) {
|
||||
return Text(
|
||||
'生效日期:$date',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
|
||||
);
|
||||
}
|
||||
|
||||
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]),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 公共协议内容组件 - 用户协议
|
||||
class UserAgreementContent extends StatelessWidget {
|
||||
const UserAgreementContent({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('用户协议'),
|
||||
_buildEffectiveDate('2026-03-26'),
|
||||
const SizedBox(height: 24),
|
||||
_buildParagraph(
|
||||
'欢迎使用 情景诗词(以下简称"本App")。本用户协议由个人开发者 *****工作室 制定。用户在下载、安装、注册、登录、使用本App服务前,应当仔细阅读并充分理解本协议内容。用户开始使用本App,即视为同意本协议全部条款。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('一、协议适用范围'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'本协议适用于用户与开发者 *****工作室 之间,关于用户使用 情景诗词 产品及服务所建立的权利义务关系。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('二、服务内容'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'情景诗词 主要提供诗词浏览、收藏、笔记记录、历史记录等功能。具体服务内容以应用内实际展示为准,开发者可根据产品运营情况进行功能优化、升级和调整。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('三、账号与安全'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'您应当确保使用本App时遵守相关法律法规。本App目前不需要注册账号,您的本地数据存储在您的设备上,请妥善保管您的设备。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('四、用户行为规范'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'用户在使用本App过程中,应当遵守中华人民共和国法律法规,不得利用本App从事违法违规行为,不得发布或传播侵犯他人合法权益的内容,不得实施影响本App安全和稳定运行的行为。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('五、知识产权'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'用户知悉并认可,本App(含程序代码、界面设计、功能及其更新、扩展、修复版本)相关权利均归开发者或合法权利人所有。',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildParagraph(
|
||||
'本条所称"知识产权"包括但不限于著作权、商标权、专利权、商业秘密及反不正当竞争法等法律法规项下的一切相关权利。',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildParagraph(
|
||||
'除法律法规另有规定或开发者书面授权外,用户仅获得基于本协议的个人、非独占、不可转让、可撤销的使用许可;用户不得对本App实施复制、修改、改编、翻译、出租、出借、出售、传播、反向工程、反编译、反汇编,或以其他方式尝试获取源代码。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('六、责任限制'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'在法律允许范围内,对于因网络异常、设备故障、不可抗力、第三方服务异常等原因导致的服务中断或数据损失,开发者将在能力范围内及时修复或补救,但不承担超出法定范围的责任。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('七、协议更新'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'开发者有权根据业务变化、监管要求或法律法规变化更新本协议。更新后的协议将在应用内公示,用户继续使用本App即视为接受更新后的协议内容。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('八、适用法律与争议解决'),
|
||||
const SizedBox(height: 16),
|
||||
_buildParagraph(
|
||||
'本协议适用中华人民共和国法律。因本协议引发的争议,双方应先友好协商;协商不成的,提交被告住所地有管辖权的人民法院处理。',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildSectionTitle('九、联系方式'),
|
||||
const SizedBox(height: 16),
|
||||
_buildContactInfo('开发者', '*****工作室'),
|
||||
_buildContactInfo('应用名称', '情景诗词'),
|
||||
_buildContactInfo('联系邮箱', '********@outlook.com'),
|
||||
const SizedBox(height: 24),
|
||||
_buildBottomIndicator(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildParagraph(String text) {
|
||||
return Text(
|
||||
text,
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey[700], height: 1.6),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContactInfo(String label, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 80,
|
||||
child: Text(
|
||||
'$label:',
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey[700]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEffectiveDate(String date) {
|
||||
return Text(
|
||||
'生效日期:$date',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
|
||||
);
|
||||
}
|
||||
|
||||
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]),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 隐私政策页面(完整页面)
|
||||
class PrivacyPage extends StatefulWidget {
|
||||
const PrivacyPage({super.key});
|
||||
|
||||
@override
|
||||
State<PrivacyPage> createState() => _PrivacyPageState();
|
||||
}
|
||||
|
||||
class _PrivacyPageState extends State<PrivacyPage>
|
||||
with TickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: 2, vsync: this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@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,
|
||||
bottom: TabBar(
|
||||
controller: _tabController,
|
||||
labelColor: AppConstants.primaryColor,
|
||||
unselectedLabelColor: Colors.grey[600],
|
||||
indicatorColor: AppConstants.primaryColor,
|
||||
indicatorWeight: 2,
|
||||
tabs: const [
|
||||
Tab(text: '隐私政策'),
|
||||
Tab(text: '用户协议'),
|
||||
],
|
||||
),
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back, color: AppConstants.primaryColor),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.link, color: AppConstants.primaryColor),
|
||||
onPressed: () => _showOnlineLinkDialog(),
|
||||
tooltip: '在线版本',
|
||||
),
|
||||
],
|
||||
),
|
||||
body: TabBarView(
|
||||
controller: _tabController,
|
||||
children: const [PrivacyPolicyContent(), UserAgreementContent()],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showOnlineLinkDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(Icons.public, color: AppConstants.primaryColor),
|
||||
const SizedBox(width: 8),
|
||||
const Text('在线版本'),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('您可以访问以下链接查看最新版本的协议:', style: TextStyle(fontSize: 14)),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.grey[300]!),
|
||||
),
|
||||
child: SelectableText(
|
||||
'https://*****.github.io/privacy',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: AppConstants.primaryColor,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'💡 点击下方按钮复制链接,在浏览器中打开查看',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('关闭'),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
const ClipboardData(text: 'https://*****.github.io/privacy'),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: const Text('链接已复制到剪贴板'),
|
||||
backgroundColor: AppConstants.primaryColor,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.copy, size: 18),
|
||||
label: const Text('复制链接'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppConstants.primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
702
lib/views/profile/settings/user-plan.dart
Normal file
702
lib/views/profile/settings/user-plan.dart
Normal file
@@ -0,0 +1,702 @@
|
||||
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': '参与内测版本体验'},
|
||||
{'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]),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
309
lib/views/profile/settings/widgets.dart
Normal file
309
lib/views/profile/settings/widgets.dart
Normal file
@@ -0,0 +1,309 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../constants/app_constants.dart';
|
||||
|
||||
/// 时间: 2026-03-27
|
||||
/// 功能: 桌面小卡片设置页面
|
||||
/// 介绍: 用于配置和管理桌面小卡片
|
||||
class WidgetsPage extends StatefulWidget {
|
||||
const WidgetsPage({super.key});
|
||||
|
||||
@override
|
||||
State<WidgetsPage> createState() => _WidgetsPageState();
|
||||
}
|
||||
|
||||
class _WidgetsPageState extends State<WidgetsPage> {
|
||||
bool _enableWidget = true;
|
||||
bool _showWeather = true;
|
||||
bool _showQuote = true;
|
||||
bool _showTime = true;
|
||||
int _updateInterval = 5; // 分钟
|
||||
|
||||
final List<int> _intervalOptions = [1, 5, 10, 30, 60];
|
||||
|
||||
@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: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
_buildSettingsGroup('卡片状态', [
|
||||
_buildSwitchItem(
|
||||
'启用桌面卡片',
|
||||
'在桌面上显示诗词卡片',
|
||||
Icons.widgets,
|
||||
_enableWidget,
|
||||
(value) => setState(() => _enableWidget = value),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSettingsGroup('显示内容', [
|
||||
_buildSwitchItem(
|
||||
'显示天气',
|
||||
'在卡片上显示当前天气',
|
||||
Icons.cloud,
|
||||
_showWeather,
|
||||
(value) => setState(() => _showWeather = value),
|
||||
),
|
||||
_buildSwitchItem(
|
||||
'显示诗句',
|
||||
'在卡片上显示随机诗句',
|
||||
Icons.edit_document,
|
||||
_showQuote,
|
||||
(value) => setState(() => _showQuote = value),
|
||||
),
|
||||
_buildSwitchItem(
|
||||
'显示时间',
|
||||
'在卡片上显示当前时间',
|
||||
Icons.access_time,
|
||||
_showTime,
|
||||
(value) => setState(() => _showTime = value),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSettingsGroup('更新设置', [_buildIntervalItem()]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSettingsGroup('预览', [_buildPreviewCard()]),
|
||||
const SizedBox(height: 32),
|
||||
_buildActionButton(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSettingsGroup(String title, List<Widget> items) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(5),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
...items,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSwitchItem(
|
||||
String title,
|
||||
String subtitle,
|
||||
IconData icon,
|
||||
bool value,
|
||||
ValueChanged<bool> onChanged,
|
||||
) {
|
||||
return ListTile(
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor.withAlpha(10),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(icon, color: AppConstants.primaryColor, size: 20),
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
subtitle,
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
trailing: Switch(
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
activeThumbColor: AppConstants.primaryColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIntervalItem() {
|
||||
return ListTile(
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor.withAlpha(10),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(Icons.refresh, color: AppConstants.primaryColor, size: 20),
|
||||
),
|
||||
title: const Text(
|
||||
'更新间隔',
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(
|
||||
'每 $_updateInterval 分钟更新一次',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||
),
|
||||
trailing: SizedBox(
|
||||
width: 150,
|
||||
child: SegmentedButton<int>(
|
||||
segments: _intervalOptions.map((interval) {
|
||||
return ButtonSegment(value: interval, label: Text('$interval'));
|
||||
}).toList(),
|
||||
selected: {_updateInterval},
|
||||
onSelectionChanged: (Set<int> selection) {
|
||||
setState(() {
|
||||
_updateInterval = selection.first;
|
||||
});
|
||||
},
|
||||
style: ButtonStyle(
|
||||
visualDensity: VisualDensity.compact,
|
||||
backgroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return AppConstants.primaryColor;
|
||||
}
|
||||
return Colors.grey[200];
|
||||
}),
|
||||
foregroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return Colors.white;
|
||||
}
|
||||
return Colors.black87;
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPreviewCard() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.primaryColor.withAlpha(90),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(10),
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (_showTime) ...[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
if (_showQuote) ...[
|
||||
Text(
|
||||
'床前明月光,疑是地上霜。',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'举头望明月,低头思故乡。',
|
||||
style: const TextStyle(color: Colors.white, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'— 李白《静夜思》',
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 12),
|
||||
),
|
||||
],
|
||||
if (_showWeather) ...[
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Icon(Icons.sunny, color: Colors.white, size: 16),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'25°C',
|
||||
style: const TextStyle(color: Colors.white, fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButton() {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('桌面卡片设置已保存'),
|
||||
backgroundColor: AppConstants.primaryColor,
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppConstants.primaryColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
child: const Text(
|
||||
'应用设置',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user