fix:第一轮跑

This commit is contained in:
jingyun committed 2026-08-24 19:07:31 +08:00
1 parent e834ec7586
commit 33235f450b
56 files changed
+4357 -210

No files matched your search

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -8,9 +10,13 @@ import 'package:video_player/video_player.dart';
import '../../../common/config/app_h5_urls.dart';
import '../../../common/services/device_header_service.dart';
import '../../../common/push/umeng_push_service.dart';
import '../../../common/push/notification_permission.dart';
import '../../../common/privacy/umeng_privacy_consent.dart';
import '../../../common/theme/jinzhi_theme.dart';
import '../../../common/widgets/adaptive.dart';
import '../../../common/router/app_router.dart';
import '../../../common/tracking/umeng_service.dart';
class PrivacyConsentPage extends StatefulWidget {
const PrivacyConsentPage({super.key});
@@ -47,12 +53,46 @@ class _PrivacyConsentPageState extends State<PrivacyConsentPage> {
Future<void> _agree() async {
await DeviceHeaderService.to.markPrivacyAgreed();
await DeviceHeaderService.to.ensureInitialized();
if (mounted) context.go(AppRoutes.assets);
if (mounted) {
context.go(AppRoutes.assets);
WidgetsBinding.instance.addPostFrameCallback((_) {
_completePostConsentInitialization();
});
}
}
Future<void> _completePostConsentInitialization() async {
// Consent navigation must never depend on an optional native SDK. The
// calls below still run in order, but failures are retried by normal app
// startup instead of trapping the user on the consent page.
try {
await UmengPrivacyConsent.submitGranted();
await UmengService.initIfAllowed();
await UmengPushService.startIfAllowed();
} catch (_) {}
}
Future<void> _enter() async {
await DeviceHeaderService.to.markIosStartupSeen();
if (mounted) context.go(AppRoutes.assets);
if (!mounted) return;
// 首屏跳转与可选 SDK 调用严格解耦。原生初始化、ATT 或网络问题都不得
// 阻断“立即进入”。
context.go(AppRoutes.assets);
WidgetsBinding.instance.addPostFrameCallback((_) {
unawaited(_completeIosPostEnterInitialization());
});
}
Future<void> _completeIosPostEnterInitialization() async {
// iOS 不展示 Android 顶部说明;这里在可见首页后直接请求系统通知授权。
// U-Push 注册保留,供 APNs token/友盟通道建立使用。
try {
await NotificationPermission.requestFromSettings();
} catch (_) {}
try {
await UmengService.requestTrackingAuthorizationIfNeeded();
await UmengPushService.startIfAllowed();
} catch (_) {}
}
@override
@@ -1,14 +1,16 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_remix/flutter_remix.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:url_launcher/url_launcher_string.dart';
import '../../../common/config/app_h5_urls.dart';
import '../../../common/push/notification_permission.dart';
import '../../../common/router/app_router.dart';
import '../../../common/services/cache_clear_service.dart';
import '../../../common/theme/app_text.dart';
import '../../../common/theme/spacing.dart';
import '../../../common/theme/theme_controller.dart';
@@ -20,6 +22,9 @@ import '../../auth/data/auth_models.dart';
import '../../profile/application/profile_settings_controller.dart';
import '../../profile/data/profile_models.dart';
import '../application/feedback_email_service.dart';
import '../application/update_controller.dart';
import '../application/update_service.dart';
import '../data/update_api_client.dart';
class SettingsPage extends HookConsumerWidget {
const SettingsPage({super.key});
@@ -31,6 +36,25 @@ class SettingsPage extends HookConsumerWidget {
final profileSettings = ref.watch(profileSettingsControllerProvider);
final authAsync = ref.watch(authProvider);
final loggedIn = authAsync.valueOrNull is LoggedInAuthState;
final updateSupported = UpdateService.isSupported;
final upgrade = ref.watch(updateControllerProvider).valueOrNull;
final notificationEnabled = useState(false);
useEffect(() {
if (!updateSupported) return null;
Future<void>(() async {
try {
await ref.read(updateControllerProvider.notifier).checkUpgrade();
} catch (_) {}
});
return null;
}, [updateSupported]);
useEffect(() {
Future<void>(
() async => notificationEnabled.value =
await NotificationPermission.isGranted(),
);
return null;
}, const []);
return Scaffold(
backgroundColor: cs.background,
@@ -126,6 +150,36 @@ class SettingsPage extends HookConsumerWidget {
trailing: '占位',
onTap: () => _showWidgetSheet(context),
),
Divider(height: 1, thickness: 1, color: cs.border),
_SwitchRow(
title: '推送通知',
subtitle: notificationEnabled.value
? '已允许接收服务提醒'
: '在系统设置中开启服务提醒',
value: notificationEnabled.value,
onChanged: (enabled) async {
if (enabled) {
final granted =
await NotificationPermission.requestFromSettings();
notificationEnabled.value = granted;
if (!granted) {
await NotificationPermission.openSystemSettings();
}
} else {
await NotificationPermission.openSystemSettings();
notificationEnabled.value =
await NotificationPermission.isGranted();
}
},
),
Divider(height: 1, thickness: 1, color: cs.border),
_NavRow(
icon: FlutterRemix.delete_bin_6_line,
label: '清除缓存',
trailing: '图片与网页缓存',
showArrow: true,
onTap: () => _confirmClearCache(context),
),
if (kDebugMode) ...[
Divider(height: 1, thickness: 1, color: cs.border),
_NavRow(
@@ -144,18 +198,16 @@ class SettingsPage extends HookConsumerWidget {
_NavRow(
icon: FlutterRemix.file_list_3_line,
label: '用户协议',
onTap: () => launchUrlString(
AppH5UrlsHelper.withCacheBuster(AppH5Urls.userProtocolUrl),
mode: LaunchMode.inAppBrowserView,
onTap: () => context.push(
'${AppRoutes.web}?url=${Uri.encodeComponent(AppH5UrlsHelper.withCacheBuster(AppH5Urls.userProtocolUrl))}',
),
),
Divider(height: 1, thickness: 1, color: cs.border),
_NavRow(
icon: FlutterRemix.shield_check_line,
label: '隐私政策',
onTap: () => launchUrlString(
AppH5UrlsHelper.withCacheBuster(AppH5Urls.privacyUrl),
mode: LaunchMode.inAppBrowserView,
onTap: () => context.push(
'${AppRoutes.web}?url=${Uri.encodeComponent(AppH5UrlsHelper.withCacheBuster(AppH5Urls.privacyUrl))}',
),
),
Divider(height: 1, thickness: 1, color: cs.border),
@@ -192,7 +244,27 @@ class SettingsPage extends HookConsumerWidget {
const _Section('关于'),
_Card(
children: [
const _AboutRow(),
_AboutRow(
upgrade: upgrade,
enabled: updateSupported,
onTap: () async {
try {
final result = await ref
.read(updateControllerProvider.notifier)
.checkUpgrade();
if (!context.mounted) return;
await UpdateService.showIfNeeded(
context,
ref,
result,
source: UpgradeCheckSource.manual,
onLatest: () => toastInfo(msg: '已经是最新版本'),
);
} catch (_) {
if (context.mounted) toastInfo(msg: '检查更新失败');
}
},
),
Divider(height: 1, thickness: 1, color: cs.border),
_NavRow(
icon: FlutterRemix.mail_line,
@@ -215,6 +287,38 @@ class SettingsPage extends HookConsumerWidget {
);
}
Future<void> _confirmClearCache(BuildContext context) async {
final systemSettings = CacheClearService.requiresSystemSettings;
final confirmed = await showDialog<bool>(
context: context,
builder: (dialogContext) => AlertDialog(
title: const Text('清除缓存'),
content: Text(
systemSettings
? 'Android 需要在系统应用详情中完成缓存清理,是否立即前往?'
: '将清除图片和网页缓存。下次打开相关页面时需要重新加载。',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext, false),
child: const Text('取消'),
),
FilledButton(
onPressed: () => Navigator.pop(dialogContext, true),
child: Text(systemSettings ? '前往设置' : '确认清除'),
),
],
),
);
if (confirmed != true) return;
if (systemSettings) {
await CacheClearService.openAndroidSystemSettings();
return;
}
await CacheClearService.clear();
if (context.mounted) toastInfo(msg: '缓存已清除');
}
Future<void> _confirmLogout(BuildContext context, WidgetRef ref) async {
final cs = ShadTheme.of(context).colorScheme;
final ok = await showModalBottomSheet<bool>(
@@ -788,7 +892,7 @@ class _ThemeSegmentCard extends StatelessWidget {
Widget build(BuildContext context) {
final cs = ShadTheme.of(context).colorScheme;
final options = <(ThemeMode, String, IconData)>[
(ThemeMode.system, '跟随系统', Icons.brightness_auto_outlined),
(ThemeMode.system, '系统主题', Icons.brightness_auto_outlined),
(ThemeMode.light, '浅色', Icons.light_mode_outlined),
(ThemeMode.dark, '深色', Icons.dark_mode_outlined),
];
@@ -948,6 +1052,7 @@ class _NavRow extends StatelessWidget {
required this.label,
this.trailing,
this.onTap,
this.showArrow = false,
this.destructive = false,
});
@@ -955,6 +1060,7 @@ class _NavRow extends StatelessWidget {
final String label;
final String? trailing;
final VoidCallback? onTap;
final bool showArrow;
final bool destructive;
@override
@@ -981,7 +1087,7 @@ class _NavRow extends StatelessWidget {
color: cs.mutedForeground,
),
),
if (onTap != null && trailing == null)
if (onTap != null && (trailing == null || showArrow))
Icon(
FlutterRemix.arrow_right_s_line,
size: 20,
@@ -995,7 +1101,15 @@ class _NavRow extends StatelessWidget {
}
class _AboutRow extends StatelessWidget {
const _AboutRow();
const _AboutRow({
required this.upgrade,
required this.enabled,
required this.onTap,
});
final UpgradeInfo? upgrade;
final bool enabled;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
@@ -1006,33 +1120,47 @@ class _AboutRow extends StatelessWidget {
final v = snap.hasData
? 'v${snap.data!.version}+${snap.data!.buildNumber}'
: '';
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
child: Row(
children: [
SizedBox(
width: 24,
child: Icon(
FlutterRemix.information_line,
size: 20,
color: cs.foreground,
final label = upgrade?.hasPublishedUpdate == true
? '有新版 ${upgrade!.latestVersion}'
: v;
return InkWell(
onTap: enabled ? onTap : null,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
child: Row(
children: [
SizedBox(
width: 24,
child: Icon(
FlutterRemix.information_line,
size: 20,
color: cs.foreground,
),
),
),
const SizedBox(width: 13),
Expanded(
child: Text(
'金值',
style: TextStyle(fontSize: 15, color: cs.foreground),
const SizedBox(width: 13),
Expanded(
child: Text(
'金值',
style: TextStyle(fontSize: 15, color: cs.foreground),
),
),
),
Text(
v,
style: AppText.meta.copyWith(
fontSize: 13,
color: cs.mutedForeground,
Text(
label,
style: AppText.meta.copyWith(
fontSize: 13,
color: cs.mutedForeground,
),
),
),
],
if (enabled) ...[
const SizedBox(width: 6),
Icon(
FlutterRemix.arrow_right_s_line,
size: 20,
color: cs.mutedForeground,
),
],
],
),
),
);
},