Initial commit: Flutter 无书应用项目

This commit is contained in:
Developer
2026-03-30 02:35:31 +08:00
commit 9175ff9905
566 changed files with 103261 additions and 0 deletions

View 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),
),
),
),
],
),
);
}
}