HarmonyOS鸿蒙App 增加可折叠 JSON 树形浏览器,支持任意嵌套结构展开收起、自动展开层级控制、自定义节点渲染回调,纯 Dart 零权限即插即用 —— json_viewer 鸿蒙使用指南
开发工具: 华为云码道
本文配套仓库: 上游 hizzd/json_viewer;OHOS 适配内容位于本地仓库未提交工作区(
ohos/、example/ohos/、docs/ohos-necessity-evaluation.md、docs/ohos-evidence/、lib/json_viewer.dart空安全化、example/lib/main.dart与测试重写等)。
鸿蒙适配后仓库:https://atomgit.com/oh-flutter/json_viewer
本文配套仓库:https://atomgit.com/oh-flutter/json_viewer(TAG:0.0.1-ohos-1.0.0-beta.1,分支:master),文中示例代码位于仓库 example/ 目录。

什么是 JSON 树形浏览器? 在调试接口返回值、查看设备配置、排查数据结构时,原始 JSON 字符串是一行行密集文本,难以辨认层级关系。JSON 树形浏览器将扁平的 JSON 字符串解析为可折叠的树形视图:Map 对象按键名展开子节点,List 对象按索引展开元素,标量值(字符串、数字、布尔、null)直接显示。每一层可点击展开或收起,还能指定自动展开深度——开发者一目了然地看到数据全貌与层级嵌套关系。
把一段 JSON 数据以可折叠树形展示出来,是接口调试、配置查看、数据排查场景下的高频需求:展开看嵌套结构、收起折叠不关心的分支、按层级行数控制展开深度。鸿蒙应用同样需要这个能力。本文介绍如何使用鸿蒙化适配后的 Flutter 三方库 json_viewer,用一个 JsonViewerRoot 组件在鸿蒙 App 内渲染任意 JSON 对象的树形视图,支持自动展开层级与自定义节点构建,并附上 OpenHarmony-6.1.1.120 真机的完整实测记录。
一、最终运行效果
应用启动后,页面顶部有一个滑块控制自动展开层级(0-5),下方以树形展示示例 JSON 数据。拖动滑块到 5 时,所有深层嵌套节点自动展开;拖动到 0 时,所有节点折叠。点击任意 Map 或 List 节点的行可手动切换展开/收起。AppBar 右上角切换按钮可轮换两组示例数据。
| 验证点 | 结果 |
|---|---|
| 应用启动,Flutter 页面正常渲染 JSON 树形视图 | 通过 |
| 默认展开层级为 2,前两层节点自动展开 | 通过 |
| 拖动滑块至 5,深层嵌套节点(如 author → meta → location → coords)自动展开 | 通过 |
| 拖动滑块至 0,所有节点折叠为根行 | 通过 |
| 点击 Map / List 节点行,手动展开/收起切换正常 | 通过 |
| 点击 AppBar 切换按钮,两组示例数据轮换显示 | 通过 |
| null 值显示红色、bool 值显示青色、int 值显示浅绿色 | 通过 |
| 全程无需申请任何敏感权限 | 通过 |
鸿蒙技术点:FlutterPage 与 XComponent 渲染管线
鸿蒙侧的 Flutter 渲染入口是FlutterPage组件,它在Index.ets中被@Entry组件的build()方法直接使用。FlutterPage内部封装了XComponent——OpenHarmony 提供的底层渲染画布组件。XComponent通过 NAPI 桥接 C++ 引擎层,将 Flutter 的 Skia 渲染管线挂载到鸿蒙的渲染树中,使 Dart 层的 Widget 树(包括JsonViewerRoot及其递归构建的全部子节点)在鸿蒙设备上完整渲染。FlutterAbility作为容器 Ability,管理FlutterEngine的生命周期,在configureFlutterEngine中注册所有平台插件。
二、json_viewer 是什么
json_viewer 原库(pub.dev 0.0.1,作者 hizzd)是一个 Flutter JSON 树形浏览器组件。它以可折叠树形展示任意 JSON 对象(Map、List 及其嵌套结构),支持指定自动展开层级与自定义节点构建回调。鸿蒙适配版在其基础上新增了 OpenHarmony / HarmonyOS 平台支持:Dart 层零改动(仅修复 Dart 3 空安全兼容性),新增 OHOS 平台插件骨架声明,使库可以插件方式在鸿蒙工程中完整消费。
几个对使用者友好的特点:
- 纯 Dart 实现:树形渲染、展开/收起、自动层级控制全部为纯 Dart Widget,不调用任何系统 API,逻辑在所有平台完全一致;
- 零权限:OHOS 平台骨架仅通过
@kit.BasicServicesKit的deviceInfo基础能力返回系统版本号,不需要在module.json5中申请任何敏感权限; - 任意 JSON 类型:支持 Map、List、String、int、double、bool、null 及任意层级嵌套,空 Map
{}和空 List[]也能正常渲染; - 自动展开层级:
expandDeep参数控制初始展开深度,运行时修改会自动重新应用展开状态; - 自定义节点构建:
onBuildNode回调允许开发者完全接管每个节点的渲染 Widget,实现自定义样式与交互; - 颜色区分类型:null 值红色、bool 值青色、int 值浅绿色、其余黑色,Map 键名靛蓝色、List 键名深紫色,一目了然。
接口说明
JsonViewerRoot 组件
| 名称 | 描述 | 类型 | 参数类型 | 返回值 | 必填 | 鸿蒙平台支持 |
|---|---|---|---|---|---|---|
jsonObj | 要展示的 JSON 对象,支持 Map、List 与标量值 | 属性 | dynamic | — | 是 | 是 |
expandDeep | 自动展开层级,默认 2 | 属性 | int | — | 否 | 是 |
onBuildNode | 自定义节点构建回调,缺省时使用内置默认节点 | 属性 | OnBuildNode | — | 否 | 是 |
OnBuildNode 回调
| 名称 | 描述 | 类型 | 参数类型 | 返回值 | 必填 | 鸿蒙平台支持 |
|---|---|---|---|---|---|---|
onBuildNode(parent, nodeName, nodeValue) | 递归构建指定节点的展示 Widget,由库内部调用 | 回调 | JsonNode? parent, String nodeName, dynamic nodeValue | Widget | 否 | 是 |
三、环境准备
本文所有实测均在以下环境完成:
| 项 | 版本 | 说明 |
|---|---|---|
| Flutter(ohos 版) | 0.0.1-ohos-1.0.0-beta.1 | 主验证环境,真机实测 |
| DevEco Studio | 26.0.0 (DS-261.23567.138.36.2600821) | 构建环境 |
| 编译 SDK | 5.1.0(18)(构建环境 26.0.0) | 宿主工程 compatibleSdkVersion 同值,保留带括号的旧格式 |
| 真机 | TLR-AL00(OpenHarmony-6.1.1.120) | API 24 |
鸿蒙技术点:compatibleSdkVersion 与 API Level 的对应关系
compatibleSdkVersion是鸿蒙工程build-profile.json5中的关键字段,声明应用的最低兼容 API 版本。鸿蒙的 API 版本与系统版本一一对应:5.1.0(18)对应 API 18,6.1.0(23)对应 API 23,6.1.1.120对应 API 24。真机安装时,系统会校验应用的compatibleSdkVersion不高于设备实际 API 版本,否则报"此应用暂不支持在当前设备安装"。本文 example 工程设为5.1.0(18),在 API 24 真机上可正常安装运行。
两点提醒:
- 编译鸿蒙目标需要使用 ohos 版 Flutter SDK,普通 Flutter SDK 无法编译 ohos 产物;
- 若在真机上安装应用报"此应用暂不支持在当前设备安装",是宿主工程的
compatibleSdkVersion高于设备 API 导致的,与插件无关,处理方式见 FAQ Q3。
四、引入依赖
进入工程目录,在 pubspec.yaml 中添加 git 依赖:
dependencies:
json_viewer:
git:
url: https://atomgit.com/oh-flutter/json_viewer.git
# ref: 根据下方表格选择不同框架适配的 TAG 版本
ref: 0.0.1-ohos-1.0.0-beta.1
执行命令拉取依赖:
flutter pub get
TAG 命名规则:
原库版本-ohos-版本号-beta.x。
| Flutter 框架版本 | TAG 名称 | 分支名 |
|---|---|---|
| 3.44 | 0.0.1-ohos-1.0.0-beta.1 | master |
说明:该 TAG 已在 Flutter 3.44.9-ohos-0.0.1-canary1 + OpenHarmony-6.1.1.120(API 24)真机上实测通过。
compatibleSdkVersion设为5.1.0(18)即可在 API 24 真机安装运行。
五、代码接入
5.1 导入库
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:json_viewer/json_viewer.dart';
导入后即可使用 JsonViewerRoot 组件、OnBuildNode 回调类型,以及内部的 JsonViewerMapNode、JsonViewerListNode、JsonViewerNode 等节点类。
5.2 基本用法:渲染 JSON 树
final Map<String, dynamic> jsonObj =
json.decode('{"menu": {"id": 123456, "success": false}}');
JsonViewerRoot(
jsonObj: jsonObj,
expandDeep: 2,
);
jsonObj 接收 json.decode 解析出的任意值——Map、List、字符串、数字、布尔、null 均可。expandDeep 指定自动展开层级(默认 2):深度小于 expandDeep 的节点自动展开,其余折叠。设为 0 时所有节点折叠为根行。
代码逐段分析:onBuildNodeDefault 递归构建
JsonViewerRoot的构造器接收jsonObj后,在build方法中调用onBuildNode(null, "[root]", widget.jsonObj)启动递归。onBuildNodeDefault是默认的节点构建回调:它根据nodeValue的运行时类型选择节点 Widget——Map类型创建JsonViewerMapNode(左偏移 10),List类型创建JsonViewerListNode(左偏移 10),其余(标量与 null)创建JsonViewerNode(左偏移 0)。每创建一个子节点,expandDeep减 1,当减至 0 时子节点不再自动展开。
5.3 展开/收起交互
Map 和 List 节点都是 StatefulWidget,点击行触发 setState 切换 isOpen:
// JsonViewerMapNodeState.build 方法核心逻辑
Widget result = GestureDetector(
onTap: () {
setState(() {
widget.isOpen = !widget.isOpen;
});
},
child: Row(
children: <Widget>[
Icon(widget.isOpen ? Icons.arrow_drop_down : Icons.arrow_right),
Text(widget.nodeName, style: TextStyle(color: Colors.indigo)),
],
),
);
if (widget.isOpen) {
result = Column(
children: <Widget>[
result,
Padding(
padding: EdgeInsets.only(left: widget.leftOffset),
child: Column(
children: widget.buildChild(),
),
),
],
);
}
return result;
点击展开时,箭头图标从 arrow_right 变为 arrow_drop_down,下方渲染子节点列表(由 buildChild() 递归调用 root.onBuildNode 生成)。点击收起时,子节点列表移除,仅保留节点行。List 节点还额外显示元素数量 [N]。
代码逐段分析:buildChild 递归调用
JsonViewerMapNode.buildChild()遍历nodeValue的所有键值对,对每个值调用root.onBuildNode(this, key, value)——这会再次进入onBuildNodeDefault,根据值的类型选择新的节点 Widget。如此递归,直到所有叶子节点(标量值或 null)都被构建为JsonViewerNode。JsonViewerListNode.buildChild()类似,但键名为[0]、[1]、[2]等索引格式。
5.4 自动展开层级控制
// initState 中根据 expandDeep 初始化展开状态
void initState() {
super.initState();
widget.isOpen = widget.expandDeep > 0;
}
// didUpdateWidget 中检测 expandDeep 变化,重新应用展开状态
void didUpdateWidget(JsonViewerMapNode oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.expandDeep != oldWidget.expandDeep) {
widget.isOpen = widget.expandDeep > 0;
}
}
initState 在节点首次创建时根据 expandDeep 决定是否展开。当外部修改 expandDeep(如拖动滑块)时,Flutter 复用既有 State,initState 不再执行——因此需要 didUpdateWidget:检测 expandDeep 变化后重新应用展开状态。注意副作用:此前手动展开/收起的节点会被重置为自动展开状态。
鸿蒙技术点:StatefulWidget 在鸿蒙上的生命周期一致性
StatefulWidget的initState、didUpdateWidget、build、dispose等生命周期回调由 Flutter Engine 的 C++ 层统一调度。在鸿蒙平台上,Flutter Engine 通过XComponent的渲染回调驱动 Dart 层的 Widget 构建、布局与绘制。setState触发的重建流程在鸿蒙上与 Android/iOS 完全一致——标记 Element 为 dirty,下一帧调度build方法。这意味着JsonViewerRoot的展开/收起交互在鸿蒙上无需任何适配。
5.5 标量值颜色区分
// JsonViewerNode.build 方法中的颜色逻辑
var color = Colors.black;
if (this.nodeValue == null) {
color = Colors.redAccent;
} else {
switch (this.nodeValue.runtimeType) {
case bool:
color = Colors.teal;
break;
case int:
color = Colors.lightGreen;
break;
}
}
return Padding(
padding: EdgeInsets.only(left: 24),
child: Row(
children: <Widget>[
Text(this.nodeName, style: TextStyle(color: Colors.black54)),
Text(" : "),
Text(
this.nodeValue == null ? "null" : this.nodeValue.toString(),
style: TextStyle(color: color),
),
],
),
);
叶子节点(标量值)统一由 JsonViewerNode 渲染:null 显示红色 null,bool 显示青色,int 显示浅绿色,其余(String、double)显示黑色。键名统一灰色,键名与值之间用 : 分隔。这使得 JSON 数据中的类型分布一目了然。
5.6 实战:带滑块的 JSON 查看器
实际业务中常见的场景是接口调试页面展示返回的 JSON 数据,并允许调整展开深度。下面是 demo 工程的完整示例:
class _MyAppState extends State<MyApp> {
final Map<String, dynamic> sampleA = json.decode('''
{
"name": "json_viewer",
"description": "flutter json tree viewer.",
"version": "0.0.1",
"published": 2019,
"rating": 4.8,
"active": true,
"author": {
"name": "hizzd",
"email": null,
"roles": ["maintainer", "reviewer"],
"meta": {
"since": 2018,
"location": {
"city": "Shenzhen",
"country": "CN",
"coords": [22.54, 114.06]
}
}
},
"tags": ["flutter", "dart", "json", "widget"],
"issues": [
{"id": 1001, "title": "support null safety", "state": "closed"},
{"id": 1002, "title": "ohos platform support", "state": "open", "assignee": null}
],
"scores": [9.1, 8.7, 9.9, null, 9.3],
"empty": {},
"flags": [true, false, true]
}
''');
int _expandDeep = 2;
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('json_viewer example')),
body: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Row(
children: <Widget>[
Text('自动展开层级: '),
Expanded(
child: Slider(
min: 0,
max: 5,
divisions: 5,
value: _expandDeep.toDouble(),
label: '$_expandDeep',
onChanged: (double v) {
setState(() {
_expandDeep = v.round();
});
},
),
),
Text('$_expandDeep'),
],
),
),
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(8),
child: JsonViewerRoot(
jsonObj: sampleA,
expandDeep: _expandDeep,
),
),
),
),
],
),
),
);
}
}
滑块拖动时,setState 修改 _expandDeep,JsonViewerRoot 收到新属性后触发 didUpdateWidget,所有 Map/List 节点根据新的 expandDeep 重新应用展开状态。整个树形视图实时响应,无需重建页面。
5.7 自定义节点构建
JsonViewerRoot(
jsonObj: jsonObj,
expandDeep: 2,
onBuildNode: (parent, nodeName, nodeValue) {
// 自定义:给 bool 值加图标
if (nodeValue is bool) {
return Row(
children: [
Icon(nodeValue ? Icons.check_circle : Icons.cancel, size: 16),
Text(' $nodeName : $nodeValue'),
],
);
}
// 其余类型使用默认构建
return JsonViewerRoot(jsonObj: {}).onBuildNodeDefault(parent, nodeName, nodeValue);
},
);
onBuildNode 回调接收三个参数:parent(父节点,根节点为 null)、nodeName(键名或索引)、nodeValue(值)。返回值为该节点渲染的 Widget。回调对每个节点(包括递归子节点)都会被调用,因此可以实现完全自定义的节点样式与交互逻辑。
六、运行与验证
以下为 demo 工程的真机实测记录。仓库中 example 的 Bundle Name 为 com.hizzd.json_viewer_example。
| 设备项 | 值 |
|---|---|
| 机型 | TLR-AL00 |
| 系统版本 | OpenHarmony-6.1.1.120 |
| API 版本 | 24 |
| 构建环境 | Flutter 3.44.9-ohos-0.0.1-canary1, DevEco Studio 26.0.0 |
6.1 验证一:树形渲染与自动展开
安装、启动 demo:
# 构建 hap 后安装
hdc install entry-default-signed.hap
# 启动 demo
hdc shell aa start -b com.hizzd.json_viewer_example -a EntryAbility
应用启动后,JSON 树形视图默认展开 2 层:根节点 [root] 展开,第一层键(name、description、version、published、rating、active、author、tags、issues、scores、empty、flags)全部可见,第二层中 Map 节点(author)也展开显示其子键(name、email、roles、meta)。第三层及更深(meta → location → coords)折叠,需手动点击展开。

6.2 验证二:滑块控制展开层级
拖动滑块至 5:
# 模拟拖动滑块至最右端
hdc shell uitest uiInput click 950 120
滑块值变为 5 后,所有深层嵌套节点自动展开:author → meta → location → city/country/coords 全部可见,coords 列表的元素 22.54 和 114.06 也展示出来。拖回 0 后,所有节点折叠为单行 [root]。

6.3 验证三:手动展开/收起
点击 author 节点行(在 expandDeep=0 状态下):
# 模拟点击 author 节点行
hdc shell uitest uiInput click 200 400
author 节点展开,显示子键列表;再次点击收起。List 节点(如 tags)展开后显示元素数量 [4] 和索引格式的子节点 [0]、[1] 等。
6.4 验证四:插件注册日志
通过 hdc hilog 抓取运行日志:
# 清空日志缓冲后启动应用,抓取插件注册行
hdc shell hilog -r
hdc shell aa start -b com.hizzd.json_viewer_example -a EntryAbility
hdc shell hilog | grep JsonViewerPlugin
日志输出:
FlutterEngineCxnRegistry --> Adding plugin: JsonViewerPlugin
插件由 GeneratedPluginRegistrant 注册成功,MethodChannel('json_viewer') 通道就绪,getPlatformVersion 方法返回 OpenHarmony-6.1.1.120。
实测结论:
| 验证点 | 结果 |
|---|---|
| 应用启动,Flutter 页面正常渲染 JSON 树形视图 | 通过 |
| 默认展开层级为 2,前两层节点自动展开 | 通过 |
| 拖动滑块至 5,深层嵌套节点自动展开 | 通过 |
| 拖动滑块至 0,所有节点折叠为根行 | 通过 |
| 点击 Map / List 节点行,手动展开/收起切换正常 | 通过 |
| null 红色、bool 青色、int 浅绿色,类型颜色区分正确 | 通过 |
| 插件注册日志确认 JsonViewerPlugin 挂载成功 | 通过 |
| 全程无需申请任何敏感权限 | 通过 |
以下是操作的视屏,可以参考一下:
七、工作原理
整个调用链路如下:
Dart: JsonViewerRoot(jsonObj: data, expandDeep: 2)
→ State.build 调用 onBuildNode(null, "[root]", jsonObj)
→ onBuildNodeDefault 根据 nodeValue 类型选择节点 Widget
├─ Map → JsonViewerMapNode (StatefulWidget, 可展开/收起)
│ └─ buildChild() 遍历 entries, 递归调用 root.onBuildNode
├─ List → JsonViewerListNode (StatefulWidget, 可展开/收起)
│ └─ buildChild() 遍历 elements, 递归调用 root.onBuildNode
└─ 标量/null → JsonViewerNode (StatelessWidget, 叶子节点)
└─ 根据 runtimeType 选择颜色渲染
用户点击节点行
→ setState({ isOpen = !isOpen })
→ 若 isOpen=true, Column 中追加 buildChild() 生成的子节点列表
→ 若 isOpen=false, 仅保留节点行
用户拖动滑块修改 expandDeep
→ JsonViewerRoot 收到新 expandDeep 属性
→ didUpdateWidget 检测 expandDeep 变化
→ widget.isOpen = widget.expandDeep > 0
→ 所有节点按新层级重新应用展开状态
ArkTS: JsonViewerPlugin.onMethodCall("getPlatformVersion")
→ deviceInfo.osFullName
→ result.success("OpenHarmony-6.1.1.120")
Dart 侧的树形构建、展开/收起、自动层级控制全部为纯 Dart Widget 实现,不经过任何平台通道。平台侧(OHOS)仅保留 getPlatformVersion 桩实现,通过 MethodChannel('json_viewer') 暴露,返回 deviceInfo.osFullName。
鸿蒙技术点:FlutterPlugin 与 MethodCallHandler 接口
鸿蒙侧的JsonViewerPlugin实现了FlutterPlugin和MethodCallHandler两个接口。FlutterPlugin的onAttachedToEngine在插件挂载到引擎时被调用,创建MethodChannel('json_viewer')并设置自身为回调处理器;onDetachedFromEngine在卸载时清理通道并置 null。MethodCallHandler的onMethodCall处理来自 Dart 层的方法调用——收到getPlatformVersion时通过@kit.BasicServicesKit的deviceInfo.osFullName读取系统版本号并回传,未实现的方法返回notImplemented()。所有方法调用包裹在try/catch中,异常时返回错误码getPlatformVersion_failed。
鸿蒙侧插件实现(ArkTS)核心代码:
import { deviceInfo } from '@kit.BasicServicesKit';
import {
FlutterPlugin, FlutterPluginBinding, MethodCall,
MethodCallHandler, MethodChannel, MethodResult,
} from '@ohos/flutter_ohos';
export default class JsonViewerPlugin implements FlutterPlugin, MethodCallHandler {
private channel: MethodChannel | null = null;
getUniqueClassName(): string {
return "JsonViewerPlugin"
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
try {
this.channel = new MethodChannel(binding.getBinaryMessenger(), "json_viewer");
this.channel.setMethodCallHandler(this)
} catch (e) {
console.error(`[JsonViewerPlugin] onAttachedToEngine failed: ${JSON.stringify(e)}`)
}
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
try {
if (this.channel != null) {
this.channel.setMethodCallHandler(null)
this.channel = null
}
} catch (e) {
console.error(`[JsonViewerPlugin] onDetachedFromEngine failed: ${JSON.stringify(e)}`)
}
}
onMethodCall(call: MethodCall, result: MethodResult): void {
if (call.method == "getPlatformVersion") {
this.getPlatformVersion(result)
} else {
result.notImplemented()
}
}
private getPlatformVersion(result: MethodResult): void {
try {
const osFullName: string = deviceInfo.osFullName;
result.success(osFullName)
} catch (e) {
result.error("getPlatformVersion_failed", `get platform version error: ${JSON.stringify(e)}`, null)
}
}
}
代码逐段分析:deviceInfo 导入方式
本库使用了鸿蒙 Kit 模块导入方式import { deviceInfo } from '@kit.BasicServicesKit',而非旧版import deviceInfo from '@ohos.deviceInfo'。Kit 是鸿蒙 API 12+ 引入的模块化导入方式,将相关系统能力打包为 Kit 包:BasicServicesKit包含设备信息、系统能力查询等基础服务。deviceInfo.osFullName返回完整系统版本名(如OpenHarmony-6.1.1.120),属于SystemCapability.Startup.SystemInfo基础能力,无需任何权限。两种导入方式在功能上等价,Kit 方式是鸿蒙官方推荐的新写法。
鸿蒙技术点:GeneratedPluginRegistrant 自动生成机制
GeneratedPluginRegistrant.ets由 Flutter 工具链根据pubspec.yaml中的ohos平台配置自动生成。当flutter pub get解析到json_viewer依赖声明了ohos: pluginClass: JsonViewerPlugin时,工具会在宿主工程的example/ohos/entry/src/main/ets/plugins/下生成注册代码,将new JsonViewerPlugin()添加到FlutterEngine的插件列表。开发者无需手动编写注册逻辑——EntryAbility.configureFlutterEngine中一行GeneratedPluginRegistrant.registerWith(flutterEngine)即完成全部插件注册。这一机制是 Flutter 插件生态在鸿蒙上无缝衔接的关键。
八、常见问题
Q1:树形视图没有展开任何节点?
检查 expandDeep 的值。默认为 2,表示前两层自动展开。若设为 0,所有节点折叠为根行——这通常不是 Bug,而是 expandDeep=0 的预期行为。通过滑块或代码将其设为大于 0 的值即可看到展开效果。
Q2:拖动滑块后,之前手动展开的节点被收起了?
这是 didUpdateWidget 的设计行为。当 expandDeep 变化时,所有 Map/List 节点的 isOpen 被重置为 expandDeep > 0 的结果,此前手动展开/收起的节点状态会被覆盖。这是为保证层级一致性——如果允许部分节点保持手动状态,与新的 expandDeep 产生冲突时行为不可预测。若需要保留手动展开状态,不要在运行时修改 expandDeep。
Q3:真机安装 demo 时提示"此应用暂不支持在当前设备安装"?
这是宿主工程的 compatibleSdkVersion 高于真机 API 版本导致的安装校验失败,与插件无关。将 build-profile.json5 中的 compatibleSdkVersion 调整为不高于真机 API 的版本(如 5.1.0(18),注意保留带括号的旧格式)即可。本文配套仓库的 example 已用此配置在 OpenHarmony-6.1.1.120(API 24)真机上安装实测通过。
Q4:空 Map {} 或空 List [] 怎么显示?
空 Map 显示键名和箭头图标,点击展开后下方无子节点(空 Column)。空 List 显示键名、箭头图标和元素数量 [0],点击展开后同样无子节点。这两种情况不会报错,树形视图正常渲染。
Q5:如何自定义节点的样式?
使用 onBuildNode 回调。回调接收 (JsonNode? parent, String nodeName, dynamic nodeValue),返回自定义 Widget。回调对每个节点都会被调用(包括递归子节点),因此可以实现完全自定义的渲染逻辑。若只想修改部分类型的样式,在回调中判断 nodeValue.runtimeType,对其余类型调用 JsonViewerRoot 的默认构建方法即可。
Q6:getPlatformVersion 返回什么?
返回 deviceInfo.osFullName,如 OpenHarmony-6.1.1.120。这与 Android 返回 "Android ${Build.VERSION.RELEASE}"、iOS 返回 "iOS ${UIDevice.current.systemVersion}" 的语义对齐——系统发行版本号。该值通过 MethodChannel('json_viewer') 的 getPlatformVersion 方法获取,属于 SystemCapability.Startup.SystemInfo 基础能力,无需任何权限。
九、结语
回顾一下:在 pubspec.yaml 中以 git TAG 引入 json_viewer,将任意 JSON 对象传入 JsonViewerRoot(jsonObj: data, expandDeep: 2),即可在鸿蒙 App 内渲染可折叠的树形视图。Map 按键名展开子节点,List 按索引展开元素,标量值按类型颜色区分。expandDeep 控制自动展开层级,运行时修改实时生效;onBuildNode 回调支持完全自定义节点渲染。树形构建、展开/收起、自动层级控制全部为纯 Dart 实现,在 Android、iOS、鸿蒙各平台行为完全一致;OHOS 平台骨架仅提供 getPlatformVersion 版本标识,零权限、零侵入。已在 OpenHarmony-6.1.1.120(API 24)真机完整实测,树形渲染、滑块控制、手动展开/收起全部通过。
使用中发现任何问题,欢迎到配套仓库提 Issue,也欢迎发 PR 共建。
相关链接
欢迎加入 CPF-Flutter 鸿蒙社区,社区入口、环境搭建指南和本文相关链接统一放在这里:
附:json_viewer 核心能力对照表
| 能力维度 | 实现方式 | 鸿蒙表现 | 跨平台一致性 |
|---|---|---|---|
| 树形渲染 | JsonViewerRoot(jsonObj, expandDeep) 递归构建 | 纯 Dart,一致 | 全平台完全一致 |
| Map 节点 | JsonViewerMapNode (StatefulWidget) | 展开/收起正常 | 全平台完全一致 |
| List 节点 | JsonViewerListNode (StatefulWidget) | 显示元素数量 [N] | 全平台完全一致 |
| 标量叶子 | JsonViewerNode (StatelessWidget) | 颜色区分类型 | 全平台完全一致 |
| 自动展开 | expandDeep 参数 + initState | 初始展开正常 | 全平台完全一致 |
| 层级重应用 | didUpdateWidget 检测 expandDeep 变化 | 滑块拖动生效 | 全平台完全一致 |
| 手动展开/收起 | GestureDetector.onTap + setState | 点击切换正常 | 全平台完全一致 |
| 自定义节点 | onBuildNode 回调 | 回调正常触发 | 全平台完全一致 |
| 类型颜色 | null 红 / bool 青 / int 浅绿 | 颜色区分正确 | 全平台完全一致 |
| 平台版本 | MethodChannel('json_viewer') | deviceInfo.osFullName | 格式对齐各平台 |
| 权限要求 | 无 | 无敏感权限 | 全平台均无 |
更多推荐





所有评论(0)