1. 新增工作台三栏布局模式,适配宽屏设备 2. 添加跨平台系统托盘支持,新增托盘图标资源 3. 修复工作台模式下导航返回异常问题 4. 统一JSON类型安全解析,替换硬类型转换 5. 增加macOS深度链接支持,统一渠道分发信息 6. 优化部分页面生命周期和状态加载逻辑 7. 移除废弃的nearby_connections依赖
206 lines
6.6 KiB
Dart
206 lines
6.6 KiB
Dart
// ============================================================
|
||
// 闲言APP — 引导页状态管理
|
||
// 创建时间: 2026-05-21
|
||
// 更新时间: 2026-06-12
|
||
// 作用: 管理引导页流程状态(页面切换、协议勾选、个性化设置)
|
||
// 上次更新: 从providers/子目录提升至onboarding根目录
|
||
// ============================================================
|
||
|
||
import 'dart:io';
|
||
|
||
import 'package:flutter/services.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
||
import '../../../core/services/post_agreement_initializer.dart';
|
||
import '../../../core/storage/hive_safe_access.dart';
|
||
import '../../../core/storage/kv_storage.dart';
|
||
import '../../../core/utils/logger.dart';
|
||
import '../../../core/constants/default_settings.dart';
|
||
|
||
class OnboardingState {
|
||
const OnboardingState({
|
||
this.currentPage = 0,
|
||
this.totalPages = 3,
|
||
this.selectedLocale = 'zh',
|
||
this.privacyAgreed = false,
|
||
this.termsAgreed = false,
|
||
this.permissionRead = false,
|
||
this.showOnNextLaunch = false,
|
||
this.isCompleting = false,
|
||
this.agreementTabIndex = 0,
|
||
this.soundEnabled = DefaultSettings.onboardingSoundEnabled,
|
||
this.shaderBackground = false,
|
||
this.knowNewFeatures = false,
|
||
});
|
||
|
||
final int currentPage;
|
||
final int totalPages;
|
||
final String selectedLocale;
|
||
final bool privacyAgreed;
|
||
final bool termsAgreed;
|
||
final bool permissionRead;
|
||
final bool showOnNextLaunch;
|
||
final bool isCompleting;
|
||
final int agreementTabIndex;
|
||
final bool soundEnabled;
|
||
final bool shaderBackground;
|
||
final bool knowNewFeatures;
|
||
|
||
bool get canProceedAgreement =>
|
||
privacyAgreed && termsAgreed && permissionRead;
|
||
|
||
bool get isLastPage => currentPage >= totalPages - 1;
|
||
|
||
OnboardingState copyWith({
|
||
int? currentPage,
|
||
int? totalPages,
|
||
String? selectedLocale,
|
||
bool? privacyAgreed,
|
||
bool? termsAgreed,
|
||
bool? permissionRead,
|
||
bool? showOnNextLaunch,
|
||
bool? isCompleting,
|
||
int? agreementTabIndex,
|
||
bool? soundEnabled,
|
||
bool? shaderBackground,
|
||
bool? knowNewFeatures,
|
||
}) {
|
||
return OnboardingState(
|
||
currentPage: currentPage ?? this.currentPage,
|
||
totalPages: totalPages ?? this.totalPages,
|
||
selectedLocale: selectedLocale ?? this.selectedLocale,
|
||
privacyAgreed: privacyAgreed ?? this.privacyAgreed,
|
||
termsAgreed: termsAgreed ?? this.termsAgreed,
|
||
permissionRead: permissionRead ?? this.permissionRead,
|
||
showOnNextLaunch: showOnNextLaunch ?? this.showOnNextLaunch,
|
||
isCompleting: isCompleting ?? this.isCompleting,
|
||
agreementTabIndex: agreementTabIndex ?? this.agreementTabIndex,
|
||
soundEnabled: soundEnabled ?? this.soundEnabled,
|
||
shaderBackground: shaderBackground ?? this.shaderBackground,
|
||
knowNewFeatures: knowNewFeatures ?? this.knowNewFeatures,
|
||
);
|
||
}
|
||
}
|
||
|
||
class OnboardingNotifier extends Notifier<OnboardingState> {
|
||
@override
|
||
OnboardingState build() => const OnboardingState();
|
||
|
||
void nextPage() {
|
||
if (state.currentPage < state.totalPages - 1) {
|
||
state = state.copyWith(currentPage: state.currentPage + 1);
|
||
Log.i('Onboarding: nextPage → ${state.currentPage}');
|
||
}
|
||
}
|
||
|
||
void previousPage() {
|
||
if (state.currentPage > 0) {
|
||
state = state.copyWith(currentPage: state.currentPage - 1);
|
||
Log.i('Onboarding: previousPage → ${state.currentPage}');
|
||
}
|
||
}
|
||
|
||
void setPage(int page) {
|
||
if (page >= 0 && page < state.totalPages) {
|
||
state = state.copyWith(currentPage: page);
|
||
Log.i('Onboarding: setPage → $page');
|
||
}
|
||
}
|
||
|
||
/// 设置引导页总页数(根据平台和skipAgreement动态调整)
|
||
void setTotalPages(int pages) {
|
||
state = state.copyWith(totalPages: pages);
|
||
Log.i('Onboarding: setTotalPages → $pages');
|
||
}
|
||
|
||
void togglePrivacy() {
|
||
state = state.copyWith(privacyAgreed: !state.privacyAgreed);
|
||
}
|
||
|
||
void toggleTerms() {
|
||
state = state.copyWith(termsAgreed: !state.termsAgreed);
|
||
}
|
||
|
||
void togglePermissionRead() {
|
||
state = state.copyWith(permissionRead: !state.permissionRead);
|
||
}
|
||
|
||
void toggleShowOnNextLaunch() {
|
||
state = state.copyWith(showOnNextLaunch: !state.showOnNextLaunch);
|
||
}
|
||
|
||
void toggleSound() {
|
||
state = state.copyWith(soundEnabled: !state.soundEnabled);
|
||
}
|
||
|
||
void toggleShaderBackground() {
|
||
state = state.copyWith(shaderBackground: !state.shaderBackground);
|
||
}
|
||
|
||
/// 切换"了解新版本功能"开关
|
||
void toggleKnowNewFeatures() {
|
||
state = state.copyWith(knowNewFeatures: !state.knowNewFeatures);
|
||
}
|
||
|
||
void setLocale(String locale) {
|
||
state = state.copyWith(selectedLocale: locale);
|
||
Log.i('Onboarding: setLocale → $locale');
|
||
}
|
||
|
||
void setAgreementTabIndex(int index) {
|
||
state = state.copyWith(agreementTabIndex: index);
|
||
}
|
||
|
||
Future<void> completeOnboarding() async {
|
||
state = state.copyWith(isCompleting: true);
|
||
try {
|
||
await KvStorage.markLaunched();
|
||
await KvStorage.setBool('onboarding_completed', true);
|
||
await KvStorage.setShowOnboarding(state.showOnNextLaunch);
|
||
await KvStorage.setString('locale', state.selectedLocale);
|
||
await KvStorage.setBool('sfx_enabled', state.soundEnabled);
|
||
await KvStorage.setBool(
|
||
'general_shader_background',
|
||
state.shaderBackground,
|
||
);
|
||
// 保存"了解新版本功能"开关状态
|
||
await KvStorage.setBool('know_new_features', state.knowNewFeatures);
|
||
|
||
// 立即将关键数据刷入磁盘,防止App被杀后数据丢失导致重复显示引导页
|
||
try {
|
||
final appBox = HiveSafeAccess.tryGetBox<dynamic>(HiveBoxNames.app);
|
||
await appBox?.flush();
|
||
} catch (_) {}
|
||
|
||
// 通知原生端协议已同意,注册之前跳过的敏感插件(传感器/麦克风/位置等)
|
||
await _notifyNativeAgreementAccepted();
|
||
|
||
Log.i('Onboarding: completeOnboarding ✓ 用户已同意协议,开始初始化权限敏感服务');
|
||
await PostAgreementInitializer.init();
|
||
Log.i('Onboarding: 权限敏感服务初始化完成 ✓');
|
||
} catch (e, st) {
|
||
Log.e('Onboarding: completeOnboarding error', e, st);
|
||
} finally {
|
||
state = state.copyWith(isCompleting: false);
|
||
}
|
||
}
|
||
|
||
/// 通知原生端协议已同意(Android端补充注册敏感插件)
|
||
static const _agreementChannel = MethodChannel('apps.xy.xianyan/agreement');
|
||
|
||
Future<void> _notifyNativeAgreementAccepted() async {
|
||
if (!Platform.isAndroid) return;
|
||
try {
|
||
await _agreementChannel.invokeMethod<bool>('markAgreementAccepted');
|
||
Log.i('Onboarding: 原生端协议同意通知成功 ✓');
|
||
} catch (e) {
|
||
Log.e('Onboarding: 原生端协议同意通知失败', e);
|
||
}
|
||
}
|
||
}
|
||
|
||
final onboardingProvider =
|
||
NotifierProvider<OnboardingNotifier, OnboardingState>(
|
||
OnboardingNotifier.new,
|
||
);
|