Files
conti-retail-app/packages/native_scan/lib/native_scan.dart
T
2026-08-17 15:29:55 +08:00

99 lines
3.6 KiB
Dart
Raw 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.
/// 扫码能力的对外 API。来源:conti-docs/07-native-integration.md。
///
/// **调用方只允许 import 这个文件**,不允许直接 import `src/generated/` 里的
/// 生成代码(07 §使用规则)。调用方包括 `feature_scan` 和 `core_webview` 的
/// JSBridge——后者正是 01 里「`core_*` 允许依赖 `native_*`」这条例外存在的
/// 原因。
///
/// 本包按 01 的硬约束**不依赖仓库内任何其他包**(连 `core_foundation` 也不),
/// 所以这里抛的是包内自定义的 [NativeScanException];转成统一错误体系里的
/// `NativeException` 由调用方完成。
library;
import 'package:flutter/services.dart';
import 'src/generated/scan_api.g.dart';
export 'src/generated/scan_api.g.dart' show ScanMode, ScanOptions, ScanResult;
/// 扫码失败的错误码。
///
/// 取值与 12 的 `NativeException.code` 对齐,调用方可以直接透传。
abstract final class NativeScanErrorCode {
/// 用户主动取消。
///
/// 这不是异常流程,调用方通常应当静默返回,**不弹错误提示、不上报**。
static const String cancelled = 'CANCELLED';
/// 相机权限被拒绝。
static const String permissionDenied = 'PERMISSION_DENIED';
/// 能力暂时不可用(相机被占用、初始化失败等)。
static const String unavailable = 'UNAVAILABLE';
/// 当前平台没有实现。
///
/// 见 07 §「OHOS 后续演进」:**不允许静默返回空值或占位假数据**——
/// 静默返回会让"这个平台其实没实现"的问题一直藏到用户手里。
static const String unsupportedPlatform = 'UNSUPPORTED_PLATFORM';
/// 超时。
static const String timeout = 'TIMEOUT';
}
/// 扫码相关的异常。
class NativeScanException implements Exception {
/// [code] 取自 [NativeScanErrorCode]。
const NativeScanException(this.code, this.message);
/// 稳定错误码。
final String code;
/// 面向开发者的描述。**不要直接展示给用户**——文案由调用方按 12 的
/// `ErrorPresenter` 决定。
final String message;
/// 是否是用户主动取消。
bool get isCancelled => code == NativeScanErrorCode.cancelled;
@override
String toString() => 'NativeScanException($code): $message';
}
/// 扫码。
class NativeScan {
/// [api] 仅供测试注入;生产走默认实例。
NativeScan({ScanHostApi? api}) : _api = api ?? ScanHostApi();
final ScanHostApi _api;
/// 打开扫码页并等待一次结果。
///
/// 用户取消时抛 [NativeScanException]`code == CANCELLED`)而不是返回 null——
/// 「取消」和「扫到了空字符串」必须能区分开。
Future<ScanResult> startScan(ScanOptions options) async {
try {
return await _api.startScan(options);
} on PlatformException catch (e) {
// 原生异常类型不外泄(07 §使用规则)。
throw NativeScanException(e.code, e.message ?? '扫码失败');
} on MissingPluginException {
throw const NativeScanException(NativeScanErrorCode.unsupportedPlatform, '当前平台未实现扫码能力');
}
}
/// 当前平台是否支持指定识别类型。
///
/// 调用方应当先查这个再决定要不要显示入口,而不是等 [startScan] 抛错——
/// 车牌识别的技术路径尚未确定(07 待确认项),首版可能只有部分平台支持。
Future<bool> isModeSupported(ScanMode mode) async {
try {
return await _api.isModeSupported(mode);
} on PlatformException {
return false;
} on MissingPluginException {
return false;
}
}
}