Initial commit: Flutter 无书应用项目
This commit is contained in:
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user