Files
xianyan/lib/core/services/accessibility_service.dart
Developer f91be94e9c refactor: 完成项目架构重构,统一模块导入路径
- 清理大量废弃的 barrel 导出文件,移除冗余的中间导出层
- 修复所有相对路径导入错误,统一调整为扁平化模块引用
- 更新多平台 pubspec 版本号与依赖库版本
- 补充后端功能问题管理后台与脚本工具
- 调整部分页面的快捷方式文案适配新功能
- 更新部分翻译覆盖率与API文档
2026-06-12 08:53:57 +08:00

132 lines
4.1 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// ============================================================
/// 闲言APP — 无障碍服务
/// 创建时间: 2026-06-05
/// 更新时间: 2026-06-12
/// 作用: 管理VoiceOver/TalkBack适配提供无障碍模式检测、
/// 语义标注辅助、设置管理
/// 上次更新: 从 accessibility/ 子目录上移至 services/ 目录
/// ============================================================
import 'package:flutter/semantics.dart';
import 'package:flutter/widgets.dart';
import '../utils/logger.dart';
/// 无障碍服务 — 单例管理VoiceOver/TalkBack适配
///
/// 职责:
/// - 检测系统无障碍状态(高对比度、减少动画、粗体文本)
/// - 管理语义调试开关SemanticsDebugger
/// - 提供语义标注辅助方法
class AccessibilityService {
AccessibilityService._();
static AccessibilityService? _instance;
static AccessibilityService get instance => _instance ??= AccessibilityService._();
// ── 状态字段 ──
/// 是否启用语义调试(开发者选项)
bool _semanticsDebugEnabled = false;
/// 是否高对比度模式(系统级,只读)
bool _highContrastEnabled = false;
/// 是否减少动画(系统级,只读)
bool _reducedMotionEnabled = false;
/// 是否粗体文本(系统级,只读)
bool _boldTextEnabled = false;
// ── Getter ──
/// 是否启用语义调试
bool get semanticsDebugEnabled => _semanticsDebugEnabled;
/// 是否高对比度模式
bool get highContrastEnabled => _highContrastEnabled;
/// 是否减少动画
bool get reducedMotionEnabled => _reducedMotionEnabled;
/// 是否粗体文本
bool get boldTextEnabled => _boldTextEnabled;
// ── 初始化与更新 ──
/// 从BuildContext更新系统无障碍状态
///
/// 应在App根组件build时调用以同步系统级无障碍设置
void updateFromContext(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
final changed = _highContrastEnabled != mediaQuery.highContrast ||
_reducedMotionEnabled != mediaQuery.disableAnimations ||
_boldTextEnabled != mediaQuery.boldText;
_highContrastEnabled = mediaQuery.highContrast;
_reducedMotionEnabled = mediaQuery.disableAnimations;
_boldTextEnabled = mediaQuery.boldText;
if (changed) {
Log.i(
'无障碍状态更新: 高对比度=$_highContrastEnabled, '
'减少动画=$_reducedMotionEnabled, '
'粗体文本=$_boldTextEnabled',
null,
null,
LogCategory.service,
);
}
}
/// 初始化语义调试状态(从持久化存储恢复)
void initSemanticsDebug(bool enabled) {
_semanticsDebugEnabled = enabled;
Log.i('无障碍服务初始化: 语义调试=$enabled', null, null, LogCategory.service);
}
/// 切换语义调试开关
///
/// 开启后会在应用最外层显示SemanticsDebugger覆盖层
/// 方便开发者检查语义标注是否正确
void setSemanticsDebug(bool enabled) {
if (_semanticsDebugEnabled == enabled) return;
_semanticsDebugEnabled = enabled;
Log.i('语义调试切换: $enabled', null, null, LogCategory.service);
// 通知语义调试状态变化
// ignore: deprecated_member_use
SemanticsService.announce(
enabled ? '语义调试已开启' : '语义调试已关闭',
TextDirection.ltr,
);
}
// ── 语义标注辅助 ──
/// 为卡片类组件生成语义标注字符串
///
/// [title] 卡片标题
/// [subtitle] 卡片副标题
/// [extra] 额外信息(如点赞数、评论数)
static String cardLabel({
required String title,
String? subtitle,
String? extra,
}) {
final parts = [title];
if (subtitle != null && subtitle.isNotEmpty) parts.add(subtitle);
if (extra != null && extra.isNotEmpty) parts.add(extra);
return parts.join('');
}
/// 为开关类组件生成语义hint
static String toggleHint(String label, bool currentValue) {
return currentValue ? '关闭$label' : '开启$label';
}
/// 为列表项生成语义value
static String listItemValue(int index, int total) {
return '${index + 1}项,共$total项';
}
}