Flutter for OpenHarmony 实战:HarmonyOS ArkTS API 24实现一个柱状图
2026/8/31 12:51:18 网站建设 项目流程

在移动开发领域,我们总是面临着选择与适配。今天,你的Flutter应用在Android和iOS上跑得正欢,明天可能就需要考虑一个新的平台:HarmonyOS(鸿蒙)。这不是一道选答题,而是很多团队正在面对的现实。

Flutter的优势很明确——写一套代码,就能在两个主要平台上运行,开发体验流畅。而鸿蒙代表的是下一个时代的互联生态,它不仅仅是手机系统,更着眼于未来全场景的体验。将现有的Flutter应用适配到鸿蒙,听起来像是一个“跨界”任务,但它本质上是一次有价值的技术拓展:让产品触达更多用户,也让技术栈覆盖更广。

不过,这条路走起来并不像听起来那么简单。Flutter和鸿蒙,从底层的架构到上层的工具链,都有着各自的设计逻辑。会遇到一些具体的问题:代码如何组织?原有的功能在鸿蒙上如何实现?那些平台特有的能力该怎么调用?更实际的是,从编译打包到上架部署,整个流程都需要重新摸索。
这篇文章想做的,就是把这些我们趟过的路、踩过的坑,清晰地摊开给你看。我们不会只停留在“怎么做”,还会聊到“为什么得这么做”,以及“如果出了问题该往哪想”。这更像是一份实战笔记,源自真实的项目经验,聚焦于那些真正卡住过我们的环节。

无论你是在为一个成熟产品寻找新的落地平台,还是从一开始就希望构建能面向多端的应用,这里的思路和解决方案都能提供直接的参考。理解了两套体系之间的异同,掌握了关键的衔接技术,不仅能完成这次迁移,更能积累起应对未来技术变化的能力。

混合工程结构深度解析

项目目录架构

当Flutter项目集成鸿蒙支持后,典型的项目结构会发生显著变化。以下是经过ohos_flutter插件初始化后的项目结构:

my_flutter_harmony_app/ ├── lib/ # Flutter业务代码(基本不变) │ ├── main.dart # 应用入口 │ ├── home_page.dart # 首页 │ └── utils/ │ └── platform_utils.dart # 平台工具类 ├── pubspec.yaml # Flutter依赖配置 ├── ohos/ # 鸿蒙原生层(核心适配区) │ ├── entry/ # 主模块 │ │ └── src/main/ │ │ ├── ets/ # ArkTS代码 │ │ │ ├── MainAbility/ │ │ │ │ ├── MainAbility.ts # 主Ability │ │ │ │ └── MainAbilityContext.ts │ │ │ └── pages/ │ │ │ ├── Index.ets # 主页面 │ │ │ └── Splash.ets # 启动页 │ │ ├── resources/ # 鸿蒙资源文件 │ │ │ ├── base/ │ │ │ │ ├── element/ # 字符串等 │ │ │ │ ├── media/ # 图片资源 │ │ │ │ └── profile/ # 配置文件 │ │ │ └── en_US/ # 英文资源 │ │ └── config.json # 应用核心配置 │ ├── ohos_test/ # 测试模块 │ ├── build-profile.json5 # 构建配置 │ └── oh-package.json5 # 鸿蒙依赖管理 └── README.md

展示效果图片

flutter 实时预览 效果展示

运行到鸿蒙虚拟设备中效果展示

目录

  • 功能代码实现
    • FlHorizontalBarChart 组件实现
    • 主页面集成
  • 本次开发中容易遇到的问题
  • 总结本次开发中用到的技术点

功能代码实现

FlHorizontalBarChart 组件实现

FlHorizontalBarChart 是一个基于 fl_chart 库实现的柱状图组件,具有以下特性:

  • 支持自定义标题和数据
  • 实现点击交互效果
  • 包含图例说明
  • 响应式布局设计

核心代码实现

import'package:flutter/material.dart';import'package:fl_chart/fl_chart.dart';classFlHorizontalBarChartextendsStatefulWidget{finalStringtitle;finalList<Map<String,dynamic>>data;finaldouble?width;finaldouble?height;constFlHorizontalBarChart({Key?key,requiredthis.title,requiredthis.data,this.width,this.height,}):super(key:key);@overrideState<FlHorizontalBarChart>createState()=>_FlHorizontalBarChartState();}class_FlHorizontalBarChartStateextendsState<FlHorizontalBarChart>{int?_touchedIndex;String_clickInfo='';// 颜色列表finalList<Color>colors=[Colors.blue,Colors.green,Colors.orange,Colors.purple,Colors.red,Colors.yellow,Colors.teal,Colors.pink,];void_onBarTap(BarTouchResponse?response){if(response!=null&&response.spot!=null){setState((){_touchedIndex=response.spot!.touchedBarGroupIndex;finaldataItem=widget.data[_touchedIndex!];_clickInfo='点击了: ${dataItem['name']} (${dataItem['value']})';});// 显示点击信息提示ScaffoldMessenger.of(context).showSnackBar(SnackBar(content:Text('${widget.data[_touchedIndex!]['name']}: ${widget.data[_touchedIndex!]['value']}'),duration:constDuration(seconds:1),),);}}@overrideWidgetbuild(BuildContextcontext){// 计算最大值用于Y轴缩放double maxValue=0;for(variteminwidget.data){finalvalue=item['value']asdouble???(item['value']asint).toDouble();if(value>maxValue){maxValue=value;}}// 向上取整到最近的100的倍数maxValue=(maxValue/100).ceil()*100;returnContainer(width:widget.width??double.infinity,height:widget.height??400,margin:constEdgeInsets.all(16),padding:constEdgeInsets.all(16),decoration:BoxDecoration(borderRadius:BorderRadius.circular(8),color:Colors.white,boxShadow:[BoxShadow(color:Colors.grey.withOpacity(0.2),spreadRadius:1,blurRadius:3,offset:constOffset(0,1),),],),child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[Text(widget.title,style:constTextStyle(fontSize:18,fontWeight:FontWeight.bold,color:Colors.black87,),),constSizedBox(height:16),Expanded(child:BarChart(BarChartData(alignment:BarChartAlignment.spaceAround,barTouchData:BarTouchData(touchCallback:(event,response)=>_onBarTap(response),enabled:true,touchTooltipData:BarTouchTooltipData(tooltipBgColor:Colors.blueGrey,getTooltipItem:(group,groupIndex,rod,rodIndex){finaldataItem=widget.data[groupIndex];returnBarTooltipItem('${dataItem['name']}: ${dataItem['value']}',constTextStyle(color:Colors.white),);},),),titlesData:FlTitlesData(leftTitles:AxisTitles(sideTitles:SideTitles(showTitles:true,getTitlesWidget:(value,meta){if(value.toInt()>=0&&value.toInt()<widget.data.length){returnText(widget.data[value.toInt()]['name'],style:constTextStyle(fontSize:12),);}returnconstText('');},interval:1,reservedSize:80,),),bottomTitles:AxisTitles(sideTitles:SideTitles(showTitles:true,getTitlesWidget:(value,meta){returnText('\$value',style:constTextStyle(fontSize:12),);},interval:maxValue/5,),),topTitles:constAxisTitles(sideTitles:SideTitles(showTitles:false)),rightTitles:constAxisTitles(sideTitles:SideTitles(showTitles:false)),),gridData:FlGridData(show:true,drawVerticalLine:true,drawHorizontalLine:false,verticalInterval:maxValue/5,),borderData:FlBorderData(show:true,border:constBorder(bottom:BorderSide(),left:BorderSide(),),),maxY:maxValue,barGroups:widget.data.asMap().entries.map((entry){finalindex=entry.key;finaldata=entry.value;finalvalue=data['value']asdouble???(data['value']asint).toDouble();finalisTouched=index==_touchedIndex;finalbarWidth=isTouched?25.0:20.0;returnBarChartGroupData(x:index,barRods:[BarChartRodData(toY:value,color:colors[index%colors.length],width:barWidth,borderRadius:BorderRadius.circular(4),),],);}).toList(),),),),if(_clickInfo.isNotEmpty)Padding(padding:constEdgeInsets.only(top:12),child:Text(_clickInfo,style:constTextStyle(fontSize:14,color:Colors.grey,),),),],),);}}

组件参数说明

参数类型必填描述
titleString柱状图标题
dataList<Map<String, dynamic>>柱状图数据,每个元素包含 name 和 value 字段
widthdouble?组件宽度,默认充满父容器
heightdouble?组件高度,默认 400

使用方法

FlHorizontalBarChart(title:'访问来源分析',data:[{'value':335,'name':'直接访问'},{'value':310,'name':'邮件营销'},{'value':234,'name':'联盟广告'},{'value':135,'name':'视频广告'},{'value':1548,'name':'搜索引擎'}],)

主页面集成

主页面使用 MyHomePage 组件实现,集成了两个 FlHorizontalBarChart 实例,分别展示访问来源分析和产品销售分布数据。

核心代码实现

import'package:flutter/material.dart';import'package:aa/widgets/fl_horizontal_bar_chart.dart';voidmain(){runApp(constMyApp());}classMyAppextendsStatelessWidget{constMyApp({super.key});@overrideWidgetbuild(BuildContextcontext){returnMaterialApp(title:'Flutter for openHarmony',theme:ThemeData(colorScheme:ColorScheme.fromSeed(seedColor:Colors.deepPurple),useMaterial3:true,),debugShowCheckedModeBanner:false,home:constMyHomePage(title:'Flutter for openHarmony'),);}}classMyHomePageextendsStatefulWidget{constMyHomePage({super.key,requiredthis.title});finalStringtitle;@overrideState<MyHomePage>createState()=>_MyHomePageState();}class_MyHomePageStateextendsState<MyHomePage>{// 柱状图数据finalList<Map<String,dynamic>>barData1=[{'value':335,'name':'直接访问'},{'value':310,'name':'邮件营销'},{'value':234,'name':'联盟广告'},{'value':135,'name':'视频广告'},{'value':1548,'name':'搜索引擎'}];finalList<Map<String,dynamic>>barData2=[{'value':120,'name':'A产品'},{'value':200,'name':'B产品'},{'value':150,'name':'C产品'},{'value':80,'name':'D产品'},{'value':70,'name':'E产品'}];@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:Text(widget.title),),body:SingleChildScrollView(padding:constEdgeInsets.all(16),child:Column(crossAxisAlignment:CrossAxisAlignment.center,children:<Widget>[constSizedBox(height:20),// 第一个柱状图FlHorizontalBarChart(title:'访问来源分析',data:barData1,),constSizedBox(height:40),// 第二个柱状图FlHorizontalBarChart(title:'产品销售分布',data:barData2,),constSizedBox(height:40),constText('交互说明:点击柱状图查看详细信息',style:TextStyle(fontSize:16,color:Colors.grey,),),constSizedBox(height:40),],),),);}}

本次开发中容易遇到的问题

  1. 依赖管理问题

    • 问题:添加 fl_chart 依赖后,需要确保依赖版本与 Flutter 版本兼容,否则可能导致编译失败或运行时错误
    • 解决方案:在 pubspec.yaml 中指定合适的 fl_chart 版本,如 ^0.64.0,并执行flutter pub get命令更新依赖
  2. 数据格式问题

    • 问题:柱状图数据中的 value 字段类型需要为 double,直接使用 int 类型可能导致类型错误
    • 解决方案:在构建 BarChartRodData 时,对 int 类型的 value 进行类型转换,如(value as int).toDouble()
  3. 布局适配问题

    • 问题:在不同屏幕尺寸下,柱状图和标签的布局可能出现异常,特别是在小屏幕设备上
    • 解决方案:使用 Expanded 组件和响应式布局,确保在不同屏幕尺寸下的良好显示效果,同时为组件提供可自定义的宽高参数
  4. 交互响应问题

    • 问题:使用 fl_chart 库时,BarTouchData 的 touchCallback 方法签名与预期不符,可能导致编译错误
    • 解决方案:使用适配的回调方式,通过匿名函数转换参数格式,确保回调函数能正确接收和处理事件参数
  5. Y轴刻度计算问题

    • 问题:柱状图的 Y 轴刻度需要根据数据最大值动态计算,否则可能导致刻度显示不合理
    • 解决方案:遍历数据计算最大值,然后向上取整到合适的刻度值,确保所有数据都能在图表中完整显示

总结本次开发中用到的技术点

  1. Flutter 状态管理

    • 使用 StatefulWidget 和 setState 管理组件状态,实现柱状图点击交互效果
    • 通过状态变量 _touchedIndex 跟踪当前点击的柱状图
  2. Flutter 布局系统

    • 使用 Container、Column、Expanded 等布局组件构建响应式界面
    • 运用 BoxDecoration 实现卡片阴影效果,提升视觉体验
  3. fl_chart 图表库

    • 基于 fl_chart 库实现柱状图渲染
    • 配置 BarChartData、BarChartGroupData 等参数实现自定义柱状图效果
    • 使用 BarTouchData 实现柱状图点击交互和提示信息
  4. 数据处理

    • 使用 List<Map<String, dynamic>> 存储和管理柱状图数据
    • 实现数据最大值计算和 Y 轴刻度动态调整
  5. 用户交互

    • 实现柱状图点击事件处理,显示详细信息
    • 使用 ScaffoldMessenger 显示 SnackBar 提示信息
  6. 代码组织

    • 采用组件化开发方式,将柱状图封装为独立的 FlHorizontalBarChart 组件
    • 分离业务逻辑和 UI 展示,提高代码可维护性
  7. 跨平台适配

    • 选择跨平台兼容性好的 fl_chart 库,避免平台特定依赖
    • 确保代码在 Flutter 支持的所有平台(包括 OpenHarmony)上正常运行

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询