Flutter for OpenHarmony 实战:device_info_plus 精准获取鸿蒙设备参数

在这里插入图片描述

前言

在进行 HarmonyOS NEXT 适配时,我们经常需要准确识别硬件形态:当前是 Mate 60 Pro 手机还是 MatePad 平板?系统版本是首个全自研正式版还是开发测试版?

获取这些硬件与系统的“身份标识”,是实现差异化体验、数据埋点以及 Bug 追踪的基础。device_info_plus 配合专门的鸿蒙适配库,为我们提供了获取底层参数的最佳链路。


一、 为什么在鸿蒙开发中使用 device_info_plus?

1.1 精准的硬件适配

鸿蒙设备形态多样(折叠屏、平板、智慧屏)。通过识别 deviceTypemarketName,开发者可以动态开启特定 UI(如双栏布局、高采样率动效)。

1.2 系统合规与权限防范

通过获取 sdkApiVersion,可以精准判断环境,动态调整安全策略或 API 调用。


二、 技术内幕:鸿蒙特有的字段映射

针对鸿蒙平台,我们需要关注 OhosDeviceInfo 对象。

字段名 含义 鸿蒙实战价值
osFullName 操作系统全称 用于“关于”页面,展示鸿蒙纯血身份
sdkApiVersion 系统 API 级别 判断是否支持特定的鸿蒙原生功能
marketName 市场营销名 展示给用户看的设备型号(如 HUAWEI Mate 60 Pro)
udid 唯一设备标识 深度权限限制,仅限系统 App 使用

三、 集成指南

3.1 添加依赖

在鸿蒙环境下,建议显式引入鸿蒙适配库:

dependencies:
  device_info_plus: any
  device_info_plus_ohos: any

四、 核心关键技术分解

4.1 基础鸿蒙参数获取

通过 DeviceInfoOhosPlugin 提取核心硬件特征。

📂 示例文件:lib/device-info/device_info_basic_4_1.dart

import 'package:device_info_plus_ohos/device_info_plus_ohos.dart';

final DeviceInfoOhosPlugin deviceInfoOhosPlugin = DeviceInfoOhosPlugin();

Future<void> loadInfo() async {
  // 获取基础信息
  OhosDeviceInfo deviceInfo = await deviceInfoOhosPlugin.ohosDeviceInfo;
  print('设备名称: ${deviceInfo.marketName}');
  print('API 级别: ${deviceInfo.sdkApiVersion}');
}

在这里插入图片描述

4.2 深度适配:获取系统级唯一标识 (UDID)

这是一个受限 API,涉及核心隐私,需要特定权限并仅限系统应用。

// 💡 注意:需要权限 ohos.permission.sec.ACCESS_UDID
// 该权限为系统级权限,仅对系统应用开放
OhosAccessUDIDInfo accessUDIDInfo = await deviceInfoOhosPlugin.ohosAccessUDIDInfo;
print('UDID: ${accessUDIDInfo.udid}');

在这里插入图片描述

4.3 为什么我的 UDID 识别为空?

HarmonyOS NEXT 调试过程中,如果你发现 UDID 返回为空字符串,这通常不是 Bug,而是系统安全机制:

  1. 系统权限拦截ACCESS_UDID 属于系统权限 (System Permission)。在鸿蒙设备上,普通三方 App 在即使申请了此权限也无法通过系统授权。
  2. 模拟器局限性:部分模拟器环境并未模拟出真实的底层硬件序列号。
  3. 工业级替代方案
    • 业务身份识别:推荐使用 OhosDeviceInfo.idOhosDeviceInfo.odid (匿名设备标识符)。
    • 持久化随机 ID:在应用首次启动时由 Flutter 生成一个 uuid,并存储在本地持久化。

五、 完整示例:鸿蒙设备诊断中心

打造高颜值的硬件参数面板,集成全量鸿蒙设备元数据。

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

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

  
  State<DeviceInfoFullPage> createState() => _DeviceInfoFullPageState();
}

class _DeviceInfoFullPageState extends State<DeviceInfoFullPage> {
  final DeviceInfoOhosPlugin _plugin = DeviceInfoOhosPlugin();
  Map<String, dynamic> _infoMap = {};
  bool _isLoading = true;

  
  void initState() {
    super.initState();
    _initData();
  }

  Future<void> _initData() async {
    try {
      final info = await _plugin.ohosDeviceInfo;
      setState(() {
        // 💡 演示:将对象属性映射为 UI 可读的键值对
        _infoMap = {
          '市场名称': info.marketName,
          '产品型号': info.productModel,
          '系统类型': info.osFullName,
          '设备类型': info.deviceType,
          'SDK API 级别': info.sdkApiVersion,
          '显示版本': info.displayVersion,
          '硬件模型': info.hardwareModel,
          '版本特征值': info.buildRootHash,
        };
        _isLoading = false;
      });
    } catch (e) {
      debugPrint("数据初始化失败: $e");
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF5F5F7),
      appBar: AppBar(
        title: const Text('鸿蒙设备智感详情'),
        centerTitle: true,
        backgroundColor: Colors.white,
        foregroundColor: Colors.black,
        elevation: 0,
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : CustomScrollView(
              slivers: [
                _buildHeader(),
                SliverPadding(
                  padding: const EdgeInsets.all(16),
                  sliver: SliverList(
                    delegate: SliverChildBuilderDelegate(
                      (context, index) {
                        final key = _infoMap.keys.elementAt(index);
                        return _buildInfoCard(key, _infoMap[key].toString());
                      },
                      childCount: _infoMap.length,
                    ),
                  ),
                ),
              ],
            ),
    );
  }

  Widget _buildHeader() {
    return SliverToBoxAdapter(
      child: Container(
        margin: const EdgeInsets.all(16),
        padding: const EdgeInsets.all(24),
        decoration: BoxDecoration(
          gradient: const LinearGradient(
            colors: [Color(0xFF007DFF), Color(0xFF00C7FF)],
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
          ),
          borderRadius: BorderRadius.circular(20),
          boxShadow: [
            BoxShadow(
                color: const Color(0xFF007DFF).withOpacity(0.3),
                blurRadius: 20,
                offset: const Offset(0, 8)),
          ],
        ),
        child: Column(
          children: [
            const Icon(Icons.devices_other, size: 64, color: Colors.white),
            const SizedBox(height: 16),
            Text(
              _infoMap['市场名称'] ?? '探测中...',
              style: const TextStyle(
                  color: Colors.white,
                  fontSize: 22,
                  fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
              decoration: BoxDecoration(
                  color: Colors.white24,
                  borderRadius: BorderRadius.circular(20)),
              child: const Text(
                '鸿蒙星辰版 (Next) 已就绪',
                style: TextStyle(color: Colors.white, fontSize: 12),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildInfoCard(String label, String value) {
    return Container(
      margin: const EdgeInsets.only(bottom: 12),
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
      ),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 14)),
          Flexible(
            child: Text(
              value,
              textAlign: TextAlign.right,
              style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
              overflow: TextOverflow.ellipsis,
            ),
          ),
        ],
      ),
    );
  }
}

在这里插入图片描述


六、 适配鸿蒙的避坑指南

6.1 UDID 权限风险

绝大多数三方应用无法获取 UDID。在涉及唯一性校验时,建议使用 id 字段或业务生成的 UUID 存储在本地。

6.2 模拟器返回逻辑

在远程模拟器或工程机上,部分字段(如 serial)可能会返回 “unknown”,务必在代码中加入空保护或兜底字符串。


七、 总结

获取设备信息是 App “精细化运营”的第一步。通过对 device_info_plus_ohos 的深度应用,我们不仅能获取数据,更能看懂数据背后的设备形态,从而在鸿蒙全场景生态下,打造出极致适配的跨端体验。


🌐 欢迎加入开源鸿蒙跨平台社区开源鸿蒙跨平台开发者社区

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐