本文用 is_lock_screen 2.0.0 在 Flutter 鸿蒙页面读取设备锁屏状态,并结合应用生命周期处理
truefalsenull 三种结果。它适合通话、敏感页面恢复和状态刷新辅助判断,但不能替代系统身份认证。

三方库仓库: https://atomgit.com/oh-flutter/is_lock_screen

本文锁定版本: a3574eae572c27aa4fdf97ca8d55b089a7696525

完整 Demo: is_lock_screen/example(受测提交)

一、最终真机效果

在这里插入图片描述

图 1:CHZ-AL00 / HarmonyOS 7.0.0.105 上展示锁屏查询结果和 Flutter 生命周期状态。

在这里插入图片描述

在这里插入图片描述

同一应用进程留下了 false -> true -> false 三个真实样本:设备解锁时为 false,进入锁屏后为 true,用户解锁并返回应用后再次为 false。由于锁屏期间 Dart timer 和日志可能暂停,这组结果来自同 PID 的跨采样窗口,不应写成单个连续自动用例已经通过。

返回值业务含义推荐处理
true系统报告设备已锁屏暂停敏感内容、等待恢复
false系统报告设备未锁屏再结合应用前后台状态处理
null平台读取失败或状态未知保守降级并记录错误,不能当成 false

二、适用范围与重要限制

应用进入后台不一定是锁屏,可能只是用户回到桌面或切换了应用;屏幕熄灭也不应直接当作系统已经完成锁定。这个库直接查询系统锁屏状态,比从亮度或 Flutter 生命周期猜测可靠,但它只提供一个时刻的快照。

OHOS 适配底层使用的 screenLock.isScreenLocked() 从 API 9 起已标记废弃,公开替代能力目前面向系统应用。本文在 API 26 指定真机上验证可用,不代表未来系统或所有设备都保证兼容。生产项目必须保留 null 分支,并建立自己的设备和系统版本矩阵。

涉及支付、口令或隐私数据时,锁屏状态只能作为界面脱敏信号。真正的身份确认仍应使用系统认证能力,服务端会话也不能只依赖这个布尔值。

三、环境与依赖

组件实测版本
Flutter OH3.41.10-ohos-1.0.1
Dart3.11.5
DevEco Studio26.0.0 Release
HarmonyOS SDKAPI 26,示例兼容 API 18
测试设备CHZ-AL00 / HarmonyOS 7.0.0.105
is_lock_screen2.0.0 / 上述受测提交

预览标签 3.44.9+ohos-0.0.1-canary1 未用于本文回归。适配尚未发布稳定 TAG,因此锁定完整 SHA:

dependencies:
  is_lock_screen:
    git:
      url: https://atomgit.com/oh-flutter/is_lock_screen.git
      ref: a3574eae572c27aa4fdf97ca8d55b089a7696525
flutter pub get

确认 pubspec.lockresolved-ref 后再构建。本库只读取锁屏状态,不执行锁定或解锁,也不需要新增鸿蒙权限。

在这里插入图片描述

图 2:AtomGit 仓库、适配分支和当前提交。

四、核心 API 与三态判断

import 'package:is_lock_screen/is_lock_screen.dart';

final bool? locked = await isLockScreen();
switch (locked) {
  case true:
    // 系统明确报告已锁屏
    break;
  case false:
    // 系统明确报告未锁屏
    break;
  case null:
    // 读取失败,保持未知状态
    break;
}

不要写成 final unlocked = !(locked ?? false)。这种表达会把读取错误当作已解锁,恰好在需要保守处理的场景中放宽保护。

五、跟随生命周期重新读取

import 'package:flutter/material.dart';
import 'package:is_lock_screen/is_lock_screen.dart';

class LockStatePage extends StatefulWidget {
  const LockStatePage({super.key});

  
  State<LockStatePage> createState() => _LockStatePageState();
}

class _LockStatePageState extends State<LockStatePage>
    with WidgetsBindingObserver {
  bool? _locked;
  bool _loading = false;
  String _lifecycle = 'resumed';
  int _requestId = 0;

  
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _refresh();
  }

  Future<void> _refresh() async {
    final requestId = ++_requestId;
    setState(() => _loading = true);
    final result = await isLockScreen();
    if (!mounted || requestId != _requestId) return;
    setState(() {
      _locked = result;
      _loading = false;
    });
  }

  
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (!mounted) return;
    setState(() => _lifecycle = state.name);
    if (state == AppLifecycleState.resumed ||
        state == AppLifecycleState.paused ||
        state == AppLifecycleState.inactive) {
      _refresh();
    }
  }

  
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    final label = switch (_locked) {
      true => '已锁屏',
      false => '未锁屏',
      null => '状态未知',
    };
    return Scaffold(
      appBar: AppBar(
        title: const Text('锁屏状态'),
        actions: [
          IconButton(
            tooltip: '刷新',
            onPressed: _loading ? null : _refresh,
            icon: const Icon(Icons.refresh),
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(_locked == true ? Icons.lock : Icons.lock_open, size: 48),
            const SizedBox(height: 12),
            Text(_loading ? '读取中' : label),
            Text('应用状态:$_lifecycle'),
          ],
        ),
      ),
    );
  }
}

请求编号用来丢弃晚到结果。锁屏期间应用可能被冻结,所以不要依靠 Timer.periodic 保证持续采样;最有价值的刷新时机通常是 resumed,即用户返回应用时。

在这里插入图片描述

图 3:OHOS 端直接调用系统锁屏查询,并把失败交给 Dart 转为 null

六、测试、构建与真机验收

flutter analyze
flutter test
cd example
flutter test
flutter build hap --debug --no-codesign

在这里插入图片描述

图 4:7 项 Dart/Widget 测试与静态检查结果。

在这里插入图片描述

图 5:HAP 构建结果及真机宿主锁定的 SHA。

在这里插入图片描述

图 6:同一 PID 跨采样窗口的 false -> true -> false,同时保留单窗口自动用例 incomplete 的事实。

正确的人工步骤是:先在解锁状态读取 false;按电源键并确认真实锁屏界面后等待采样;再由用户解锁并回到应用读取 false。不要用测试命令绕过锁屏认证,也不要仅凭应用进入 paused 就断言已锁屏。

七、常见问题

Q1:返回 null 是不是表示没锁屏

不是。null 表示读取失败或未知。敏感页面应保持遮挡、提示重试或要求重新认证。

Q2:按下电源键后为什么没有立即打印 true

应用进入后台后 Dart 任务可能暂停,且熄屏与锁定完成不是同一时刻。解锁回前台后再读取,必要时结合原生日志核对。

Q3:底层 API 已废弃还能上线吗

只能在你的目标系统矩阵验证后谨慎使用,并实现未知状态降级。本文只确认 HarmonyOS 7.0.0.105 / API 26 这一台设备。

八、总结

is_lock_screen 可以帮助 Flutter 鸿蒙应用区分真实锁屏与普通前后台切换。接入时最重要的是保留 bool? 三态、在生命周期恢复时重新读取,并把底层废弃 API 的兼容风险写进产品策略。本文已完成指定真机跨采样回环,但没有把它夸大成连续自动化或全系统兼容结论。

九、参考链接

欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐