新增网络测试功能,预留付费用户线路自定义与线路自选入口
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
@@ -16,6 +17,7 @@ import 'file_preferences_page.dart';
|
||||
import 'app_settings_page.dart';
|
||||
import 'credit_history_page.dart';
|
||||
import 'quick_access_settings_page.dart';
|
||||
import 'network_test_page.dart';
|
||||
import '../../../router/app_router.dart';
|
||||
|
||||
/// 设置主页
|
||||
@@ -83,112 +85,127 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
),
|
||||
body: DesktopConstrained(
|
||||
child: ListView(
|
||||
children: [
|
||||
_buildProfileCard(context, user, settings, capacity),
|
||||
const SizedBox(height: 8),
|
||||
_buildSection(
|
||||
title: '账户与安全',
|
||||
children: [
|
||||
_SettingsTile(
|
||||
icon: Icons.person_outline,
|
||||
title: '个人资料',
|
||||
subtitle: '修改昵称、头像',
|
||||
onTap: () => _navigateTo(context, const ProfileEditPage()),
|
||||
),
|
||||
_SettingsTile(
|
||||
icon: Icons.security_outlined,
|
||||
title: '安全设置',
|
||||
subtitle: _securitySubtitle(settings),
|
||||
onTap: () => _navigateTo(context, const SecuritySettingsPage()),
|
||||
),
|
||||
],
|
||||
),
|
||||
_buildSection(
|
||||
title: '偏好',
|
||||
children: [
|
||||
_SettingsTile(
|
||||
icon: Icons.sync_outlined,
|
||||
title: '文件同步',
|
||||
subtitle: '本地与云端文件自动同步',
|
||||
onTap: () => Navigator.of(context).pushNamed(RouteNames.syncSettings),
|
||||
),
|
||||
_SettingsTile(
|
||||
icon: Icons.apps_outlined,
|
||||
title: '快捷入口',
|
||||
subtitle: '自定义概览页快捷目录',
|
||||
onTap: () => _navigateTo(context, const QuickAccessSettingsPage()),
|
||||
),
|
||||
_SettingsTile(
|
||||
icon: Icons.folder_outlined,
|
||||
title: '文件偏好',
|
||||
subtitle: '版本保留、视图同步、分享可见性',
|
||||
onTap: () => _navigateTo(context, const FilePreferencesPage()),
|
||||
),
|
||||
_SettingsTile(
|
||||
icon: Icons.tune,
|
||||
title: '应用设置',
|
||||
subtitle: '缓存、主题、语言',
|
||||
onTap: () => _navigateTo(context, const AppSettingsPage()),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 财务区域(有数据时才显示)
|
||||
if (settings != null) ..._buildProSections(context, settings),
|
||||
_buildSection(
|
||||
title: '关于',
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: const Text('应用名称'),
|
||||
subtitle: const Text('公云存储'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.tag),
|
||||
title: const Text('版本号'),
|
||||
subtitle: Text(_appVersion.isEmpty ? '加载中...' : _appVersion),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.copyright),
|
||||
title: const Text('License'),
|
||||
subtitle: const Text('AGPL-3.0'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.code),
|
||||
title: const Text('二次开发'),
|
||||
subtitle: const Text('gongyun_app'),
|
||||
trailing: const Icon(Icons.open_in_new, size: 16),
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse('https://git.saont.net/gongyun/app'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.code),
|
||||
title: const Text('基于'),
|
||||
subtitle: const Text('cloudreve4_flutter'),
|
||||
trailing: const Icon(Icons.open_in_new, size: 16),
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse('https://github.com/LimoYuan/cloudreve4_flutter'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildLogoutButton(context, auth),
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
children: [
|
||||
_buildProfileCard(context, user, settings, capacity),
|
||||
const SizedBox(height: 8),
|
||||
_buildSection(
|
||||
title: '账户与安全',
|
||||
children: [
|
||||
_SettingsTile(
|
||||
icon: Icons.person_outline,
|
||||
title: '个人资料',
|
||||
subtitle: '修改昵称、头像',
|
||||
onTap: () => _navigateTo(context, const ProfileEditPage()),
|
||||
),
|
||||
_SettingsTile(
|
||||
icon: Icons.security_outlined,
|
||||
title: '安全设置',
|
||||
subtitle: _securitySubtitle(settings),
|
||||
onTap: () =>
|
||||
_navigateTo(context, const SecuritySettingsPage()),
|
||||
),
|
||||
],
|
||||
),
|
||||
_buildSection(
|
||||
title: '偏好',
|
||||
children: [
|
||||
_SettingsTile(
|
||||
icon: Icons.sync_outlined,
|
||||
title: '文件同步',
|
||||
subtitle: '本地与云端文件自动同步',
|
||||
onTap: () =>
|
||||
Navigator.of(context).pushNamed(RouteNames.syncSettings),
|
||||
),
|
||||
_SettingsTile(
|
||||
icon: Icons.apps_outlined,
|
||||
title: '快捷入口',
|
||||
subtitle: '自定义概览页快捷目录',
|
||||
onTap: () =>
|
||||
_navigateTo(context, const QuickAccessSettingsPage()),
|
||||
),
|
||||
_SettingsTile(
|
||||
icon: Icons.folder_outlined,
|
||||
title: '文件偏好',
|
||||
subtitle: '版本保留、视图同步、分享可见性',
|
||||
onTap: () =>
|
||||
_navigateTo(context, const FilePreferencesPage()),
|
||||
),
|
||||
_SettingsTile(
|
||||
icon: Icons.tune,
|
||||
title: '应用设置',
|
||||
subtitle: '缓存、主题、语言',
|
||||
onTap: () => _navigateTo(context, const AppSettingsPage()),
|
||||
),
|
||||
_SettingsTile(
|
||||
icon: Icons.network_check,
|
||||
title: '网络测试',
|
||||
subtitle: '服务连通性测试与自定义线路(会员)',
|
||||
onTap: () => _openNetworkTest(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 财务区域(有数据时才显示)
|
||||
if (settings != null) ..._buildProSections(context, settings),
|
||||
_buildSection(
|
||||
title: '关于',
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: const Text('应用名称'),
|
||||
subtitle: const Text('公云存储'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.tag),
|
||||
title: const Text('版本号'),
|
||||
subtitle: Text(_appVersion.isEmpty ? '加载中...' : _appVersion),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.copyright),
|
||||
title: const Text('License'),
|
||||
subtitle: const Text('AGPL-3.0'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.code),
|
||||
title: const Text('二次开发'),
|
||||
subtitle: const Text('gongyun_app'),
|
||||
trailing: const Icon(Icons.open_in_new, size: 16),
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse('https://git.saont.net/gongyun/app'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.code),
|
||||
title: const Text('基于'),
|
||||
subtitle: const Text('cloudreve4_flutter'),
|
||||
trailing: const Icon(Icons.open_in_new, size: 16),
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse(
|
||||
'https://github.com/LimoYuan/cloudreve4_flutter',
|
||||
),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildLogoutButton(context, auth),
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 财务区域(存储包、积分、会员)
|
||||
List<Widget> _buildProSections(BuildContext context, UserSettingModel settings) {
|
||||
List<Widget> _buildProSections(
|
||||
BuildContext context,
|
||||
UserSettingModel settings,
|
||||
) {
|
||||
final sections = <Widget>[];
|
||||
final hasStoragePacks = settings.storagePacks.isNotEmpty;
|
||||
final hasCredit = settings.credit > 0;
|
||||
@@ -197,40 +214,54 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
if (hasStoragePacks || hasCredit || hasMembership) {
|
||||
final children = <Widget>[];
|
||||
if (hasMembership) {
|
||||
children.add(ListTile(
|
||||
leading: const Icon(Icons.workspace_premium_outlined),
|
||||
title: const Text('会员'),
|
||||
subtitle: Text('到期: ${_formatDate(settings.groupExpires!)}'),
|
||||
trailing: TextButton(
|
||||
onPressed: () => _cancelMembership(context),
|
||||
child: const Text('取消会员', style: TextStyle(color: Colors.red)),
|
||||
children.add(
|
||||
ListTile(
|
||||
leading: const Icon(Icons.workspace_premium_outlined),
|
||||
title: const Text('会员'),
|
||||
subtitle: Text('到期: ${_formatDate(settings.groupExpires!)}'),
|
||||
trailing: TextButton(
|
||||
onPressed: () => _cancelMembership(context),
|
||||
child: const Text('取消会员', style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
),
|
||||
));
|
||||
);
|
||||
}
|
||||
if (hasStoragePacks) {
|
||||
children.add(ListTile(
|
||||
leading: const Icon(Icons.inventory_2_outlined),
|
||||
title: Text('存储包 (${settings.storagePacks.length})'),
|
||||
subtitle: Text(_storagePackSummary(settings.storagePacks)),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => _showStoragePacks(context, settings.storagePacks),
|
||||
));
|
||||
children.add(
|
||||
ListTile(
|
||||
leading: const Icon(Icons.inventory_2_outlined),
|
||||
title: Text('存储包 (${settings.storagePacks.length})'),
|
||||
subtitle: Text(_storagePackSummary(settings.storagePacks)),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => _showStoragePacks(context, settings.storagePacks),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (hasCredit) {
|
||||
children.add(ListTile(
|
||||
leading: const Icon(Icons.account_balance_wallet_outlined),
|
||||
title: const Text('积分'),
|
||||
subtitle: Text('${settings.credit} 积分'),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => _navigateTo(context, CreditHistoryPage(currentCredit: settings.credit)),
|
||||
));
|
||||
children.add(
|
||||
ListTile(
|
||||
leading: const Icon(Icons.account_balance_wallet_outlined),
|
||||
title: const Text('积分'),
|
||||
subtitle: Text('${settings.credit} 积分'),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => _navigateTo(
|
||||
context,
|
||||
CreditHistoryPage(currentCredit: settings.credit),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
sections.add(_buildSection(title: '财务', children: children));
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
|
||||
Widget _buildProfileCard(BuildContext context, UserModel? user, UserSettingModel? settings, UserCapacityModel? capacity) {
|
||||
Widget _buildProfileCard(
|
||||
BuildContext context,
|
||||
UserModel? user,
|
||||
UserSettingModel? settings,
|
||||
UserCapacityModel? capacity,
|
||||
) {
|
||||
final theme = Theme.of(context);
|
||||
final colorScheme = theme.colorScheme;
|
||||
|
||||
@@ -253,17 +284,24 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
children: [
|
||||
Text(
|
||||
user?.nickname ?? '未登录',
|
||||
style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
user?.email ?? '',
|
||||
style: theme.textTheme.bodySmall?.copyWith(color: colorScheme.onSurfaceVariant),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
if (user?.group != null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
@@ -316,9 +354,12 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('存储空间', style: theme.textTheme.bodySmall),
|
||||
Text('$usedText / $totalText', style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
)),
|
||||
Text(
|
||||
'$usedText / $totalText',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
@@ -334,7 +375,10 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSection({required String title, required List<Widget> children}) {
|
||||
Widget _buildSection({
|
||||
required String title,
|
||||
required List<Widget> children,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Column(
|
||||
@@ -366,11 +410,18 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _confirmLogout(context, auth),
|
||||
icon: Icon(Icons.logout, color: Theme.of(context).colorScheme.error),
|
||||
label: Text('退出登录', style: TextStyle(color: Theme.of(context).colorScheme.error)),
|
||||
label: Text(
|
||||
'退出登录',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
side: BorderSide(color: Theme.of(context).colorScheme.error.withValues(alpha: 0.3)),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).colorScheme.error.withValues(alpha: 0.3),
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -407,33 +458,47 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Text('存储包', style: Theme.of(ctx).textTheme.titleMedium),
|
||||
),
|
||||
...packs.map((pack) => Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(pack.name, style: Theme.of(ctx).textTheme.titleSmall),
|
||||
Text(_formatBytes(pack.size), style: Theme.of(ctx).textTheme.labelLarge),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'激活: ${_formatDate(pack.activeSince)}'
|
||||
'${pack.expireAt != null ? " · 到期: ${_formatDate(pack.expireAt!)}" : " · 永久"}',
|
||||
style: Theme.of(ctx).textTheme.bodySmall,
|
||||
),
|
||||
if (pack.isExpired) ...[
|
||||
...packs.map(
|
||||
(pack) => Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
pack.name,
|
||||
style: Theme.of(ctx).textTheme.titleSmall,
|
||||
),
|
||||
Text(
|
||||
_formatBytes(pack.size),
|
||||
style: Theme.of(ctx).textTheme.labelLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text('已过期', style: TextStyle(color: Theme.of(ctx).colorScheme.error, fontSize: 12)),
|
||||
Text(
|
||||
'激活: ${_formatDate(pack.activeSince)}'
|
||||
'${pack.expireAt != null ? " · 到期: ${_formatDate(pack.expireAt!)}" : " · 永久"}',
|
||||
style: Theme.of(ctx).textTheme.bodySmall,
|
||||
),
|
||||
if (pack.isExpired) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'已过期',
|
||||
style: TextStyle(
|
||||
color: Theme.of(ctx).colorScheme.error,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
@@ -449,10 +514,15 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
title: const Text('取消会员'),
|
||||
content: const Text('确定要取消当前会员吗?取消后将失去会员权益。'),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(ctx).pop(false), child: const Text('取消')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(false),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(true),
|
||||
style: FilledButton.styleFrom(backgroundColor: Theme.of(ctx).colorScheme.error),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Theme.of(ctx).colorScheme.error,
|
||||
),
|
||||
child: const Text('确认取消'),
|
||||
),
|
||||
],
|
||||
@@ -479,6 +549,42 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openNetworkTest(BuildContext context) async {
|
||||
final agreed = await showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: const Text('会话信息授权'),
|
||||
content: _NetworkTestConsentText(
|
||||
onOpenPrivacy: () => launchUrl(
|
||||
Uri.parse('https://www.gongyun.org/policy/privacy.html'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
onOpenTerms: () => launchUrl(
|
||||
Uri.parse('https://www.gongyun.org/policy/terms.html'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: const Text('不同意'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(true),
|
||||
child: const Text('同意'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (agreed == true && context.mounted) {
|
||||
await Navigator.of(
|
||||
context,
|
||||
).push(MaterialPageRoute(builder: (_) => const NetworkTestPage()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _confirmLogout(BuildContext context, AuthProvider auth) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
@@ -486,10 +592,15 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
title: const Text('退出登录'),
|
||||
content: const Text('确定要退出当前账号吗?'),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(ctx).pop(false), child: const Text('取消')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(false),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(true),
|
||||
style: FilledButton.styleFrom(backgroundColor: Theme.of(context).colorScheme.error),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
child: const Text('退出'),
|
||||
),
|
||||
],
|
||||
@@ -506,7 +617,9 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
String _formatBytes(int bytes) {
|
||||
if (bytes < 1024) return '$bytes B';
|
||||
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
|
||||
if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
||||
if (bytes < 1024 * 1024 * 1024) {
|
||||
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
||||
}
|
||||
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
|
||||
}
|
||||
|
||||
@@ -515,6 +628,75 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
}
|
||||
}
|
||||
|
||||
class _NetworkTestConsentText extends StatefulWidget {
|
||||
final VoidCallback onOpenPrivacy;
|
||||
final VoidCallback onOpenTerms;
|
||||
|
||||
const _NetworkTestConsentText({
|
||||
required this.onOpenPrivacy,
|
||||
required this.onOpenTerms,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_NetworkTestConsentText> createState() =>
|
||||
_NetworkTestConsentTextState();
|
||||
}
|
||||
|
||||
class _NetworkTestConsentTextState extends State<_NetworkTestConsentText> {
|
||||
late final TapGestureRecognizer _privacyRecognizer;
|
||||
late final TapGestureRecognizer _termsRecognizer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_privacyRecognizer = TapGestureRecognizer()..onTap = widget.onOpenPrivacy;
|
||||
_termsRecognizer = TapGestureRecognizer()..onTap = widget.onOpenTerms;
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant _NetworkTestConsentText oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_privacyRecognizer.onTap = widget.onOpenPrivacy;
|
||||
_termsRecognizer.onTap = widget.onOpenTerms;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_privacyRecognizer.dispose();
|
||||
_termsRecognizer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final linkStyle = TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
);
|
||||
|
||||
return RichText(
|
||||
text: TextSpan(
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
children: [
|
||||
const TextSpan(text: '您需同意'),
|
||||
TextSpan(
|
||||
text: '隐私政策',
|
||||
style: linkStyle,
|
||||
recognizer: _privacyRecognizer,
|
||||
),
|
||||
const TextSpan(text: '与'),
|
||||
TextSpan(
|
||||
text: '用户协议',
|
||||
style: linkStyle,
|
||||
recognizer: _termsRecognizer,
|
||||
),
|
||||
const TextSpan(text: '方可进入网络测试页。'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SettingsTile extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String title;
|
||||
|
||||
Reference in New Issue
Block a user