- 全局字体统一(Outfit/DM Sans), 头部/按钮/Toast规范化 - 故事详情页: Genie Suck吸入动画(标题+卡片一起缩小模糊消失) - 书架页: bookPop弹出+粒子效果(三段式动画完整链路) - 音乐页面: 心情卡片emoji换Material图标+彩色圆块横排布局 - 音乐页面: 进度条胶囊宽度对齐, 播放按钮位置修复, 间距均匀化 - 音乐播放: 接入just_audio, 支持播放暂停进度拖拽自动切歌 - 新增: iOS风格毛玻璃Toast, 渐变背景组件, 通知页面 - 阶段总结文档更新 Co-authored-by: Cursor <cursoragent@cursor.com>
599 lines
25 KiB
Dart
599 lines
25 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:airhub_app/theme/design_tokens.dart';
|
||
import 'package:airhub_app/widgets/animated_gradient_background.dart';
|
||
|
||
class SettingsContentPage extends StatelessWidget {
|
||
final String title;
|
||
final String date;
|
||
final List<Widget> children;
|
||
|
||
const SettingsContentPage({
|
||
super.key,
|
||
required this.title,
|
||
required this.date,
|
||
required this.children,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
backgroundColor: Colors.transparent,
|
||
body: Stack(
|
||
children: [
|
||
const AnimatedGradientBackground(),
|
||
Column(
|
||
children: [
|
||
_buildHeader(context),
|
||
Expanded(
|
||
child: ShaderMask(
|
||
shaderCallback: (Rect rect) {
|
||
return const LinearGradient(
|
||
begin: Alignment.topCenter,
|
||
end: Alignment.bottomCenter,
|
||
colors: [Colors.transparent, Colors.black, Colors.black, Colors.transparent],
|
||
stops: [0.0, 0.12, 0.92, 1.0],
|
||
).createShader(rect);
|
||
},
|
||
blendMode: BlendMode.dstIn,
|
||
child: SingleChildScrollView(
|
||
padding: EdgeInsets.only(
|
||
top: 20,
|
||
left: 24,
|
||
right: 24,
|
||
bottom: 40 + MediaQuery.of(context).padding.bottom,
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
...children,
|
||
const SizedBox(height: 40),
|
||
Center(
|
||
child: Text(
|
||
'更新日期:$date',
|
||
style: const TextStyle(
|
||
color: AppColors.textSecondary,
|
||
fontSize: 13,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildHeader(BuildContext context) {
|
||
return Container(
|
||
padding: EdgeInsets.only(
|
||
top: MediaQuery.of(context).padding.top + 20,
|
||
left: AppSpacing.lg,
|
||
right: AppSpacing.lg,
|
||
bottom: AppSpacing.md,
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
GestureDetector(
|
||
onTap: () => Navigator.pop(context),
|
||
child: Container(
|
||
width: 40,
|
||
height: 40,
|
||
decoration: BoxDecoration(
|
||
color: AppColors.iconBtnBg,
|
||
borderRadius: BorderRadius.circular(AppRadius.button),
|
||
),
|
||
child: const Icon(
|
||
Icons.arrow_back_ios_new,
|
||
color: AppColors.textPrimary,
|
||
size: 18,
|
||
),
|
||
),
|
||
),
|
||
Text(title, style: AppTextStyles.title),
|
||
const SizedBox(width: 40), // Balance
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
// Helper methods to generate text styles
|
||
Widget buildSectionTitle(String text) {
|
||
return Padding(
|
||
padding: const EdgeInsets.only(top: 32, bottom: 12),
|
||
child: Text(
|
||
text,
|
||
style: const TextStyle(
|
||
fontSize: 17,
|
||
fontWeight: FontWeight.w700,
|
||
color: AppColors.textPrimary,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget buildParagraph(String text) {
|
||
return Padding(
|
||
padding: const EdgeInsets.only(bottom: 16),
|
||
child: Text(
|
||
text,
|
||
textAlign: TextAlign.justify,
|
||
style: const TextStyle(
|
||
fontSize: 15,
|
||
height: 1.6,
|
||
color: Color(0xFF374151),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget buildBulletList(List<String> items) {
|
||
return Padding(
|
||
padding: const EdgeInsets.only(bottom: 16, left: 20),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: items
|
||
.map(
|
||
(item) => Padding(
|
||
padding: const EdgeInsets.only(bottom: 8),
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
const Text('• ', style: TextStyle(fontSize: 15, height: 1.6)),
|
||
Expanded(
|
||
child: Text(
|
||
item,
|
||
style: const TextStyle(
|
||
fontSize: 15,
|
||
height: 1.6,
|
||
color: Color(0xFF374151),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
)
|
||
.toList(),
|
||
),
|
||
);
|
||
}
|
||
|
||
// Pre-defined pages content factories
|
||
class AgreementPage extends StatelessWidget {
|
||
const AgreementPage({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return SettingsContentPage(
|
||
title: '用户协议',
|
||
date: '2025年1月15日',
|
||
children: [
|
||
buildParagraph('欢迎您使用 Airhub 产品及服务!'),
|
||
buildParagraph(
|
||
'特别提示: 在您开始使用 Airhub 产品(以下简称"本产品")及相关服务之前,请您务必仔细阅读本《用户协议》(以下简称"本协议")。特别是涉及免除或者限制责任的条款、法律适用和争议解决条款等,请您重点阅读。',
|
||
),
|
||
buildSectionTitle('1. 服务说明'),
|
||
buildParagraph(
|
||
'1.1 Airhub Team(以下简称"我们")向用户提供包括但不限于设备连接控制、AI 语音交互、角色记忆存储、云端同步等服务(以下简称"本服务")。',
|
||
),
|
||
buildParagraph('1.2 本服务的具体内容由我们根据实际情况提供,我们有权随时变更、中断或终止部分或全部服务。'),
|
||
buildParagraph('1.3 用户理解并同意,本服务仅供用户个人非商业性质的使用。用户不得利用本服务进行销售或其他商业用途。'),
|
||
buildSectionTitle('2. 账号注册与使用'),
|
||
buildParagraph('2.1 用户在使用本服务时需要注册一个 Airhub 账号。用户应保证注册信息的真实性、准确性和完整性。'),
|
||
buildParagraph('2.2 用户有责任妥善保管注册账号信息及密码安全。因用户保管不善可能导致账号被盗及其后果,由用户自行承担。'),
|
||
buildParagraph(
|
||
'2.3 如发现任何未经授权使用您账号登录、使用本服务的情况,您应立即通知我们。您理解我们对您的任何请求采取行动需要合理时间,我们对在采取行动前已经产生的后果不承担责任。',
|
||
),
|
||
buildSectionTitle('3. 用户行为规范'),
|
||
buildParagraph('用户在使用本服务过程中,应当遵守法律法规,不得从事下列行为:'),
|
||
buildBulletList([
|
||
'发布、传送、传播、储存危害国家安全、破坏社会稳定、违反公序良俗的内容;',
|
||
'发布、传送、传播、储存侮辱、诽谤、淫秽、暴力、赌博等违法违规内容;',
|
||
'利用 AI 功能生成虚假信息、诈骗信息或用于非法用途;',
|
||
'对 AI 角色进行性骚扰、辱骂或诱导生成不当内容;',
|
||
'进行任何危害计算机网络安全的行为,包括但不限于攻击、侵入他人系统。',
|
||
]),
|
||
buildSectionTitle('4. 个人信息保护'),
|
||
buildParagraph(
|
||
'4.1 保护用户个人信息是我们的基本原则。我们将按照本协议及《隐私政策》的规定收集、使用、存储和分享您的个人信息。',
|
||
),
|
||
// ... simplified for brevity, following the pattern
|
||
],
|
||
);
|
||
}
|
||
}
|
||
|
||
class PrivacyPage extends StatelessWidget {
|
||
const PrivacyPage({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return SettingsContentPage(
|
||
title: '隐私政策',
|
||
date: '2025年1月15日',
|
||
children: [
|
||
buildParagraph('Airhub (以下简称"我们")非常重视您的隐私。本隐私政策(以下简称"本政策")旨在向您说明我们在您使用 Airhub 产品及服务时如何收集、使用、保存、共享和转让您的个人信息,以及您所享有的相关权利。'),
|
||
buildParagraph('请您在使用我们的服务前,仔细阅读并了解本政策。'),
|
||
buildSectionTitle('1. 我们如何收集您的个人信息'),
|
||
buildParagraph('为了向您提供优质的服务,我们会按照合法、正当、必要的原则收集您的信息:'),
|
||
buildBulletList([
|
||
'账号注册信息:当您注册 Airhub 账号时,我们会收集您的手机号码或电子邮箱地址,用于验证身份及为您提供服务。',
|
||
'设备连接信息:当您使用 Airhub 硬件设备时,我们会收集设备的 MAC 地址、SN 序列号、固件版本、IP 地址、Wi-Fi 信号强度等信息,以便实现设备连接、控制及固件升级功能。',
|
||
'语音交互数据:当您使用语音功能与 AI 角色互动时,我们会收集您的语音指令及对话内容。这些数据将用于生成 AI 回复并优化模型效果。您可以选择不保留历史对话记录。',
|
||
'角色记忆数据:您的 AI 角色养成数据(如亲密度、性格标签、记忆库)存储于云端,以便支持跨设备无缝迁移体验。',
|
||
'日志信息:为保障服务安全及运行稳定,我们会收集您的操作日志、错误日志等。',
|
||
]),
|
||
buildSectionTitle('2. 我们如何使用您的个人信息'),
|
||
buildParagraph('我们将收集的信息用于以下用途:'),
|
||
buildBulletList([
|
||
'提供各项服务:包括设备配网、远程控制、AI 语音对话等核心功能。',
|
||
'产品优化:分析用户使用习惯,改善产品功能和用户体验。',
|
||
'安全保障:监测账号异常状态,防范欺诈风险,保障系统安全。',
|
||
'个性化推荐:基于您的角色记忆,为您提供更符合您偏好的 AI 个性化回复。',
|
||
]),
|
||
buildSectionTitle('3. 信息的共享、转让与公开披露'),
|
||
buildParagraph('3.1 共享:我们不会向任何第三方共享您的个人信息,但以下情况除外:'),
|
||
buildBulletList([
|
||
'获得您的明确同意;',
|
||
'为了实现核心功能需要与合作伙伴(如云服务提供商、语音识别技术提供商)共享必要信息;',
|
||
'法律法规规定的情形。',
|
||
]),
|
||
buildParagraph('3.2 转让:我们不会将您的个人信息转让给任何第三方,除非发生合并、收购或破产清算,我们将要求受让方继续受本政策约束。'),
|
||
buildSectionTitle('4. 信息的存储与保护'),
|
||
buildParagraph('4.1 存储地点:我们依照法律法规的规定,将收集的个人信息存储于中华人民共和国境内。'),
|
||
buildParagraph('4.2 存储期限:我们仅在实现服务目的所必需的时间内保留您的个人信息。账号注销后,我们将对您的个人信息进行删除或匿名化处理。'),
|
||
buildParagraph('4.3 安全措施:我们采用 SSL 加密传输、AES 数据加密存储、严格的访问权限控制等技术措施保护您的信息安全。'),
|
||
buildSectionTitle('5. 您的权利'),
|
||
buildParagraph('5.1 访问与更正:您有权登录 APP 查阅或修改您的个人信息。'),
|
||
buildParagraph('5.2 删除:您可以通过【我的-设置-账号安全】申请注销账号。注销后,我们将删除您的所有数据且不可恢复。'),
|
||
buildParagraph('5.3 撤回同意:您可以通过设备系统设置关闭相关权限(如麦克风权限),撤回您的授权。'),
|
||
buildSectionTitle('6. 联系我们'),
|
||
buildParagraph('如您对本隐私政策有任何疑问或投诉,请发送邮件至 privacy@airhub.com 联系我们。'),
|
||
],
|
||
);
|
||
}
|
||
}
|
||
|
||
class CollectionListPage extends StatelessWidget {
|
||
const CollectionListPage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => SettingsContentPage(
|
||
title: '个人信息收集清单',
|
||
date: '2025年1月15日',
|
||
children: [
|
||
buildParagraph('为了向您提供 Airhub 的核心服务,我们需要收集以下类型的个人信息。我们将严格遵守法律法规,保护您的个人信息安全。'),
|
||
_buildInfoCard('基础功能服务', [
|
||
{'label': '收集信息类型', 'value': '手机号码、登录密码'},
|
||
{'label': '使用目的', 'value': '用于账号注册、登录、找回密码及身份认证'},
|
||
{'label': '收集场景', 'value': '用户注册或登录 APP 时'},
|
||
]),
|
||
_buildInfoCard('硬件连接与控制', [
|
||
{'label': '收集信息类型', 'value': 'Wi-Fi信息(SSID/BSSID)、蓝牙信息、设备序列号(SN)、MAC地址'},
|
||
{'label': '使用目的', 'value': '用于发现附近设备、建立蓝牙/Wi-Fi连接、设备配网及固件升级'},
|
||
{'label': '收集场景', 'value': '绑定设备、连接设备或使用设备控制功能时'},
|
||
]),
|
||
_buildInfoCard('AI 语音交互业务', [
|
||
{'label': '收集信息类型', 'value': '语音录音、对话文本、交互时间'},
|
||
{'label': '使用目的', 'value': '将语音转换为文本以理解指令、生成 AI 回复、优化语音识别模型'},
|
||
{'label': '收集场景', 'value': '使用语音对话功能与 AI 角色互动时'},
|
||
]),
|
||
_buildInfoCard('应用安全保障', [
|
||
{'label': '收集信息类型', 'value': '设备IMSI/IMEI、Android ID、IP地址、操作日志'},
|
||
{'label': '使用目的', 'value': '风控验证、安全防范、故障排查与分析'},
|
||
{'label': '收集场景', 'value': 'APP 运行期间(包括后台运行)'},
|
||
]),
|
||
],
|
||
);
|
||
|
||
static Widget _buildInfoCard(String title, List<Map<String, String>> rows) {
|
||
return Container(
|
||
margin: const EdgeInsets.only(bottom: 16),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white.withOpacity(0.92),
|
||
borderRadius: BorderRadius.circular(16),
|
||
border: Border.all(color: const Color(0xFFECCFA8).withOpacity(0.2)),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: const Color(0xFF8B5E3C).withOpacity(0.06),
|
||
offset: const Offset(0, 4),
|
||
blurRadius: 16,
|
||
),
|
||
BoxShadow(
|
||
color: Colors.black.withOpacity(0.02),
|
||
offset: const Offset(0, 1),
|
||
blurRadius: 4,
|
||
),
|
||
],
|
||
),
|
||
child: ClipRRect(
|
||
borderRadius: BorderRadius.circular(16),
|
||
child: IntrinsicHeight(
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
// Left warm accent border strip
|
||
Container(
|
||
width: 4,
|
||
decoration: const BoxDecoration(
|
||
gradient: LinearGradient(
|
||
begin: Alignment.topCenter,
|
||
end: Alignment.bottomCenter,
|
||
colors: [Color(0xFFECCFA8), Color(0xFFC99672)],
|
||
),
|
||
),
|
||
),
|
||
// Card content
|
||
Expanded(
|
||
child: Padding(
|
||
padding: const EdgeInsets.all(20),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// Title with icon
|
||
Row(
|
||
children: [
|
||
Container(
|
||
width: 28,
|
||
height: 28,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFECCFA8).withOpacity(0.15),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: const Icon(
|
||
Icons.shield_outlined,
|
||
size: 16,
|
||
color: Color(0xFFC99672),
|
||
),
|
||
),
|
||
const SizedBox(width: 10),
|
||
Text(
|
||
title,
|
||
style: const TextStyle(
|
||
fontWeight: FontWeight.w700,
|
||
color: Color(0xFF1F2937),
|
||
fontSize: 16,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 16),
|
||
// Info rows
|
||
...rows.asMap().entries.map((entry) {
|
||
final isLast = entry.key == rows.length - 1;
|
||
return Container(
|
||
padding: EdgeInsets.only(bottom: isLast ? 0 : 10),
|
||
margin: EdgeInsets.only(bottom: isLast ? 0 : 12),
|
||
decoration: isLast
|
||
? null
|
||
: BoxDecoration(
|
||
border: Border(
|
||
bottom: BorderSide(
|
||
color: const Color(0xFFECCFA8).withOpacity(0.15),
|
||
style: BorderStyle.solid,
|
||
),
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
entry.value['label']!,
|
||
style: const TextStyle(
|
||
color: Color(0xFF9CA3AF),
|
||
fontSize: 12,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
const SizedBox(height: 4),
|
||
Text(
|
||
entry.value['value']!,
|
||
style: const TextStyle(
|
||
color: Color(0xFF374151),
|
||
fontWeight: FontWeight.w500,
|
||
fontSize: 14,
|
||
height: 1.4,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
class SharingListPage extends StatelessWidget {
|
||
const SharingListPage({super.key});
|
||
@override
|
||
Widget build(BuildContext context) => SettingsContentPage(
|
||
title: '第三方信息共享清单',
|
||
date: '2025年1月15日',
|
||
children: [
|
||
buildParagraph('为保障 Airhub 的相关功能实现与应用安全稳定运行,我们可能会接入第三方提供的软件开发包(SDK)或服务。我们将审慎评估合作方的安全保障能力,并要求其遵守严格的保密协议。'),
|
||
_buildShareCard(
|
||
company: '阿里云计算有限公司',
|
||
sdkType: '阿里云对象存储/API网关 SDK',
|
||
purpose: '用于存储角色记忆数据、用户头像及语音文件,提供云端API服务稳定性。',
|
||
data: '设备信息(IP地址)、网络连接状态。',
|
||
method: 'SDK 本机采集',
|
||
privacy: 'https://terms.aliyun.com/legal-agreement/terms',
|
||
),
|
||
_buildShareCard(
|
||
company: '深圳市腾讯计算机系统有限公司',
|
||
sdkType: '微信开放平台 SDK',
|
||
purpose: '用于支持微信账号一键登录、分享内容至微信朋友圈或会话。',
|
||
data: '设备标识信息、微信个人授权信息(昵称、头像)。',
|
||
method: 'SDK 本机采集',
|
||
privacy: 'https://weixin.qq.com/cgi-bin/readtemplate?t=weixin_agreement',
|
||
),
|
||
_buildShareCard(
|
||
company: '深圳市和讯华谷信息技术有限公司',
|
||
sdkType: '极光推送 (JPush) SDK',
|
||
purpose: '用于实现消息推送功能,向您发送设备状态更新通知或系统公告。',
|
||
data: '设备标识符(IMEI/MAC/Android ID/IDFA)、网络信息、应用安装列表。',
|
||
method: 'SDK 本机采集',
|
||
privacy: 'https://www.jiguang.cn/license/privacy',
|
||
),
|
||
],
|
||
);
|
||
|
||
static Widget _buildShareCard({
|
||
required String company,
|
||
required String sdkType,
|
||
required String purpose,
|
||
required String data,
|
||
required String method,
|
||
required String privacy,
|
||
}) {
|
||
return Container(
|
||
margin: const EdgeInsets.only(bottom: 16),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white.withOpacity(0.92),
|
||
borderRadius: BorderRadius.circular(16),
|
||
border: Border.all(color: const Color(0xFFECCFA8).withOpacity(0.2)),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: const Color(0xFF8B5E3C).withOpacity(0.06),
|
||
offset: const Offset(0, 4),
|
||
blurRadius: 16,
|
||
),
|
||
BoxShadow(
|
||
color: Colors.black.withOpacity(0.02),
|
||
offset: const Offset(0, 1),
|
||
blurRadius: 4,
|
||
),
|
||
],
|
||
),
|
||
child: ClipRRect(
|
||
borderRadius: BorderRadius.circular(16),
|
||
child: IntrinsicHeight(
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
// Left warm accent border strip
|
||
Container(
|
||
width: 4,
|
||
decoration: const BoxDecoration(
|
||
gradient: LinearGradient(
|
||
begin: Alignment.topCenter,
|
||
end: Alignment.bottomCenter,
|
||
colors: [Color(0xFFECCFA8), Color(0xFFC99672)],
|
||
),
|
||
),
|
||
),
|
||
// Card content
|
||
Expanded(
|
||
child: Padding(
|
||
padding: const EdgeInsets.all(20),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// Header
|
||
Container(
|
||
padding: const EdgeInsets.only(bottom: 12),
|
||
margin: const EdgeInsets.only(bottom: 12),
|
||
decoration: BoxDecoration(
|
||
border: Border(
|
||
bottom: BorderSide(
|
||
color: const Color(0xFFECCFA8).withOpacity(0.15),
|
||
),
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
company,
|
||
style: const TextStyle(
|
||
fontWeight: FontWeight.w700,
|
||
fontSize: 16,
|
||
color: Color(0xFF1F2937),
|
||
),
|
||
),
|
||
const SizedBox(height: 6),
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||
decoration: BoxDecoration(
|
||
gradient: LinearGradient(
|
||
colors: [
|
||
const Color(0xFFECCFA8).withOpacity(0.15),
|
||
const Color(0xFFC99672).withOpacity(0.1),
|
||
],
|
||
),
|
||
borderRadius: BorderRadius.circular(6),
|
||
),
|
||
child: Text(
|
||
sdkType,
|
||
style: const TextStyle(
|
||
fontSize: 12,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFFB07D5A),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
// Rows
|
||
_buildShareRow('使用目的', purpose),
|
||
_buildShareRow('收集数据', data),
|
||
_buildShareRow('共享方式', method),
|
||
_buildShareRow('隐私政策', privacy),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
static Widget _buildShareRow(String label, String value) {
|
||
return Padding(
|
||
padding: const EdgeInsets.only(bottom: 8),
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
SizedBox(
|
||
width: 70,
|
||
child: Text(
|
||
label,
|
||
style: const TextStyle(
|
||
color: Color(0xFF6B7280),
|
||
fontSize: 13,
|
||
),
|
||
),
|
||
),
|
||
Expanded(
|
||
child: Text(
|
||
value,
|
||
style: const TextStyle(
|
||
color: Color(0xFF374151),
|
||
fontSize: 13,
|
||
height: 1.5,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|