Flutter for OpenHarmony 实战:通知栏实现
用于创建具有状态的组件:实现通知的进入和退出动画:用于通知的滑动动画效果Overlay 和 OverlayEntry:用于在界面顶层显示通知Material:提供阴影和圆角效果Row 和 Expanded:实现通知内容的布局。
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
前言:跨生态开发的新机遇
在移动开发领域,我们总是面临着选择与适配。今天,你的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 实时预览 效果展示
运行到鸿蒙虚拟设备中效果展示
目录
功能代码实现
NotificationType 枚举
设计思路
为了支持不同类型的通知(信息、成功、警告、错误),我们首先定义了一个 NotificationType 枚举类,用于区分不同类型的通知样式和行为。
核心代码
/// 通知类型枚举
enum NotificationType {
info, // 信息通知
success, // 成功通知
warning, // 警告通知
error, // 错误通知
}
使用方法
在创建 NotificationBar 组件或调用 NotificationManager 的方法时,通过指定 NotificationType 来选择通知类型。
NotificationBar 组件
设计思路
NotificationBar 组件是通知功能的核心,它负责显示通知内容、处理动画效果和交互逻辑。我们设计了以下特性:
- 支持四种通知类型,每种类型有不同的颜色和图标
- 支持自动消失功能,可自定义消失延迟时间
- 支持自定义 leading 和 trailing 组件
- 支持自定义背景色、文本样式、阴影和边框圆角
- 实现了平滑的进入和退出动画
核心代码
/// 通知栏组件
class NotificationBar extends StatefulWidget {
final String message;
final NotificationType type;
final Duration duration;
final bool autoDismiss;
final Widget? leading;
final Widget? trailing;
final VoidCallback? onDismiss;
final Color? backgroundColor;
final TextStyle? textStyle;
final double elevation;
final BorderRadius borderRadius;
const NotificationBar({
Key? key,
required this.message,
this.type = NotificationType.info,
this.duration = const Duration(seconds: 3),
this.autoDismiss = true,
this.leading,
this.trailing,
this.onDismiss,
this.backgroundColor,
this.textStyle,
this.elevation = 4.0,
this.borderRadius = const BorderRadius.all(Radius.circular(8.0)),
}) : super(key: key);
State<NotificationBar> createState() => _NotificationBarState();
}
class _NotificationBarState extends State<NotificationBar> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _slideAnimation;
bool _isVisible = true;
void initState() {
super.initState();
// 初始化动画控制器
_controller = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
// 初始化滑动动画
_slideAnimation = Tween<Offset>(
begin: const Offset(0, -1),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
));
// 启动进入动画
_controller.forward();
// 如果设置为自动消失,则启动定时器
if (widget.autoDismiss) {
Future.delayed(widget.duration, () {
if (mounted) {
_dismiss();
}
});
}
}
void dispose() {
_controller.dispose();
super.dispose();
}
// 关闭通知
void _dismiss() {
_controller.reverse().then((_) {
setState(() {
_isVisible = false;
});
if (widget.onDismiss != null) {
widget.onDismiss!();
}
});
}
// 根据通知类型获取颜色
Color _getTypeColor() {
switch (widget.type) {
case NotificationType.success:
return Colors.green;
case NotificationType.warning:
return Colors.orange;
case NotificationType.error:
return Colors.red;
case NotificationType.info:
default:
return Colors.blue;
}
}
// 根据通知类型获取图标
Widget _getTypeIcon() {
switch (widget.type) {
case NotificationType.success:
return const Icon(Icons.check_circle, color: Colors.white);
case NotificationType.warning:
return const Icon(Icons.warning, color: Colors.white);
case NotificationType.error:
return const Icon(Icons.error, color: Colors.white);
case NotificationType.info:
default:
return const Icon(Icons.info, color: Colors.white);
}
}
Widget build(BuildContext context) {
if (!_isVisible) {
return const SizedBox.shrink();
}
final bgColor = widget.backgroundColor ?? _getTypeColor();
final leadingWidget = widget.leading ?? _getTypeIcon();
final trailingWidget = widget.trailing ??
IconButton(
icon: const Icon(Icons.close, color: Colors.white),
onPressed: _dismiss,
);
return SlideTransition(
position: _slideAnimation,
child: Material(
elevation: widget.elevation,
borderRadius: widget.borderRadius,
child: Container(
decoration: BoxDecoration(
color: bgColor,
borderRadius: widget.borderRadius,
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 12),
child: leadingWidget,
),
Expanded(
child: Text(
widget.message,
style: widget.textStyle ??
const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
trailingWidget,
],
),
),
),
);
}
}
使用方法
在页面中直接使用 NotificationBar 组件:
NotificationBar(
message: '这是一条信息通知,用于显示一般信息',
type: NotificationType.info,
autoDismiss: false,
),
注意事项
- 动画控制器管理:组件使用了
AnimationController,需要在dispose方法中正确释放资源,避免内存泄漏。 - 自动消失逻辑:使用
Future.delayed实现自动消失功能时,需要检查组件是否仍然挂载,避免在组件已销毁后调用setState。 - 布局适配:组件使用了
SlideTransition实现动画效果,需要确保父容器有足够的空间。
NotificationManager 管理器
设计思路
为了方便在应用的任何地方快速显示通知,我们创建了 NotificationManager 类,提供了静态方法来显示不同类型的通知。
核心代码
/// 通知栏管理器
class NotificationManager {
static OverlayEntry? _currentEntry;
/// 显示通知
static void show(
BuildContext context,
String message,
NotificationType type,
) {
// 先移除当前显示的通知
dismiss();
// 创建新的通知条目
_currentEntry = OverlayEntry(
builder: (context) => Positioned(
top: 40, // 距离顶部的距离
left: 16,
right: 16,
child: NotificationBar(
message: message,
type: type,
onDismiss: () {
dismiss();
},
),
),
);
// 将通知添加到覆盖层
Overlay.of(context)?.insert(_currentEntry!);
}
/// 显示信息通知
static void info(BuildContext context, String message) {
show(context, message, NotificationType.info);
}
/// 显示成功通知
static void success(BuildContext context, String message) {
show(context, message, NotificationType.success);
}
/// 显示警告通知
static void warning(BuildContext context, String message) {
show(context, message, NotificationType.warning);
}
/// 显示错误通知
static void error(BuildContext context, String message) {
show(context, message, NotificationType.error);
}
/// 关闭通知
static void dismiss() {
if (_currentEntry != null) {
_currentEntry!.remove();
_currentEntry = null;
}
}
}
使用方法
通过 NotificationManager 的静态方法快速显示通知:
ElevatedButton(
onPressed: () {
NotificationManager.info(context, '这是一条信息通知');
},
child: const Text('显示信息通知'),
),
注意事项
- 覆盖层管理:使用
OverlayEntry显示通知时,需要在适当的时候调用remove方法移除,避免内存泄漏。 - 通知队列:当前实现会替换已显示的通知,如需支持通知队列,需要进一步扩展。
- 上下文传递:显示通知时需要传递
BuildContext,确保在正确的上下文环境中显示。
主页面集成
设计思路
在首页直接显示不同类型的通知栏效果,让用户能够直观地了解通知功能的使用方法和效果。
核心代码
import 'package:flutter/material.dart';
import 'widgets/notification_bar.dart';
void main() {
runApp(const MyApp());
}
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 MyHomePage(title: 'Flutter for openHarmony'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'通知栏效果展示',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
const Text(
'以下是不同类型的通知栏:',
style: TextStyle(fontSize: 16),
),
const SizedBox(height: 40),
// 信息通知
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: NotificationBar(
message: '这是一条信息通知,用于显示一般信息',
type: NotificationType.info,
autoDismiss: false,
),
),
const SizedBox(height: 20),
// 成功通知
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: NotificationBar(
message: '操作成功!数据已保存',
type: NotificationType.success,
autoDismiss: false,
),
),
const SizedBox(height: 20),
// 警告通知
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: NotificationBar(
message: '警告:请检查您的输入信息',
type: NotificationType.warning,
autoDismiss: false,
),
),
const SizedBox(height: 20),
// 错误通知
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: NotificationBar(
message: '错误:操作失败,请重试',
type: NotificationType.error,
autoDismiss: false,
),
),
const SizedBox(height: 40),
ElevatedButton(
onPressed: () {
NotificationManager.info(context, '这是一条信息通知');
},
child: const Text('显示信息通知'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
NotificationManager.success(context, '操作成功!');
},
child: const Text('显示成功通知'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
NotificationManager.warning(context, '警告信息');
},
child: const Text('显示警告通知'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
NotificationManager.error(context, '错误信息');
},
child: const Text('显示错误通知'),
),
],
),
),
);
}
}
使用方法
直接运行应用,首页会显示四种类型的通知栏效果,以及四个按钮用于触发不同类型的通知。
开发中容易遇到的问题
1. 动画控制器内存泄漏
问题描述:在使用 AnimationController 时,如果不及时释放资源,可能会导致内存泄漏。
解决方案:在 dispose 方法中正确调用 _controller.dispose() 释放动画控制器资源。
2. 组件已销毁后调用 setState
问题描述:在使用 Future.delayed 实现自动消失功能时,如果组件在延迟期间被销毁,调用 setState 会导致错误。
解决方案:在调用 setState 之前,使用 mounted 属性检查组件是否仍然挂载。
if (mounted) {
_dismiss();
}
3. OverlayEntry 未正确移除
问题描述:使用 OverlayEntry 显示通知时,如果不及时移除,可能会导致内存泄漏和界面混乱。
解决方案:在通知关闭时,调用 _currentEntry!.remove() 移除覆盖层条目,并将 _currentEntry 置为 null。
4. 通知显示位置适配
问题描述:在不同设备和屏幕尺寸上,通知的显示位置可能需要调整。
解决方案:使用相对位置和边距,确保通知在不同设备上都能正确显示。
5. 通知类型颜色和图标一致性
问题描述:不同类型的通知需要保持颜色和图标的一致性,以提供良好的用户体验。
解决方案:通过统一的方法 _getTypeColor() 和 _getTypeIcon() 来获取不同类型通知的颜色和图标,确保一致性。
总结开发中用到的技术点
1. Flutter 核心组件
- StatefulWidget:用于创建具有状态的
NotificationBar组件 - AnimationController:实现通知的进入和退出动画
- SlideTransition:用于通知的滑动动画效果
- Overlay 和 OverlayEntry:用于在界面顶层显示通知
- Material:提供阴影和圆角效果
- Row 和 Expanded:实现通知内容的布局
2. Dart 语言特性
- 枚举(Enum):定义
NotificationType枚举,用于区分不同类型的通知 - 可选参数:在组件构造函数中使用可选参数,提供灵活的配置选项
- 空安全(Null Safety):使用
?和!操作符处理可能为 null 的值 - 静态方法:在
NotificationManager中使用静态方法,方便快速调用 - Future.delayed:实现通知的自动消失功能
3. 组件化开发
- 组件拆分:将通知功能拆分为
NotificationBar组件和NotificationManager管理器,提高代码复用性和可维护性 - 参数化设计:通过参数化设计,使组件具有高度的可定制性
- 管理器模式:使用
NotificationManager类管理通知的显示和隐藏,提供简洁的 API
4. 动画效果
- 平滑动画:使用
Tween<Offset>和CurvedAnimation实现通知的平滑滑动效果 - 动画控制:通过
AnimationController控制动画的启动、暂停和反转 - 动画曲线:使用
Curves.easeOut使动画效果更加自然
5. 状态管理
- 组件内部状态:使用
_isVisible变量管理通知的显示状态 - 全局状态:使用静态变量
_currentEntry管理当前显示的通知
6. 用户体验优化
- 自动消失:支持通知自动消失,减少用户操作
- 手动关闭:提供关闭按钮,允许用户手动关闭通知
- 视觉反馈:通过不同的颜色和图标,为不同类型的通知提供清晰的视觉反馈
- 动画效果:添加平滑的动画效果,提升用户体验
7. 跨平台适配
- Flutter 跨平台特性:利用 Flutter 的跨平台特性,确保通知功能在不同平台上都能正常工作
- 响应式布局:使用相对位置和边距,确保通知在不同屏幕尺寸上都能正确显示
通过以上技术点的综合应用,我们成功实现了一个功能完善、用户体验良好的通知栏功能,可在 Flutter for OpenHarmony 项目中使用。
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)