Flutter for OpenHarmony 实战:顶部消息通知栏(Overlay 实现)
override// 背景动画循环播放// 通知持续时间// 是否可关闭// 是否显示操作按钮提供动画控制器所需的vsync。_bg控制背景动画,其他变量控制通知行为。});@overrideduration: const Duration(milliseconds: 420), // 进入动画时长reverseDuration: const Duration(milliseconds: 260)
前言
Flutter是Google开发的开源UI工具包,支持用一套代码构建iOS、Android、Web、Windows、macOS和Linux六大平台应用,实现"一次编写,多处运行"。
OpenHarmony是由开放原子开源基金会运营的分布式操作系统,为全场景智能设备提供统一底座,具有多设备支持、模块化设计、分布式能力和开源开放等特性。
Flutter for OpenHarmony技术方案使开发者能够:
- 复用Flutter现有代码(Skia渲染引擎、热重载、丰富组件库)
- 快速构建符合OpenHarmony规范的UI
- 降低多端开发成本
- 利用Dart生态插件资源加速生态建设
先看效果

在鸿蒙真机 上模拟器上成功运行后的效果

本文详细解析了一个完整的 Flutter 顶部通知栏应用的开发过程,支持队列管理、流畅动画、毛玻璃效果、发光边框和拖拽关闭等现代化交互特性,使用 Overlay 实现覆盖层显示,易于维护和扩展。
📋 目录
项目结构说明
应用入口
演示页面 (TopNoticeDemoPage)
TopNotice 核心类
_TopNoticeManager 管理器
_TopNoticeOverlayEntry 覆盖层入口
TopNoticeCard 卡片组件
📁 项目结构说明
文件目录结构
lib/
├── main.dart # 应用入口文件
├── app/ # 应用配置目录
│ └── app.dart # 应用主类
├── demo/ # 演示页面目录
│ └── top_notice_demo_page.dart # 顶部通知栏演示页面
└── overlay_notice/ # 通知栏组件目录
├── top_notice.dart # 通知栏核心逻辑
└── top_notice_card.dart # 通知卡片组件
文件说明
入口文件
lib/main.dart
- 应用入口点,包含
main()函数 - 定义
MyApp类,启动应用
应用配置
lib/app/app.dart
DemoApp类:应用主类- 配置应用主题
- 设置路由和首页
演示页面
lib/demo/top_notice_demo_page.dart
TopNoticeDemoPage类:演示页面主类- 展示各种类型的通知
- 提供配置选项(持续时间、是否可关闭、是否显示操作按钮)
- 支持批量显示通知
核心组件
lib/overlay_notice/top_notice.dart
TopNotice类:通知栏静态接口_TopNoticeManager类:通知管理器(队列管理)_TopNoticeOverlayEntry类:覆盖层入口组件- 数据模型:
TopNoticeType、TopNoticeData、TopNoticeAction、TopNoticeController
lib/overlay_notice/top_notice_card.dart
TopNoticeCard组件:通知卡片 UI- 毛玻璃效果
- 发光边框
- 进度条显示
- 关闭按钮
组件依赖关系
main.dart
└── app/app.dart (导入应用配置)
└── demo/top_notice_demo_page.dart (导入演示页面)
└── overlay_notice/top_notice.dart (导入通知栏核心)
└── overlay_notice/top_notice_card.dart (导入通知卡片)
数据流向
- 触发通知:调用
TopNotice.show(context, data)显示通知 - 队列管理:
_TopNoticeManager将通知加入队列 - 覆盖层显示:创建
OverlayEntry插入到覆盖层 - 动画播放:
_TopNoticeOverlayEntry播放滑入动画 - 自动关闭:计时器到期或用户拖拽关闭
- 队列处理:当前通知关闭后,自动显示下一个
应用入口
1. main() 函数
import 'package:flutter/material.dart';
import 'app/app.dart';
void main() {
runApp(const MyApp());
}
应用入口,导入应用配置。
2. MyApp 类
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const DemoApp();
}
}
应用根组件,返回 DemoApp。
演示页面 (TopNoticeDemoPage)
1. 类定义和状态管理
class TopNoticeDemoPage extends StatefulWidget {
const TopNoticeDemoPage({super.key});
State<TopNoticeDemoPage> createState() => _TopNoticeDemoPageState();
}
class _TopNoticeDemoPageState extends State<TopNoticeDemoPage>
with SingleTickerProviderStateMixin {
late final AnimationController _bg = AnimationController(
vsync: this,
duration: const Duration(seconds: 10),
)..repeat(); // 背景动画循环播放
Duration _duration = const Duration(seconds: 4); // 通知持续时间
bool _dismissible = true; // 是否可关闭
bool _withAction = true; // 是否显示操作按钮
SingleTickerProviderStateMixin 提供动画控制器所需的 vsync。_bg 控制背景动画,其他变量控制通知行为。
2. 显示通知
void _show(TopNoticeType type) {
final label = switch (type) {
TopNoticeType.success => 'Success',
TopNoticeType.info => 'Info',
TopNoticeType.warning => 'Warning',
TopNoticeType.error => 'Error',
};
TopNotice.show(
context,
TopNoticeData(
type: type, // 通知类型
title: '$label · 顶部 Overlay 通知',
message: '支持队列、滑入动画、毛玻璃、发光边框、拖拽上滑关闭。',
duration: _duration, // 持续时间
dismissible: _dismissible, // 是否可关闭
action: _withAction
? TopNoticeAction(
label: 'Action',
leading: Icons.bolt_rounded,
onPressed: () {
// 点击操作按钮可以触发新的通知
TopNotice.show(
context,
const TopNoticeData(
type: TopNoticeType.success,
title: 'Action 已触发',
message: '这是一个由 action 触发的二次通知(队列展示)。',
duration: Duration(seconds: 2),
),
);
},
)
: null,
onTap: () {
// 点击通知的回调
},
),
);
}
_show() 方法根据类型显示通知。使用 switch 表达式获取标签,TopNotice.show() 显示通知。操作按钮可以触发新的通知,实现队列展示。
3. 批量显示
void _burst() {
final types = [
TopNoticeType.info,
TopNoticeType.success,
TopNoticeType.warning,
TopNoticeType.error,
TopNoticeType.info,
];
for (final t in types) {
_show(t); // 快速连续显示多个通知
}
}
_burst() 方法快速连续显示多个通知,演示队列功能。
TopNotice 核心类
1. TopNoticeType 枚举
enum TopNoticeType { success, info, warning, error }
通知类型枚举,定义四种类型:成功、信息、警告、错误。
2. TopNoticeData 数据类
class TopNoticeData {
const TopNoticeData({
required this.title, // 标题(必填)
this.message, // 消息内容(可选)
this.type = TopNoticeType.info, // 类型(默认 info)
this.duration = const Duration(seconds: 4), // 持续时间
this.action, // 操作按钮(可选)
this.onTap, // 点击回调(可选)
this.dismissible = true, // 是否可关闭
this.haptic = true, // 是否触觉反馈
});
final String title;
final String? message;
final TopNoticeType type;
final Duration duration; // Duration.zero 表示不自动关闭
final TopNoticeAction? action;
final VoidCallback? onTap;
final bool dismissible;
final bool haptic;
}
通知数据类,包含所有配置参数。duration 为 Duration.zero 时不自动关闭。
3. TopNoticeAction 操作类
class TopNoticeAction {
const TopNoticeAction({
required this.label, // 按钮文字
required this.onPressed, // 点击回调
this.leading, // 前置图标(可选)
});
final String label;
final VoidCallback onPressed;
final IconData? leading;
}
操作按钮数据类,定义按钮的文字、回调和图标。
4. TopNoticeController 控制器
class TopNoticeController {
TopNoticeController._(this._token, this._dismiss);
final Object _token; // 唯一标识
final void Function(Object token) _dismiss; // 关闭方法
void dismiss() => _dismiss(_token); // 手动关闭通知
}
通知控制器,提供手动关闭通知的能力。通过 token 标识特定的通知。
5. TopNotice 静态类
class TopNotice {
static final _TopNoticeManager _manager = _TopNoticeManager();
static TopNoticeController show(BuildContext context, TopNoticeData data) {
return _manager.enqueue(context, data); // 加入队列并显示
}
static void dismissAll() => _manager.dismissAll(); // 关闭所有通知
}
通知栏静态接口,提供 show() 显示通知和 dismissAll() 关闭所有通知。
_TopNoticeManager 管理器
1. 队列管理
class _TopNoticeManager {
final Queue<_PendingNotice> _queue = ListQueue<_PendingNotice>(); // 待显示队列
OverlayEntry? _entry; // 当前覆盖层入口
_TopNoticeOverlayEntryState? _entryState; // 当前状态
bool _isShowing = false; // 是否正在显示
Object? _currentToken; // 当前通知的 token
TopNoticeController enqueue(BuildContext context, TopNoticeData data) {
final token = Object(); // 生成唯一标识
_queue.add(_PendingNotice(context, data, token)); // 加入队列
_pump(); // 尝试显示
return TopNoticeController._(token, _dismissByToken);
}
管理器使用队列存储待显示的通知。enqueue() 将通知加入队列并尝试显示。
2. 通知显示
void _pump() {
if (_isShowing) return; // 正在显示,等待
if (_queue.isEmpty) return; // 队列为空,返回
final pending = _queue.removeFirst(); // 取出第一个
final overlay = Overlay.maybeOf(pending.context, rootOverlay: true);
if (overlay == null) {
// 覆盖层未准备好,重新加入队列,下一帧再试
_queue.addFirst(pending);
WidgetsBinding.instance.addPostFrameCallback((_) => _pump());
return;
}
_isShowing = true;
_currentToken = pending.token;
_entry = OverlayEntry(
builder: (context) {
return _TopNoticeOverlayEntry(
data: pending.data,
onDismissed: _onDismissed, // 关闭回调
captureState: (s) => _entryState = s, // 保存状态引用
);
},
);
overlay.insert(_entry!); // 插入覆盖层
}
_pump() 方法从队列取出通知并显示。如果覆盖层未准备好,延迟到下一帧再试。
3. 通知关闭
void _onDismissed() {
_entryState = null;
_entry?.remove(); // 移除覆盖层
_entry = null;
_isShowing = false;
_currentToken = null;
_pump(); // 显示下一个
}
void dismissAll() {
_queue.clear(); // 清空队列
_entryState?.dismiss(); // 关闭当前显示的通知
}
void _dismissByToken(Object token) {
// 如果当前显示的通知匹配,关闭它;否则从队列中移除
if (_isShowing && _currentToken == token) {
_entryState?.dismiss();
return;
}
_queue.removeWhere((p) => p.token == token);
}
_onDismissed() 在通知关闭后调用,清理状态并显示下一个。dismissAll() 关闭所有通知,_dismissByToken() 根据 token 关闭特定通知。
_TopNoticeOverlayEntry 覆盖层入口
1. 类定义和动画
class _TopNoticeOverlayEntry extends StatefulWidget {
const _TopNoticeOverlayEntry({
required this.data,
required this.onDismissed,
required this.captureState,
});
final TopNoticeData data;
final VoidCallback onDismissed;
final ValueChanged<_TopNoticeOverlayEntryState> captureState;
State<_TopNoticeOverlayEntry> createState() => _TopNoticeOverlayEntryState();
}
class _TopNoticeOverlayEntryState extends State<_TopNoticeOverlayEntry>
with TickerProviderStateMixin {
late final AnimationController _enter = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 420), // 进入动画时长
reverseDuration: const Duration(milliseconds: 260), // 退出动画时长
);
late final CurvedAnimation _curve = CurvedAnimation(
parent: _enter,
curve: Curves.easeOutCubic, // 进入曲线
reverseCurve: Curves.easeInCubic, // 退出曲线
);
AnimationController? _life; // 生命周期动画(进度条)
Timer? _fallbackTimer; // 备用计时器
// 拖拽相关
double _dragDy = 0; // 拖拽偏移量
bool _dragging = false; // 是否正在拖拽
bool _dismissing = false; // 是否正在关闭
TickerProviderStateMixin 提供多个动画控制器所需的 vsync。_enter 控制进入/退出动画,_life 控制进度条,_dragDy 记录拖拽偏移。
2. 生命周期管理
void initState() {
super.initState();
widget.captureState(this); // 保存状态引用给管理器
_enter.forward(); // 播放进入动画
if (widget.data.haptic) {
HapticFeedback.lightImpact(); // 触觉反馈
}
final d = widget.data.duration;
if (d > Duration.zero) {
_life = AnimationController(vsync: this, duration: d)..forward();
// 进度完成时自动关闭
_life!.addStatusListener((s) {
if (s == AnimationStatus.completed) dismiss();
});
// 备用计时器,避免边缘情况
_fallbackTimer = Timer(d + const Duration(milliseconds: 80), dismiss);
}
}
void dismiss() {
if (_dismissing) return; // 防止重复关闭
_dismissing = true;
_fallbackTimer?.cancel();
_fallbackTimer = null;
_life?.dispose();
_life = null;
_enter.reverse().whenComplete(() {
if (mounted) widget.onDismissed(); // 通知管理器
});
}
initState() 初始化动画和计时器。dismiss() 关闭通知,播放退出动画后通知管理器。
3. 拖拽手势处理
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
widget.data.onTap?.call(); // 点击通知
},
onVerticalDragStart: widget.data.dismissible
? (_) => setState(() {
_dragging = true;
_dragDy = 0;
})
: null,
onVerticalDragUpdate: widget.data.dismissible
? (d) => setState(() {
_dragDy = (_dragDy + d.delta.dy).clamp(-120.0, 48.0); // 限制范围
})
: null,
onVerticalDragEnd: widget.data.dismissible
? (d) {
final v = d.primaryVelocity ?? 0; // 获取速度
final shouldDismiss = _dragDy < -42 || v < -700; // 判断是否关闭
if (shouldDismiss) {
dismiss();
return;
}
setState(() {
_dragging = false;
_dragDy = 0; // 恢复位置
});
}
: null,
拖拽手势处理:onVerticalDragStart 开始拖拽,onVerticalDragUpdate 更新偏移,onVerticalDragEnd 根据偏移和速度决定是否关闭。
4. 动画构建
AnimatedBuilder(
animation: _curve,
builder: (context, child) {
final slideY = Tween<double>(begin: -28, end: 0).transform(_curve.value); // 滑入距离
final fade = _curve.value; // 透明度
return Opacity(
opacity: fade,
child: Transform.translate(
offset: Offset(0, slideY + (_dragging ? _dragDy : 0)), // 滑入 + 拖拽偏移
child: child,
),
);
},
child: TopNoticeCard(...),
)
AnimatedBuilder 监听动画,实现滑入和淡入效果。拖拽时叠加拖拽偏移。
TopNoticeCard 卡片组件
1. 卡片装饰
class TopNoticeCard extends StatelessWidget {
const TopNoticeCard({
super.key,
required this.data,
required this.progress, // 进度动画控制器
required this.onClose, // 关闭回调
});
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final palette = _Palette.fromType(data.type, scheme); // 根据类型获取颜色
final width = MediaQuery.sizeOf(context).width;
final cardWidth = width >= 520 ? 520.0 : width - 24.0; // 响应式宽度
return ConstrainedBox(
constraints: BoxConstraints.tightFor(width: cardWidth),
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(18),
gradient: LinearGradient(
colors: [
palette.borderA.withAlpha(_a(0.95)), // 渐变边框
palette.borderB.withAlpha(_a(0.70)),
palette.borderC.withAlpha(_a(0.85)),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: palette.glow.withAlpha(_a(0.30)), // 发光效果
blurRadius: 26,
spreadRadius: 1,
offset: const Offset(0, 10),
),
BoxShadow(
color: Colors.black.withAlpha(_a(0.18)), // 阴影
blurRadius: 26,
offset: const Offset(0, 14),
),
],
),
child: Padding(
padding: const EdgeInsets.all(1.2), // 边框宽度
child: ClipRRect(
borderRadius: BorderRadius.circular(17),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 18, sigmaY: 18), // 毛玻璃效果
child: DecoratedBox(
decoration: BoxDecoration(
color: palette.surface.withAlpha(_a(0.62)),
gradient: LinearGradient(
colors: [
palette.surface.withAlpha(_a(0.65)),
palette.surface.withAlpha(_a(0.48)),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
卡片使用渐变边框、发光阴影和毛玻璃效果。BackdropFilter 实现毛玻璃,DecoratedBox 实现渐变背景。
2. 内容布局
child: _Content(
data: data,
palette: palette,
progress: progress,
onClose: onClose,
),
内容组件显示标题、消息、进度条、操作按钮和关闭按钮。
使用示例
基本使用
// 显示一个简单的信息通知
TopNotice.show(
context,
const TopNoticeData(
type: TopNoticeType.info,
title: '提示',
message: '这是一条信息通知',
duration: Duration(seconds: 3),
),
);
// 显示成功通知,带操作按钮
TopNotice.show(
context,
TopNoticeData(
type: TopNoticeType.success,
title: '操作成功',
message: '数据已保存',
duration: const Duration(seconds: 4),
action: TopNoticeAction(
label: '查看',
leading: Icons.visibility,
onPressed: () {
// 处理操作
},
),
),
);
// 显示错误通知,不可关闭
TopNotice.show(
context,
const TopNoticeData(
type: TopNoticeType.error,
title: '错误',
message: '操作失败,请重试',
duration: Duration(seconds: 5),
dismissible: false, // 不可手动关闭
),
);
// 手动关闭通知
final controller = TopNotice.show(context, data);
// 稍后关闭
controller.dismiss();
// 关闭所有通知
TopNotice.dismissAll();
使用步骤:
- 调用
TopNotice.show()显示通知 - 传入
TopNoticeData配置通知内容 - 可选:使用返回的
TopNoticeController手动关闭 - 可选:调用
TopNotice.dismissAll()关闭所有通知
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)