Flutter三方库适配OpenHarmony【apple_product_name】5分钟快速上手指南

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

本文将通过简洁明了的步骤,帮助开发者在5分钟 内快速上手apple_product_name库,实现在OpenHarmony设备上获取友好的产品名称 。对于初次接触该库的开发者来说,快速上手指南能够帮助你在最短的时间内理解库的核心功能并跑通第一个示例程序。本文将从添加依赖开始,逐步引导你完成库的集成、API调用和界面展示,每个步骤都配有项目中的真实代码和详细说明。

本文是apple_product_name OpenHarmony适配系列的第3篇,共30篇,带你从零到一跑通第一个设备名称查询示例。

一、快速上手三步走

1.1 整体流程概览

快速上手只需三个核心步骤:

  1. 添加依赖 :在pubspec.yaml中声明Git依赖
  2. 安装依赖 :执行flutter pub get拉取代码
  3. 调用API :导入库文件,调用getProductName()

整个过程不超过5分钟,下面逐步详解每个步骤。

1.2 前置条件

在开始之前,请确认以下环境已就绪:

环境项 最低版本 验证命令
Flutter OHOS 3.35.7-dev flutter --version
Dart SDK 3.5.0 dart --version
DevEco Studio 6.0.2 Release 打开IDE查看版本
OpenHarmony SDK API 20 DevEco Studio中查看
真机设备 ROM 6.0.0.130 SP8 设备设置→关于手机

提示 :如果环境尚未搭建,请先阅读第2篇:环境搭建与依赖配置完成准备工作。

二、第一步:添加依赖

2.1 配置pubspec.yaml

在项目的pubspec.yaml文件中添加apple_product_name的Git依赖:

yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  apple_product_name:
    git:
      url: https://gitcode.com/oh-flutter/flutter_apple_product_name.git
      ref: main

由于OpenHarmony适配版本尚未发布到pub.dev官方仓库,需要通过Git依赖 引入。url指向GitCode平台上的适配仓库,ref: main指定使用主分支最新代码。

2.2 库自身的依赖声明

以下是库的pubspec.yaml中的真实版本约束(来自项目根目录):

yaml 复制代码
name: apple_product_name
description: Library for translating Apple machine identifiers into Apple product names (e.g. 'iPhone17,1' to 'iPhone 16 Pro')
version: 3.7.0
homepage: https://github.com/kyle-seongwoo-jun/flutter_apple_product_name

environment:
  sdk: ">=3.5.0 <4.0.0"
  flutter: ">=3.22.0"

dependencies:
  flutter:
    sdk: flutter
  device_info_plus: ">=10.0.0 <12.0.0"

库当前版本3.7.0 ,依赖device_info_plus获取Apple设备信息。鸿蒙平台不使用device_info_plus,而是通过自己的MethodChanneldeviceInfo系统API实现。

2.3 鸿蒙平台插件声明

yaml 复制代码
flutter:
  plugin:
    platforms:
      ohos:
        pluginClass: AppleProductNamePlugin

pubspec.yaml中通过flutter.plugin.platforms.ohos声明了鸿蒙平台的插件入口类为AppleProductNamePlugin。Flutter框架在应用启动时会根据这个配置自动加载并注册原生插件。

三、第二步:安装依赖

3.1 执行安装命令

bash 复制代码
# 获取依赖包
flutter pub get

# 验证依赖是否安装成功
flutter pub deps | grep apple_product_name

执行flutter pub get后,Flutter会自动从Git仓库拉取代码并完成依赖解析。安装成功后,可以通过flutter pub deps查看依赖树,确认apple_product_name已正确安装。

3.2 安装结果验证

安装成功后,项目中会出现以下变化:

  • pubspec.lock文件中新增了apple_product_name的版本锁定记录
  • .dart_tool/package_config.json中新增了包路径映射
  • 可以在代码中正常导入库文件

注意 :如果安装过程中遇到网络超时,可以配置国内镜像源后重试,详见第2篇:环境搭建与依赖配置

四、第三步:调用API

4.1 导入库文件

dart 复制代码
import 'package:apple_product_name/apple_product_name_ohos.dart';

这是OpenHarmony平台专用的API入口文件,其中定义了OhosProductName 类。注意导入路径是apple_product_name_ohos.dart而非apple_product_name.dart,后者是Apple设备专用的。

4.2 获取设备产品名称

dart 复制代码
// 获取当前设备的友好产品名称
final productName = await OhosProductName().getProductName();
print(productName);  // 输出: "HUAWEI Mate 60 Pro"

一行代码即可获取设备的友好名称。getProductName()是异步方法,需要使用await等待结果。内部会通过MethodChannel 调用原生层,在HUAWEI_DEVICE_MAP映射表中查找型号对应的产品名称。

4.3 获取设备型号标识符

dart 复制代码
// 获取当前设备的型号标识符
final machineId = await OhosProductName().getMachineId();
print(machineId);  // 输出: "ALN-AL00"

getMachineId()返回设备的原始型号标识符,这个值来自系统API deviceInfo.productModel。型号标识符在日志记录、问题排查等场景中非常有用。

4.4 查询任意型号

dart 复制代码
// 查询任意型号的产品名称
final name = await OhosProductName().lookup('CFR-AN00');
print(name);  // 输出: "HUAWEI Mate 70"

// 查询未收录的型号
final unknown = await OhosProductName().lookup('XXX-XX00');
print(unknown);  // 输出: "XXX-XX00"(返回原始型号)

lookup()方法允许查询任意型号标识符对应的产品名称,不限于当前设备。如果映射表中未收录该型号,会返回原始的型号字符串作为兜底。

方法 返回类型 未找到时返回 使用场景
getProductName() Future<String> "Unknown" 展示当前设备名称
getMachineId() Future<String> "Unknown" 获取型号标识符
lookup(id) Future<String> 原始id 查询任意型号
lookupOrNull(id) Future<String?> null 区分已知/未知设备

五、示例应用完整代码

5.1 项目真实示例代码

以下是项目中example/lib/main.dart真实源码,展示了跨平台的使用方式:

dart 复制代码
import 'dart:io';

import 'package:apple_product_name/apple_product_name.dart';
import 'package:apple_product_name/apple_product_name_ohos.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  Future<String> _loadProductName() async {
    if (Platform.isIOS) {
      final info = await DeviceInfoPlugin().iosInfo;
      return info.utsname.productName;
    } else if (Platform.isMacOS) {
      final info = await DeviceInfoPlugin().macOsInfo;
      return info.productName;
    } else {
      // 鸿蒙平台
      try {
        return await OhosProductName().getProductName();
      } catch (e) {
        return 'Unknown';
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Apple Product Name'),
        ),
        body: FutureBuilder<String>(
          future: _loadProductName(),
          builder: (context, snapshot) {
            final productName = snapshot.data ?? 'Loading...';
            return Center(
              child: Text(
                productName,
                style: Theme.of(context)
                    .textTheme
                    .headlineSmall!
                    .copyWith(color: Colors.black),
              ),
            );
          },
        ),
      ),
    );
  }
}

这段代码的核心逻辑:

  1. 通过Platform.isIOSPlatform.isMacOS判断当前平台
  2. iOS/macOS使用device_info_plus获取设备信息
  3. 鸿蒙平台使用OhosProductName().getProductName()
  4. FutureBuilder处理异步加载状态,数据未返回时显示"Loading..."

跨平台设计:示例代码展示了如何在同一个应用中同时支持iOS、macOS和OpenHarmony三个平台,通过平台判断分别调用不同的API。

5.2 示例工程依赖配置

示例工程的example/pubspec.yaml中的真实依赖

yaml 复制代码
name: apple_product_name_example
description: Demonstrates how to use the apple_product_name plugin.
publish_to: "none"

environment:
  sdk: ">=3.5.0 <4.0.0"

dependencies:
  flutter:
    sdk: flutter
  apple_product_name:
    path: ../
  cupertino_icons: ^1.0.2
  device_info_plus: ^11.0.0

示例工程使用path: ../引用父目录的库源码,这是Flutter插件开发中的标准做法。device_info_plus: ^11.0.0用于iOS/macOS平台的设备信息获取。

六、原生层工作原理简析

6.1 插件入口

当Dart层调用API时,请求通过MethodChannel 传递到原生层。插件入口文件ohos/index.ets

typescript 复制代码
import AppleProductNamePlugin from './src/main/ets/components/plugin/AppleProductNamePlugin';
export default AppleProductNamePlugin;
export { AppleProductNamePlugin };

6.2 getProductName的原生实现

以下是AppleProductNamePlugin.etsgetProductName方法的真实源码

typescript 复制代码
private getProductName(result: MethodResult): void {
  try {
    const model = deviceInfo.productModel;
    // 先从映射表查找
    let productName = HUAWEI_DEVICE_MAP[model];
    // 如果映射表没有,使用系统的 marketName
    if (!productName) {
      productName = deviceInfo.marketName || model;
    }
    result.success(productName);
  } catch (e) {
    const errorMsg = e instanceof Error ? e.message : String(e);
    result.error("GET_PRODUCT_NAME_ERROR", errorMsg, null);
  }
}

这是一个两级查找策略

  1. 通过deviceInfo.productModel获取型号标识符(如"ALN-AL00")
  2. HUAWEI_DEVICE_MAP映射表中查找友好名称
  3. 映射表未收录时,回退到系统marketName字段
  4. 最后兜底返回原始型号

6.3 数据流转全链路

复制代码
用户调用 OhosProductName().getProductName()
  → Dart层: _channel.invokeMethod('getProductName')
  → MethodChannel序列化消息
  → ArkTS层: onMethodCall() 路由到 getProductName()
  → 读取 deviceInfo.productModel
  → 查找 HUAWEI_DEVICE_MAP[model]
  → result.success(productName)
  → Dart层 Future<String> 完成
  → UI展示设备名称

整个链路通常在毫秒级完成,用户几乎感知不到延迟。

七、自己动手写一个完整页面

7.1 设备信息展示页面

基于项目示例代码,我们来写一个更完整的设备信息展示页面:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:apple_product_name/apple_product_name_ohos.dart';

class DeviceInfoPage extends StatefulWidget {
  @override
  _DeviceInfoPageState createState() => _DeviceInfoPageState();
}

class _DeviceInfoPageState extends State<DeviceInfoPage> {
  String _productName = '加载中...';
  String _machineId = '加载中...';
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadDeviceInfo();
  }

  Future<void> _loadDeviceInfo() async {
    try {
      final ohos = OhosProductName();
      final productName = await ohos.getProductName();
      final machineId = await ohos.getMachineId();
      setState(() {
        _productName = productName;
        _machineId = machineId;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _productName = '获取失败';
        _machineId = '获取失败';
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('设备信息')),
      body: Center(
        child: _isLoading
            ? const CircularProgressIndicator()
            : Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('产品名称: $_productName',
                      style: Theme.of(context).textTheme.headlineSmall),
                  const SizedBox(height: 16),
                  Text('型号标识: $_machineId',
                      style: Theme.of(context).textTheme.bodyLarge),
                ],
              ),
      ),
    );
  }
}

这个页面使用StatefulWidget 管理状态,_isLoading标志控制加载指示器的显示。try-catch确保即使获取失败也不会崩溃。

上图展示了设备信息页面在华为手机上的实际运行效果。

八、使用FutureBuilder简化代码

8.1 FutureBuilder方式

对于简单场景,可以使用FutureBuilder进一步简化代码:

dart 复制代码
class SimplePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('设备名称')),
      body: FutureBuilder<String>(
        future: OhosProductName().getProductName(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            return Center(child: Text('错误: ${snapshot.error}'));
          }
          return Center(
            child: Text(
              snapshot.data ?? 'Unknown',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          );
        },
      ),
    );
  }
}

FutureBuilder根据异步操作的不同状态自动构建UI:

状态 说明 UI展示
ConnectionState.waiting 等待中 圆形进度指示器
snapshot.hasError 发生错误 错误信息文本
snapshot.hasData 数据就绪 设备名称文本

8.2 两种方式对比

对比项 StatefulWidget FutureBuilder
代码量 较多 较少
状态管理 手动管理 自动管理
灵活性 高(可控制刷新时机) 中(绑定Future生命周期)
适用场景 复杂交互页面 简单数据展示
错误处理 try-catch snapshot.hasError

建议:简单的设备信息展示用FutureBuilder,需要手动刷新或复杂交互的场景用StatefulWidget。

九、错误处理最佳实践

9.1 防御性编程

在生产环境中,建议对所有API调用进行完善的错误处理:

dart 复制代码
import 'package:flutter/services.dart';
import 'package:apple_product_name/apple_product_name_ohos.dart';

Future<String> safeGetProductName() async {
  try {
    return await OhosProductName().getProductName();
  } on PlatformException catch (e) {
    // 原生侧执行出错(如插件未注册)
    print('平台异常: ${e.code} - ${e.message}');
    return 'Unknown Device';
  } on MissingPluginException {
    // 插件未注册
    print('插件未注册,请检查GeneratedPluginRegistrant');
    return 'Unknown Device';
  } catch (e) {
    // 其他未知异常
    print('未知异常: $e');
    return 'Unknown Device';
  }
}

推荐的异常处理层级:

  1. PlatformException:原生侧执行出错,包含错误码和错误信息
  2. MissingPluginException:插件未注册,需检查注册链路
  3. 通用catch:兜底处理所有其他异常

9.2 常见错误及解决方案

错误类型 错误信息 原因 解决方案
MissingPluginException No implementation found 插件未注册 检查GeneratedPluginRegistrant.ets
PlatformException GET_PRODUCT_NAME_ERROR 原生层执行异常 查看hdc hilog日志
TimeoutException Future timed out MethodChannel超时 检查设备连接状态

十、构建与运行

10.1 构建HAP包

bash 复制代码
# 构建debug版本
flutter build hap --debug

# 构建release版本
flutter build hap --release

# 查看详细构建日志
flutter build hap --debug --verbose

flutter build hap会执行完整的编译和打包流程,生成可安装的HAP文件(Harmony Ability Package)。

10.2 安装到设备

bash 复制代码
# 查看已连接的设备
hdc list targets

# 安装HAP包
hdc install ./build/outputs/default/entry-default-signed.hap

# 查看插件相关日志
hdc hilog | grep "AppleProductNamePlugin"

hdc是OpenHarmony的设备连接工具,类似Android的adb。安装成功后,在设备上打开应用即可看到设备名称。

10.3 运行效果验证

运行成功后,应用界面应显示类似以下内容:

复制代码
┌─────────────────────────┐
│     Apple Product Name   │
├─────────────────────────┤
│                         │
│   HUAWEI Mate 60 Pro    │
│                         │
└─────────────────────────┘

如果显示"Unknown"或"Loading..."停留不动,请按以下步骤排查:

  • 检查GeneratedPluginRegistrant.ets是否注册了插件
  • 检查EntryAbility.ets是否调用了registerWith
  • 查看hdc hilog日志中是否有错误信息

十一、支持的设备映射表预览

11.1 部分设备映射

HUAWEI_DEVICE_MAP映射表中收录了**90+**华为/荣耀设备,以下是部分示例:

typescript 复制代码
const HUAWEI_DEVICE_MAP: Record<string, string> = {
  // Mate 70 系列
  "CFR-AN00": "HUAWEI Mate 70",
  "CFS-AN00": "HUAWEI Mate 70 Pro",
  "CFT-AN00": "HUAWEI Mate 70 Pro+",
  "CFU-AN00": "HUAWEI Mate 70 RS 非凡大师",
  // Mate 60 系列
  "BRA-AL00": "HUAWEI Mate 60",
  "ALN-AL00": "HUAWEI Mate 60 Pro",
  "GGK-AL10": "HUAWEI Mate 60 Pro+",
  // Pura 70 系列
  "HBN-AL00": "HUAWEI Pura 70",
  "DUA-AL00": "HUAWEI Pura 70 Pro",
  "HBK-AL00": "HUAWEI Pura 70 Ultra",
  // nova 系列
  "FOA-AL00": "HUAWEI nova 13",
  "FNA-AL00": "HUAWEI nova 13 Pro",
  // 荣耀系列
  "PGT-AN00": "Honor Magic6 Pro",
  "BVL-AN00": "Honor Magic6",
  // ... 共90+个型号映射
};

11.2 设备系列覆盖

设备系列 代表型号 映射数量
Mate系列 Mate 70/60/X5/X3 22个
Pura系列 Pura 70/P60 12个
nova系列 nova 13/12/11 14个
Pocket系列 Pocket 2/Pocket S 4个
MatePad系列 MatePad Pro/Air 10个
荣耀Magic Magic6/Magic5 8个
荣耀数字 Honor 200/100 8个
华为手表 WATCH GT/Ultimate 12个

映射表会随新设备发布持续更新,社区开发者也可以通过提交Pull Request贡献新设备数据。

十二、进阶用法预告

12.1 更多API能力

除了本文介绍的基础用法,该库还提供了更多进阶能力:

dart 复制代码
// lookupOrNull:区分已知设备和未知设备
final name = await OhosProductName().lookupOrNull('ALN-AL00');
if (name != null) {
  print('已知设备: $name');
} else {
  print('未知设备,映射表未收录');
}

lookupOrNulllookup的区别在于:未找到映射时返回null而非原始machineId。这在需要区分"已知设备"和"未知设备"的场景中非常有用。

12.2 系列文章导航

本系列共30篇文章,推荐阅读路线:

阶段 篇章 主题
入门篇 第1-3篇 库简介、环境搭建、快速上手(本文)
API篇 第4-10篇 OhosProductName类、各方法详解、异步处理
映射表篇 第11-15篇 Mate/Pura/nova/MatePad/荣耀设备映射
源码篇 第16-22篇 插件架构、源码分析、MethodChannel机制
实战篇 第23-29篇 页面构建、反馈系统、统计分析、性能优化
生态篇 第30篇 源码贡献与生态共建

推荐阅读顺序:

  1. 先完成入门篇(第1-3篇)建立整体认知
  2. 再阅读API篇(第4-10篇)掌握所有方法用法
  3. 根据需要选读映射表篇、源码篇或实战篇

总结

通过本文的三步走 流程(添加依赖→安装依赖→调用API),你已经成功在OpenHarmony应用中集成了apple_product_name 库。整个过程非常简单:一行依赖配置、一条安装命令、一行API调用,即可获取设备的友好产品名称。该库提供的getProductName()getMachineId()lookup()三个核心方法覆盖了绝大多数使用场景,两级查找策略确保在任何设备上都能返回有意义的结果。

下一篇文章将深入介绍OhosProductName类的完整API和设计细节。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

相关推荐
钛态2 小时前
Flutter for OpenHarmony 实战:Supabase — 跨平台后端服务首选
flutter·ui·华为·架构·harmonyos
sdff113962 小时前
【HarmonyOS】flutter of HarmonyOS 实战项目+智慧家居控制面板全解
flutter·华为·harmonyos
哈__2 小时前
基础入门 Flutter for OpenHarmony:palette_generator 调色板生成详解
flutter
心中有国也有家15 小时前
Flutter for OpenHarmony:Flutter 全屏滑动引擎PageView 组件详解
flutter·华为·华为云·软件工程·harmonyos
ELI_He99918 小时前
升级旧flutter项目
flutter
早點睡39020 小时前
基础入门 Flutter for OpenHarmony:SnackBar 消息提示组件详解
flutter·harmonyos
空白诗20 小时前
基础入门 Flutter for OpenHarmony:BottomSheet 底部面板详解
flutter·harmonyos
RaidenLiu20 小时前
拒绝重写!Flutter Add-to-App 全攻略:让原生应用“渐进式”拥抱跨平台
前端·flutter·前端框架
不爱吃糖的程序媛1 天前
Flutter 定位插件 flutter_z_location 适配 OpenHarmony 全流程实战
flutter