Flutter for OpenHarmony 实战:颜色选择器实现
组件化开发将功能抽离为独立的组件,提高代码复用性遵循 Flutter 的组件化开发理念,保持组件的单一职责原则通过状态管理实现组件内部的数据流转布局技术使用ScaffoldColumnContainer等基础布局组件构建界面运用PaddingSizedBox等组件调整间距和布局使用实现网格布局,提高性能样式技术使用实现阴影、圆角等效果运用Border为选中元素添加边框,增强视觉反馈合理设置字体大小
前言:跨生态开发的新机遇
在移动开发领域,我们总是面临着选择与适配。今天,你的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 实时预览 效果展示
运行到鸿蒙虚拟设备中效果展示
目录
功能代码实现
颜色选择器组件
在本次开发中,我们实现了一个功能完整的颜色选择器组件 ColorPicker,该组件位于 lib/components/color_picker.dart 文件中。这个组件包含了颜色选择、实时预览、颜色代码展示等功能,具体实现如下:
核心结构设计
颜色选择器采用了清晰的分层结构,包含以下核心元素:
- 顶部应用栏(随选中颜色动态变化)
- 选中颜色展示区域
- 颜色代码和RGB值展示区域
- 颜色选择网格
import 'package:flutter/material.dart';
class ColorPicker extends StatefulWidget {
const ColorPicker({super.key});
State<ColorPicker> createState() => _ColorPickerState();
}
class _ColorPickerState extends State<ColorPicker> {
Color _selectedColor = Colors.blue;
final List<Color> _colorOptions = [
Colors.red,
Colors.orange,
Colors.yellow,
Colors.green,
Colors.blue,
Colors.indigo,
Colors.purple,
Colors.pink,
Colors.brown,
Colors.grey,
Colors.black,
Colors.white,
];
void _selectColor(Color color) {
setState(() {
_selectedColor = color;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('颜色选择器'),
backgroundColor: _selectedColor,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 组件内容
],
),
),
);
}
}
选中颜色展示区域
这个区域用于实时预览当前选中的颜色,采用了卡片式设计,带有阴影效果:
// 选中颜色展示
Container(
width: double.infinity,
height: 200,
decoration: BoxDecoration(
color: _selectedColor,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withValues(alpha: 0.2),
blurRadius: 8,
spreadRadius: 2,
),
],
),
child: Center(
child: Text(
'选中颜色',
style: TextStyle(
color: _selectedColor == Colors.white ? Colors.black : Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 32),
颜色代码展示区域
这个区域用于展示当前选中颜色的代码值和RGB值,方便开发者直接使用:
// 颜色代码展示
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
'颜色代码',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 8),
Text(
'${( (_selectedColor.r * 255).toInt() << 16 ) | ( (_selectedColor.g * 255).toInt() << 8 ) | ( (_selectedColor.b * 255).toInt() )}',
style: const TextStyle(
fontSize: 18,
fontFamily: 'Monospace',
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'RGB: (${_selectedColor.r}, ${_selectedColor.g}, ${_selectedColor.b})',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
],
),
],
),
),
const SizedBox(height: 32),
颜色选择网格
这个区域展示了12种预设颜色,用户可以通过点击选择:
// 颜色选择网格
Text(
'选择颜色',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 16),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 6,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: _colorOptions.length,
itemBuilder: (context, index) {
final color = _colorOptions[index];
return GestureDetector(
onTap: () => _selectColor(color),
child: Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(8),
border: _selectedColor == color
? Border.all(
color: Colors.black,
width: 3,
)
: null,
boxShadow: [
if (_selectedColor == color)
BoxShadow(
color: Colors.grey.withValues(alpha: 0.3),
blurRadius: 4,
spreadRadius: 2,
),
],
),
),
);
},
),
组件使用方法
直接在首页使用
在本次开发中,我们将颜色选择器组件直接作为应用的首页使用,无需通过按钮跳转。具体实现如下:
- 导入组件
在 main.dart 文件中导入颜色选择器组件:
import 'package:flutter/material.dart';
import 'components/color_picker.dart';
- 设置为首页
在 MyApp 组件的 build 方法中,将 home 属性设置为 ColorPicker:
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter for openHarmony',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
home: const ColorPicker(),
);
}
}
开发注意事项
- 组件抽离
我们将颜色选择器功能抽离为独立的组件,这样做的好处是:
- 代码结构更清晰,职责划分明确
- 颜色选择器组件可以在其他地方复用
- 便于维护和测试
- 性能优化
- 使用
const构造器优化性能 - 对固定样式的组件使用
const修饰符 - 合理使用
shrinkWrap和NeverScrollableScrollPhysics优化 GridView 性能
- 用户体验
- 实时更新 AppBar 背景色,提供直观的视觉反馈
- 根据选中颜色自动调整文本颜色,确保可读性
- 为选中颜色添加边框和阴影效果,增强视觉层次感
- 代码质量
- 遵循 Flutter 的代码风格规范
- 及时修复 lint 警告,确保代码质量
- 使用合适的变量命名和注释,提高代码可维护性
本次开发中容易遇到的问题
- 类型转换问题
在处理颜色值时,可能会遇到类型转换的问题。例如,Color 类的 r、g、b 属性返回的是 double 类型,而位移运算需要 int 类型。
解决方案:在进行位移运算前,先将 double 类型转换为 int 类型,例如:(_selectedColor.r * 255).toInt()。
- 废弃 API 使用问题
在开发过程中,可能会遇到使用废弃 API 的警告,例如 Color 类的 value、red、green、blue 属性已被标记为废弃。
解决方案:使用推荐的替代方法,例如使用 _selectedColor.r、_selectedColor.g、_selectedColor.b 替代 _selectedColor.red、_selectedColor.green、_selectedColor.blue。
- 布局溢出问题
当使用 GridView 时,如果不设置适当的约束条件,可能会导致布局溢出。
解决方案:为 GridView 设置 shrinkWrap: true 和 physics: const NeverScrollableScrollPhysics(),使其适应父容器的高度。
- 颜色对比度问题
当选中浅色背景时,文本可能会变得难以阅读。
解决方案:根据选中颜色的亮度动态调整文本颜色,例如:color: _selectedColor == Colors.white ? Colors.black : Colors.white。
- 鸿蒙平台适配问题
在将 Flutter 应用运行到鸿蒙平台时,可能会遇到一些平台特有的适配问题。
解决方案:
- 确保使用的 Flutter 版本支持 OpenHarmony
- 测试时使用鸿蒙虚拟设备进行验证
- 注意平台差异,避免使用平台特定的 API
总结本次开发中用到的技术点
- 组件化开发
- 将功能抽离为独立的组件,提高代码复用性
- 遵循 Flutter 的组件化开发理念,保持组件的单一职责原则
- 通过状态管理实现组件内部的数据流转
- 布局技术
- 使用
Scaffold、Column、Container等基础布局组件构建界面 - 运用
Padding、SizedBox等组件调整间距和布局 - 使用
GridView.builder实现网格布局,提高性能
- 样式技术
- 使用
BoxDecoration实现阴影、圆角等效果 - 运用
Border为选中元素添加边框,增强视觉反馈 - 合理设置字体大小、颜色和字重,提高可读性
- 状态管理
- 使用
setState管理组件内部状态 - 通过状态更新实现 UI 的动态变化
- 保持状态管理的简洁性,适合中小型组件
- 用户交互
- 使用
GestureDetector实现点击交互 - 提供实时视觉反馈,增强用户体验
- 根据用户操作动态更新界面状态
- 性能优化技术
- 使用
const构造器优化性能 - 对
GridView进行适当配置,避免不必要的渲染 - 合理使用
shrinkWrap和NeverScrollableScrollPhysics优化滚动性能
- 代码质量保障
- 遵循 Flutter 代码风格规范
- 及时修复 lint 警告,确保代码质量
- 使用合适的变量命名和注释,提高代码可维护性
- 平台适配技术
- 确保代码在 OpenHarmony 平台上正常运行
- 考虑跨平台兼容性,避免使用平台特定的 API
- 测试验证在不同平台上的表现
通过本次开发,我们成功实现了一个功能完整、用户体验良好的颜色选择器组件,并将其应用于 Flutter for OpenHarmony 项目中。这个实现不仅展示了 Flutter 的强大布局能力,也验证了 Flutter 应用在 OpenHarmony 平台上的可行性。
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)