chore: 发布v6.36.1版本,鸿蒙适配+隐私合规收尾
### 变更详情
1. **多语言国际化**:新增`rejectAndExit`翻译键,覆盖14种语言的拒绝退出文案
2. **鸿蒙端适配**:
- 修复USB事件监听报错,仅Android端启用原生USB监听
- 调整小部件管理页交互,鸿蒙端跳过Beta弹窗并显示"敬请期待"提示
- 适配通用设置页和功能检查的鸿蒙端toast替代弹窗逻辑
- 调整句子详情页"编辑"按钮的鸿蒙端交互
3. **隐私合规优化**:
- 移除剪贴板定时轮询,改为被动触发模式,仅在用户主动进入稍后读页面时检查剪贴板
- 移除Android端非核心权限,精简权限声明
- 新增启动页SplashActivity作为协议守门人,从根本上阻止敏感插件在协议前初始化
- 为Android端引导页新增双按钮布局,优化未勾选协议时的用户操作引导
4. **依赖与代码清理**:
- 修复macOS端flutter_secure_storage插件导入路径
- 简化字体导入逻辑,移除冗余的readAsBytes降级逻辑
- 更新CHANGELOG文档,记录版本变更细节
This commit is contained in:
@@ -75,7 +75,8 @@ class UsbDiscoveryService {
|
||||
_handleDeviceDetached(device);
|
||||
});
|
||||
|
||||
if (Platform.isAndroid || pu.isOhos) {
|
||||
// 仅Android端支持原生USB事件监听,鸿蒙端原生插件未实现xianyan/usb_events
|
||||
if (Platform.isAndroid) {
|
||||
_startNativeUsbEventListening();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import '../../../../core/providers/split_view_provider.dart';
|
||||
import '../../../../shared/widgets/feedback/app_toast.dart';
|
||||
import '../../../../shared/widgets/feedback/external_link_dialog.dart';
|
||||
import '../../../../shared/widgets/feedback/share_sheet.dart';
|
||||
import '../../../../core/utils/platform/platform_utils.dart' as pu;
|
||||
import '../../../../shared/widgets/media/tts_player_bar.dart';
|
||||
import '../../../../shared/widgets/interaction/animated_action_button.dart';
|
||||
import '../../../home/models/feed_model.dart';
|
||||
@@ -42,9 +43,14 @@ import 'sentence_detail_provider.dart';
|
||||
// ============================================================
|
||||
|
||||
class SentenceDetailActions extends ConsumerStatefulWidget {
|
||||
const SentenceDetailActions({required this.sentence, required this.cardKey, super.key});
|
||||
const SentenceDetailActions({
|
||||
required this.sentence,
|
||||
required this.cardKey,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final HomeSentence sentence;
|
||||
|
||||
/// 截图用GlobalKey,由父组件SentenceDetailPanel传入,确保每个面板实例独立
|
||||
final GlobalKey cardKey;
|
||||
|
||||
@@ -138,7 +144,9 @@ class _SentenceDetailActionsState extends ConsumerState<SentenceDetailActions> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: AnimatedActionButton(
|
||||
icon: sentence.isLiked ? CupertinoIcons.heart_fill : CupertinoIcons.hand_thumbsup,
|
||||
icon: sentence.isLiked
|
||||
? CupertinoIcons.heart_fill
|
||||
: CupertinoIcons.hand_thumbsup,
|
||||
label: sentence.isLiked ? '已点赞' : '点赞',
|
||||
isActive: sentence.isLiked,
|
||||
ext: ext,
|
||||
@@ -290,12 +298,16 @@ class _SentenceDetailActionsState extends ConsumerState<SentenceDetailActions> {
|
||||
.toggleTtsPlayer(sentence.text);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(CupertinoIcons.speaker, size: 14, color: ext.textPrimary),
|
||||
const SizedBox(width: 4),
|
||||
Text('朗读', style: TextStyle(color: ext.textPrimary)),
|
||||
],
|
||||
),
|
||||
children: [
|
||||
Icon(
|
||||
CupertinoIcons.speaker,
|
||||
size: 14,
|
||||
color: ext.textPrimary,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text('朗读', style: TextStyle(color: ext.textPrimary)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -311,6 +323,11 @@ class _SentenceDetailActionsState extends ConsumerState<SentenceDetailActions> {
|
||||
color: ext.accent,
|
||||
borderRadius: AppRadius.mdBorder,
|
||||
onPressed: () {
|
||||
// 鸿蒙端暂不支持编辑器,弹出toast提示
|
||||
if (pu.isOhos) {
|
||||
AppToast.showInfo('敬请期待');
|
||||
return;
|
||||
}
|
||||
context.appPush('/editor?text=${Uri.encodeComponent(sentence.text)}');
|
||||
},
|
||||
child: const Text(
|
||||
@@ -358,7 +375,11 @@ class _SentenceDetailActionsState extends ConsumerState<SentenceDetailActions> {
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(CupertinoIcons.hand_thumbsdown, size: 12, color: ext.accent),
|
||||
Icon(
|
||||
CupertinoIcons.hand_thumbsdown,
|
||||
size: 12,
|
||||
color: ext.accent,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text('不感兴趣', style: TextStyle(fontSize: 12, color: ext.accent)),
|
||||
],
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
/// ============================================================
|
||||
/// 闲言APP — 稍后读列表页面
|
||||
/// 创建时间: 2026-04-28
|
||||
/// 更新时间: 2026-06-09
|
||||
/// 更新时间: 2026-06-10
|
||||
/// 作用: 展示用户稍后读列表,支持搜索/筛选/排序/批量操作/富详情
|
||||
/// 上次更新: 移除本地UI状态字段,迁移至readLaterUiProvider
|
||||
/// 上次更新: 添加剪贴板被动触发(用户进入稍后读页面时检查URL)
|
||||
/// ============================================================
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:desktop_drop/desktop_drop.dart';
|
||||
|
||||
import '../../../../core/services/clipboard_monitor_service.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_typography.dart';
|
||||
import '../../../../core/utils/platform/platform_helper.dart';
|
||||
@@ -54,6 +55,8 @@ class _ReadLaterPageState extends ConsumerState<ReadLaterPage>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 被动触发:用户主动进入稍后读页面时检查剪贴板URL
|
||||
ClipboardMonitorService.instance.checkClipboardOnce();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
/// 上次更新: ext参数内部化、Mixin抽取、下拉刷新、骨架屏、评分接入、退出逻辑迁移
|
||||
/// ============================================================
|
||||
|
||||
import 'dart:io';
|
||||
// import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:custom_refresh_indicator/custom_refresh_indicator.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
// import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../../core/theme/app_spacing.dart';
|
||||
@@ -28,6 +28,7 @@ import '../../../../l10n/translations.dart';
|
||||
import '../../../../shared/widgets/containers/glass_container.dart';
|
||||
import '../../../../shared/widgets/adaptive/responsive_layout.dart';
|
||||
import '../../../../shared/widgets/feedback/app_toast.dart';
|
||||
import '../../../../core/utils/platform/platform_utils.dart' as pu;
|
||||
import '../../../../shared/widgets/input/setting_row.dart';
|
||||
import '../../settings/providers/theme_settings_provider.dart';
|
||||
import '../../settings/providers/general_settings_provider.dart';
|
||||
@@ -137,41 +138,37 @@ class _ProfilePageState extends ConsumerState<ProfilePage>
|
||||
|
||||
Future<void> _launchAppStore() async {
|
||||
final t = ref.read(translationsProvider);
|
||||
// 始终提示未找到应用商店
|
||||
// TODO: 后续恢复应用商店跳转逻辑
|
||||
AppToast.showInfo(t.profile.appStoreNotFound);
|
||||
try {
|
||||
// iOS App Store
|
||||
if (Platform.isIOS) {
|
||||
const appId = '6737492298';
|
||||
final uri = Uri.parse('https://apps.apple.com/app/id$appId');
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Android Google Play
|
||||
if (Platform.isAndroid) {
|
||||
final uri = Uri.parse(
|
||||
'market://details?id=apps.xy.xianyan',
|
||||
);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
return;
|
||||
}
|
||||
// 降级:打开网页版
|
||||
final webUri = Uri.parse(
|
||||
'https://play.google.com/store/apps/details?id=apps.xy.xianyan',
|
||||
);
|
||||
if (await canLaunchUrl(webUri)) {
|
||||
await launchUrl(webUri, mode: LaunchMode.externalApplication);
|
||||
return;
|
||||
}
|
||||
}
|
||||
AppToast.showInfo(t.profile.appStoreNotFound);
|
||||
} catch (e) {
|
||||
Log.e('_launchAppStore', e);
|
||||
AppToast.showInfo(t.profile.appStoreNotFound);
|
||||
}
|
||||
return;
|
||||
// // iOS App Store
|
||||
// if (Platform.isIOS) {
|
||||
// const appId = '6737492298';
|
||||
// final uri = Uri.parse('https://apps.apple.com/app/id$appId');
|
||||
// if (await canLaunchUrl(uri)) {
|
||||
// await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// // Android Google Play
|
||||
// if (Platform.isAndroid) {
|
||||
// final uri = Uri.parse(
|
||||
// 'market://details?id=apps.xy.xianyan',
|
||||
// );
|
||||
// if (await canLaunchUrl(uri)) {
|
||||
// await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
// return;
|
||||
// }
|
||||
// // 降级:打开网页版
|
||||
// final webUri = Uri.parse(
|
||||
// 'https://play.google.com/store/apps/details?id=apps.xy.xianyan',
|
||||
// );
|
||||
// if (await canLaunchUrl(webUri)) {
|
||||
// await launchUrl(webUri, mode: LaunchMode.externalApplication);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// AppToast.showInfo(t.profile.appStoreNotFound);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -237,15 +234,13 @@ class _ProfilePageState extends ConsumerState<ProfilePage>
|
||||
_buildProfileHeader(authState),
|
||||
|
||||
// —— 数据统计栏(仅登录) ——
|
||||
if (authState.isLoggedIn)
|
||||
_buildUserStatsBar(authState),
|
||||
if (authState.isLoggedIn) _buildUserStatsBar(authState),
|
||||
|
||||
// —— 收藏/历史/深色模式 ——
|
||||
_buildQuickSettingsSection(t),
|
||||
|
||||
// —— 账号/数据/离线/缓存(仅登录) ——
|
||||
if (authState.isLoggedIn)
|
||||
_buildAccountSection(t),
|
||||
if (authState.isLoggedIn) _buildAccountSection(t),
|
||||
|
||||
// —— 语言/主题/通用/小组件/来源 ——
|
||||
_buildGeneralSection(t, settings),
|
||||
@@ -271,376 +266,375 @@ class _ProfilePageState extends ConsumerState<ProfilePage>
|
||||
/// 顶部导航栏
|
||||
Widget _buildNavBar(AppThemeExtension ext, T t) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
CupertinoIcons.person_fill,
|
||||
size: 28,
|
||||
color: ext.accent,
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Text(
|
||||
t.navProfile,
|
||||
style: AppTypography.title1.copyWith(
|
||||
color: ext.textPrimary,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// —— 搜索按钮 ——
|
||||
Semantics(
|
||||
label: '搜索',
|
||||
button: true,
|
||||
child: GestureDetector(
|
||||
onTap: () => SpotlightSearchOverlay.show(context, ref),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: ext.accent.withValues(alpha: 0.12),
|
||||
borderRadius: AppRadius.mdBorder,
|
||||
),
|
||||
child: Icon(
|
||||
CupertinoIcons.search,
|
||||
size: 18,
|
||||
color: ext.accent,
|
||||
),
|
||||
child:
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.xs),
|
||||
// —— 快捷操作按钮 ——
|
||||
Semantics(
|
||||
label: '快捷操作',
|
||||
button: true,
|
||||
child: GestureDetector(
|
||||
onTap: () => showQuickActions(context, ref),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: ext.bgSecondary,
|
||||
borderRadius: AppRadius.mdBorder,
|
||||
),
|
||||
child: Icon(
|
||||
CupertinoIcons.plus,
|
||||
size: 18,
|
||||
color: ext.accent,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
CupertinoIcons.person_fill,
|
||||
size: 28,
|
||||
color: ext.accent,
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Text(
|
||||
t.navProfile,
|
||||
style: AppTypography.title1.copyWith(
|
||||
color: ext.textPrimary,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// —— 搜索按钮 ——
|
||||
Semantics(
|
||||
label: '搜索',
|
||||
button: true,
|
||||
child: GestureDetector(
|
||||
onTap: () => SpotlightSearchOverlay.show(context, ref),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: ext.accent.withValues(alpha: 0.12),
|
||||
borderRadius: AppRadius.mdBorder,
|
||||
),
|
||||
child: Icon(
|
||||
CupertinoIcons.search,
|
||||
size: 18,
|
||||
color: ext.accent,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.xs),
|
||||
// —— 快捷操作按钮 ——
|
||||
Semantics(
|
||||
label: '快捷操作',
|
||||
button: true,
|
||||
child: GestureDetector(
|
||||
onTap: () => showQuickActions(context, ref),
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: ext.bgSecondary,
|
||||
borderRadius: AppRadius.mdBorder,
|
||||
),
|
||||
child: Icon(
|
||||
CupertinoIcons.plus,
|
||||
size: 18,
|
||||
color: ext.accent,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms)
|
||||
.slideY(
|
||||
begin: 0.04,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms)
|
||||
.slideY(
|
||||
begin: 0.04,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 用户头像卡片
|
||||
Widget _buildProfileHeader(AuthState authState) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
child: ProfileHeader(authState: authState),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 50.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 50.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
child:
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
child: ProfileHeader(authState: authState),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 50.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 50.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 数据统计栏
|
||||
Widget _buildUserStatsBar(AuthState authState) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
child: UserStatsBar(user: authState.user),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 80.ms)
|
||||
.slideY(
|
||||
begin: 0.04,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 80.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
child:
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
child: UserStatsBar(user: authState.user),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 80.ms)
|
||||
.slideY(
|
||||
begin: 0.04,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 80.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 收藏 / 历史 / 深色模式
|
||||
Widget _buildQuickSettingsSection(T t) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
child: GlassContainer(
|
||||
depth: GlassDepth.elevated,
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.heart_fill,
|
||||
title: t.myFavorites,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.favorites,
|
||||
'favorites',
|
||||
child:
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.book_fill,
|
||||
title: t.readingHistory,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.history,
|
||||
'history',
|
||||
child: GlassContainer(
|
||||
depth: GlassDepth.elevated,
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.heart_fill,
|
||||
title: t.myFavorites,
|
||||
onTap: () =>
|
||||
navigateOrPanel(AppRoutes.favorites, 'favorites'),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.book_fill,
|
||||
title: t.readingHistory,
|
||||
onTap: () =>
|
||||
navigateOrPanel(AppRoutes.history, 'history'),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.moon_fill,
|
||||
title: t.darkMode,
|
||||
trailing: const DarkModeSwitch(),
|
||||
onTap: () {
|
||||
ref.read(themeModeBounceProvider.notifier).trigger();
|
||||
navigateOrPanel(
|
||||
AppRoutes.themeSettings,
|
||||
'theme_settings',
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 100.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 100.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.moon_fill,
|
||||
title: t.darkMode,
|
||||
trailing: const DarkModeSwitch(),
|
||||
onTap: () {
|
||||
ref.read(themeModeBounceProvider.notifier).trigger();
|
||||
navigateOrPanel(
|
||||
AppRoutes.themeSettings,
|
||||
'theme_settings',
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 100.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 100.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 账号设置 / 数据管理 / 离线模式 / 缓存管理
|
||||
Widget _buildAccountSection(T t) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
child: GlassContainer(
|
||||
depth: GlassDepth.elevated,
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.person_crop_circle,
|
||||
title: t.profile.accountSettings,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.accountSettings,
|
||||
'account_settings',
|
||||
child:
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.arrow_down_circle_fill,
|
||||
title: t.profile.dataManagement,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.dataManagement,
|
||||
'data_management',
|
||||
child: GlassContainer(
|
||||
depth: GlassDepth.elevated,
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.person_crop_circle,
|
||||
title: t.profile.accountSettings,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.accountSettings,
|
||||
'account_settings',
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.arrow_down_circle_fill,
|
||||
title: t.profile.dataManagement,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.dataManagement,
|
||||
'data_management',
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.antenna_radiowaves_left_right,
|
||||
title: t.profile.offlineMode,
|
||||
onTap: () =>
|
||||
navigateOrPanel(AppRoutes.offline, 'offline_mode'),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.tray_full_fill,
|
||||
title: t.cacheManagement,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.cacheManagement,
|
||||
'cache_management',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 140.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 140.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.antenna_radiowaves_left_right,
|
||||
title: t.profile.offlineMode,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.offline,
|
||||
'offline_mode',
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.tray_full_fill,
|
||||
title: t.cacheManagement,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.cacheManagement,
|
||||
'cache_management',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 140.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 140.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 语言 / 主题定制 / 通用设置 / 桌面小组件 / 句子来源
|
||||
Widget _buildGeneralSection(T t, GeneralSettingsState settings) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
child: GlassContainer(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.globe,
|
||||
title: t.language,
|
||||
trailing: Text(
|
||||
AppLocale.fromId(settings.languageId).nativeName,
|
||||
style: AppTypography.subhead.copyWith(
|
||||
color: AppTheme.ext(context).textSecondary,
|
||||
child:
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
child: GlassContainer(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.globe,
|
||||
title: t.language,
|
||||
trailing: Text(
|
||||
AppLocale.fromId(settings.languageId).nativeName,
|
||||
style: AppTypography.subhead.copyWith(
|
||||
color: AppTheme.ext(context).textSecondary,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
ref.read(languageBounceProvider.notifier).trigger();
|
||||
navigateOrPanel(
|
||||
AppRoutes.languageSettings,
|
||||
'language_settings',
|
||||
);
|
||||
},
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.paintbrush_fill,
|
||||
title: t.themeCustomization,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.themeSettings,
|
||||
'theme_settings',
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.gear_solid,
|
||||
title: t.generalSettings,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.generalSettings,
|
||||
'general_settings',
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.square_grid_2x2,
|
||||
title: t.desktopWidgets,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.widgetManagement,
|
||||
'widget_management',
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.book,
|
||||
title: t.sentenceSource,
|
||||
onTap: () =>
|
||||
navigateOrPanel(AppRoutes.source, 'source'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
ref.read(languageBounceProvider.notifier).trigger();
|
||||
navigateOrPanel(
|
||||
AppRoutes.languageSettings,
|
||||
'language_settings',
|
||||
);
|
||||
},
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 180.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 180.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.paintbrush_fill,
|
||||
title: t.themeCustomization,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.themeSettings,
|
||||
'theme_settings',
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.gear_solid,
|
||||
title: t.generalSettings,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.generalSettings,
|
||||
'general_settings',
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.square_grid_2x2,
|
||||
title: t.desktopWidgets,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.widgetManagement,
|
||||
'widget_management',
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.book,
|
||||
title: t.sentenceSource,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.source,
|
||||
'source',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 180.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 180.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 关于 / 评分 / 实验性功能
|
||||
Widget _buildAboutSection(T t) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
child: GlassContainer(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.info_circle,
|
||||
title: t.aboutApp,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.about,
|
||||
'about',
|
||||
child:
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.star_fill,
|
||||
title: t.rateApp,
|
||||
onTap: _launchAppStore,
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.lab_flask_solid,
|
||||
title: t.profile.experimentalFeature,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.experimentalFeatures,
|
||||
'experimental_features',
|
||||
child: GlassContainer(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.info_circle,
|
||||
title: t.aboutApp,
|
||||
onTap: () => navigateOrPanel(AppRoutes.about, 'about'),
|
||||
),
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.star_fill,
|
||||
title: t.rateApp,
|
||||
onTap: _launchAppStore,
|
||||
),
|
||||
// 鸿蒙端不显示Beta/实验性功能入口
|
||||
if (!pu.isOhos) ...[
|
||||
const SettingsDivider(),
|
||||
SettingRow(
|
||||
icon: CupertinoIcons.lab_flask_solid,
|
||||
title: t.profile.experimentalFeature,
|
||||
onTap: () => navigateOrPanel(
|
||||
AppRoutes.experimentalFeatures,
|
||||
'experimental_features',
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 220.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 220.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.animate(onPlay: (c) => c.forward())
|
||||
.fadeIn(duration: 300.ms, delay: 220.ms)
|
||||
.slideY(
|
||||
begin: 0.05,
|
||||
end: 0,
|
||||
duration: 300.ms,
|
||||
delay: 220.ms,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,25 +685,18 @@ class FontManagementNotifier extends Notifier<FontManagementState>
|
||||
}
|
||||
}
|
||||
|
||||
// 路径不可用时,使用 readAsBytes()(file_picker 12.x 推荐)
|
||||
// 路径不可用时,使用 bytes 属性读取(file_picker 11.x API)
|
||||
if (zipBytes == null) {
|
||||
try {
|
||||
zipBytes = await platformFile.readAsBytes();
|
||||
} catch (e) {
|
||||
Log.w('ZIP字体导入 readAsBytes 失败', e);
|
||||
final rawBytes = platformFile.bytes;
|
||||
if (rawBytes != null && rawBytes.isNotEmpty) {
|
||||
zipBytes = rawBytes;
|
||||
}
|
||||
}
|
||||
|
||||
// 最终降级:bytes 属性(兼容旧版)
|
||||
if (zipBytes == null || zipBytes.isEmpty) {
|
||||
// ignore: deprecated_member_use
|
||||
final rawBytes = platformFile.bytes;
|
||||
if (rawBytes == null || rawBytes.isEmpty) {
|
||||
Log.w('ZIP字体导入跳过: 无可用文件路径或数据');
|
||||
AppToast.showWarning('无法读取ZIP文件数据');
|
||||
return;
|
||||
}
|
||||
zipBytes = rawBytes;
|
||||
Log.w('ZIP字体导入跳过: 无可用文件路径或数据');
|
||||
AppToast.showWarning('无法读取ZIP文件数据');
|
||||
return;
|
||||
}
|
||||
|
||||
final archive = ZipDecoder().decodeBytes(zipBytes);
|
||||
|
||||
@@ -28,6 +28,7 @@ import '../../../../../../shared/widgets/adaptive/adaptive_back_button.dart';
|
||||
import '../../../../onboarding/presentation/onboarding_page.dart';
|
||||
import '../../../../../../core/utils/platform/platform_utils.dart' as pu;
|
||||
import '../../../../../../shared/widgets/containers/glass_container.dart';
|
||||
import '../../../../../../shared/widgets/feedback/app_toast.dart';
|
||||
import '../../providers/general_settings_provider.dart';
|
||||
import '../../providers/plugin_provider.dart';
|
||||
import '../../../../../../core/providers/split_view_provider.dart';
|
||||
@@ -566,6 +567,11 @@ class _GeneralSettingsPageState extends ConsumerState<GeneralSettingsPage>
|
||||
HapticService.selection();
|
||||
final message =
|
||||
flag?.unsupportedMessage ?? t.common.featureNotSupported;
|
||||
// 鸿蒙端不显示弹窗,改为toast提示
|
||||
if (pu.isOhos) {
|
||||
AppToast.showInfo('敬请期待');
|
||||
return;
|
||||
}
|
||||
showCupertinoDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => CupertinoAlertDialog(
|
||||
|
||||
@@ -335,24 +335,8 @@ class FontDownloadService {
|
||||
}
|
||||
}
|
||||
|
||||
// 路径不可用时,使用 readAsBytes() 读取(file_picker 12.x 推荐)
|
||||
// 路径不可用时,使用 bytes 属性读取(file_picker 11.x API)
|
||||
if (fontBytes == null) {
|
||||
try {
|
||||
fontBytes = await platformFile.readAsBytes();
|
||||
if (fontBytes.isNotEmpty) {
|
||||
if (await File(destPath).exists()) {
|
||||
await File(destPath).delete();
|
||||
}
|
||||
await File(destPath).writeAsBytes(fontBytes);
|
||||
}
|
||||
} catch (e) {
|
||||
Log.w('字体导入 readAsBytes 失败: $fileName', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 最终降级:使用 bytes 属性(兼容旧版 file_picker)
|
||||
if (fontBytes == null || fontBytes.isEmpty) {
|
||||
// ignore: deprecated_member_use
|
||||
final rawBytes = platformFile.bytes;
|
||||
if (rawBytes != null && rawBytes.isNotEmpty) {
|
||||
fontBytes = rawBytes;
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
// ============================================================
|
||||
// 闲言APP — 软件协议页(引导页)
|
||||
// 创建时间: 2026-05-21
|
||||
// 更新时间: 2026-06-09
|
||||
// 更新时间: 2026-06-10
|
||||
// 作用: 隐私政策/用户协议/权限说明,勾选同意后继续
|
||||
// 上次更新: 添加滚动进度条;导航改用OnboardingNavScope语义索引适配安卓端页面顺序
|
||||
// 上次更新: Android端协议页改用双按钮(同意并继续+拒绝并退出),移除勾选框
|
||||
// ============================================================
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart'
|
||||
show Colors, Scrollbar, LinearProgressIndicator;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../core/constants/character_expression.dart';
|
||||
@@ -201,9 +204,15 @@ class _AgreementPageState extends ConsumerState<AgreementPage> {
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
Expanded(child: _buildContent(ext, state, ob, languageId)),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
_buildCheckboxes(ext, state, notifier, ob),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
_buildContinueButton(ext, state, notifier, ob),
|
||||
Platform.isAndroid
|
||||
? _buildAndroidButtons(ext, state, notifier, ob)
|
||||
: Column(
|
||||
children: [
|
||||
_buildCheckboxes(ext, state, notifier, ob),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
_buildContinueButton(ext, state, notifier, ob),
|
||||
],
|
||||
),
|
||||
SizedBox(height: isLandscape ? AppSpacing.xs : AppSpacing.sm),
|
||||
],
|
||||
),
|
||||
@@ -924,6 +933,61 @@ class _AgreementPageState extends ConsumerState<AgreementPage> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Android端:两个按钮布局(同意并继续 + 拒绝并退出),无勾选框
|
||||
Widget _buildAndroidButtons(
|
||||
AppThemeExtension ext,
|
||||
OnboardingState state,
|
||||
OnboardingNotifier notifier,
|
||||
TOnboarding ob,
|
||||
) {
|
||||
return Column(
|
||||
children: [
|
||||
// 同意并继续
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: CupertinoButton.filled(
|
||||
borderRadius: AppRadius.xlBorder,
|
||||
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
|
||||
onPressed: () {
|
||||
HapticService.light();
|
||||
// 自动勾选所有协议
|
||||
if (!state.privacyAgreed) notifier.togglePrivacy();
|
||||
if (!state.termsAgreed) notifier.toggleTerms();
|
||||
if (!state.permissionRead) notifier.togglePermissionRead();
|
||||
// 跳转到下一页
|
||||
final nav = OnboardingNavScope.of(context);
|
||||
nav.goToPage(nav.agreementIndex + 1);
|
||||
},
|
||||
child: Text(
|
||||
ob.agreeAndContinue,
|
||||
style: AppTypography.headline.copyWith(color: ext.textOnAccent),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
// 拒绝并退出
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: CupertinoButton(
|
||||
borderRadius: AppRadius.xlBorder,
|
||||
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
|
||||
color: const Color(0xFFFF3B30).withValues(alpha: 0.12),
|
||||
onPressed: () {
|
||||
SystemNavigator.pop();
|
||||
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
},
|
||||
child: Text(
|
||||
ob.rejectAndExit,
|
||||
style: AppTypography.headline.copyWith(
|
||||
color: const Color(0xFFFF3B30),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCheckboxes(
|
||||
AppThemeExtension ext,
|
||||
OnboardingState state,
|
||||
@@ -1049,24 +1113,47 @@ class _AgreementPageState extends ConsumerState<AgreementPage> {
|
||||
OnboardingNotifier notifier,
|
||||
TOnboarding ob,
|
||||
) {
|
||||
final enabled = state.canProceedAgreement;
|
||||
final agreed = state.canProceedAgreement;
|
||||
|
||||
if (agreed) {
|
||||
// 已勾选两个协议 → 显示"同意并继续"
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: CupertinoButton.filled(
|
||||
borderRadius: AppRadius.xlBorder,
|
||||
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
|
||||
onPressed: () {
|
||||
HapticService.light();
|
||||
// 跳转到协议页的下一页(安卓端是欢迎页,其他端是个性化页)
|
||||
final nav = OnboardingNavScope.of(context);
|
||||
nav.goToPage(nav.agreementIndex + 1);
|
||||
},
|
||||
child: Text(
|
||||
ob.agreeAndContinue,
|
||||
style: AppTypography.headline.copyWith(color: ext.textOnAccent),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 未勾选协议 → 显示"拒绝并退出"(红色警告按钮)
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: CupertinoButton.filled(
|
||||
child: CupertinoButton(
|
||||
borderRadius: AppRadius.xlBorder,
|
||||
padding: const EdgeInsets.symmetric(vertical: AppSpacing.md),
|
||||
onPressed: enabled
|
||||
? () {
|
||||
HapticService.light();
|
||||
// 跳转到协议页的下一页(安卓端是欢迎页,其他端是个性化页)
|
||||
final nav = OnboardingNavScope.of(context);
|
||||
nav.goToPage(nav.agreementIndex + 1);
|
||||
}
|
||||
: null,
|
||||
color: const Color(0xFFFF3B30).withValues(alpha: 0.12),
|
||||
onPressed: () {
|
||||
// 退出应用
|
||||
SystemNavigator.pop();
|
||||
if (Platform.isAndroid) {
|
||||
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
ob.agreeAndContinue,
|
||||
ob.rejectAndExit,
|
||||
style: AppTypography.headline.copyWith(
|
||||
color: enabled ? ext.textOnAccent : ext.textDisabled,
|
||||
color: const Color(0xFFFF3B30),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
// ============================================================
|
||||
// 闲言APP — 引导页状态管理
|
||||
// 创建时间: 2026-05-21
|
||||
// 更新时间: 2026-06-09
|
||||
// 更新时间: 2026-06-10
|
||||
// 作用: 管理引导页流程状态(页面切换、协议勾选、个性化设置)
|
||||
// 上次更新: 添加totalPages动态页数支持,适配安卓端页面顺序调整
|
||||
// 上次更新: 协议同意后通知原生端注册敏感插件(传感器/麦克风/位置等)
|
||||
// ============================================================
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../core/services/post_agreement_initializer.dart';
|
||||
@@ -158,6 +161,9 @@ class OnboardingNotifier extends Notifier<OnboardingState> {
|
||||
await appBox?.flush();
|
||||
} catch (_) {}
|
||||
|
||||
// 通知原生端协议已同意,注册之前跳过的敏感插件(传感器/麦克风/位置等)
|
||||
await _notifyNativeAgreementAccepted();
|
||||
|
||||
Log.i('Onboarding: completeOnboarding ✓ 用户已同意协议,开始初始化权限敏感服务');
|
||||
await PostAgreementInitializer.init();
|
||||
Log.i('Onboarding: 权限敏感服务初始化完成 ✓');
|
||||
@@ -167,6 +173,19 @@ class OnboardingNotifier extends Notifier<OnboardingState> {
|
||||
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 =
|
||||
|
||||
@@ -49,7 +49,10 @@ class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
|
||||
}
|
||||
|
||||
/// 开发中弹窗提示,支持"不再提醒"
|
||||
/// 鸿蒙端不显示此弹窗
|
||||
void _showDevDialogIfNeeded() {
|
||||
// 鸿蒙端不显示Beta弹窗
|
||||
if (PlatformHelper.isHarmonyOS) return;
|
||||
final dismissed = KvStorage.getBool('widget_dev_dismissed') ?? false;
|
||||
if (dismissed) return;
|
||||
showCupertinoDialog<void>(
|
||||
@@ -255,6 +258,11 @@ class _WidgetManagementPageState extends ConsumerState<WidgetManagementPage> {
|
||||
};
|
||||
|
||||
void _handleAddWidget(WidgetType type) async {
|
||||
// 鸿蒙端点击添加弹出toast提示
|
||||
if (PlatformHelper.isHarmonyOS) {
|
||||
AppToast.showInfo('敬请期待');
|
||||
return;
|
||||
}
|
||||
if (_isAdding) return;
|
||||
_isAdding = true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user