Flutter 鸿蒙实战:用 fluttertoast 三方库给应用配一套不打断操作的轻提示
欢迎加入 CPF-Flutter 鸿蒙社区: CPF-Flutter - 开源代码托管,代码协作 - AtomGit
保存按钮点下去了,存没存上心里没底,不少应用的做法是弹个对话框让用户再点一次确定。其实多数小确认用一条 Toast 就够了:屏幕上浮出来一条,两三秒自己消失,不抢焦点,也不用伸手去关。要在鸿蒙应用里发这种轻提示,可以接一个现成的 Toast 库。
这次选了 fluttertoast,做了一个叫 ToastBox 的小 Demo,界面是一个提示发射台:上半部分调文案、时长、位置、背景色和字号,中间一排按钮分别触发系统 Toast、取消显示、自定义样式和排队测试,底部把最近五次发射的参数记下来。表单提交成功、已复制这类不打断操作的小反馈,都可以照这个方式接。
ToastBox 的完整源码已经放到 AtomGit,文章里的发射台和测试代码都在这里:
项目仓库: https://atomgit.com/gcw_uKGkQkkd/toastbox
提示功能用的是 GitCode 上 CPF-Flutter 维护的鸿蒙版本仓库 flutter_fluttertoast(tag 9.0.0-ohos-1.0.0),包就在仓库根目录,lib 里是 Dart 实现,ohos 里是 ArkTS 实现。接入代码不多,这次花时间的地方有两处:一是确认这个库是单包结构,不需要配平台接口包;二是对照源码把每个参数在鸿蒙端的实际表现核了一遍,有几个参数的行为跟文档直觉不一致。下面把实际跑通的配置和过程记下来。
环境和设备
还没搭好 Flutter 鸿蒙环境的话,可以先看《Flutter OHOS 开发环境搭建指南》,链接见文末。
我这边用的环境如下,测试日期9月12号。
| 项目 | 版本 |
|---|---|
| Flutter | 3.35.8-ohos-1.0.3,channel [user-branch] |
| Dart | 3.9.2 |
| DevEco Studio | 26.0.0 |
| 编译 SDK | OpenHarmony SDK 26.0.0 |
| 验证方式 | Pura 90 Pro 7.0.0(26.0.0) |
先把依赖选对
这次用的是 fluttertoast 9.0.0,固定到提交 66d587d5114c18cc5b8cdb23699d76dc812da393,对应 tag 9.0.0-ohos-1.0.0。下面的配置可以直接放进应用的 pubspec.yaml:
dependencies:
flutter:
sdk: flutter
fluttertoast:
git:
url: https://gitcode.com/CPF-Flutter/flutter_fluttertoast.git
ref: 66d587d5114c18cc5b8cdb23699d76dc812da393
执行:
flutter pub get
fluttertoast 的依赖配置有三个容易疑惑的地方。
一是 path 不用写。包就在仓库根目录,lib、ohos 都在第一层,写 url 加 ref 就行。从聚合仓库(包在多层子目录里)搬过来的配置如果带着 path,反而指不到位置。
二是没有平台接口包要配。有些插件分主包和 *_platform_interface 两个包,鸿蒙接入时得用 dependency_overrides 把两边指到同一个提交。fluttertoast 是单包结构,Dart 层和 ArkTS 层都在这一个包里,加 dependency_overrides 属于画蛇添足。
三是核对拉到的确实是鸿蒙版。两步就够:看 pubspec.lock 里 fluttertoast 的 resolved-ref 是否等于 ref 里的提交号;再看它的 pubspec.yaml,platforms 下要有 ohos 声明。这个包的 SDK 约束很宽(sdk >=2.12.0 <4.0.0),本机 3.35.8-ohos 环境直接可用,flutter pub get 一次通过。
接口分两套:走原生的和走 Overlay 的
fluttertoast 对外是两组 API。Fluttertoast 类的静态方法走 MethodChannel(通道名 PonnamKarthik/fluttertoast),由鸿蒙端的原生代码弹系统级 Toast;FToast 类则完全在 Dart 层用 Overlay 画,不碰原生,样式随便定。按使用顺序来说。
Dart 端先导入:
import 'package:fluttertoast/fluttertoast.dart';
接口一:发一条系统 Toast,调用 Fluttertoast.showToast():
final bool? ok = await Fluttertoast.showToast(
msg: '草稿已保存', // 文案,必填
toastLength: Toast.LENGTH_SHORT, // 短时长
gravity: ToastGravity.BOTTOM, // 底部
backgroundColor: const Color(0xE6263238), // 背景色
textColor: Colors.white, // 文字色
fontSize: 16.0, // 字号
);
返回 Future<bool?>,参数被原生端拒绝时不会抛异常,Dart 层把错误接住后返回 null,所以调用方判断返回值就能知道有没有发出去。鸿蒙端底层走的是 ArkUI 的 promptAction.showToast,时长分两档:短 2000 毫秒、长 3500 毫秒,gravity 为底部时离屏幕下缘 50vp。
各参数在鸿蒙端的实际表现,对着源码核过一遍:
| 参数 | 说明 | 鸿蒙端表现 |
|---|---|---|
msg | 提示文案,必填 | 原样显示,空文案也放行 |
toastLength | LENGTH_SHORT / LENGTH_LONG | 短约 2 秒,长约 3.5 秒 |
gravity | TOP / CENTER / BOTTOM | 只认这三个,其余取值都按底部处理 |
backgroundColor / textColor | 背景、文字颜色 | 不传默认黑底白字 |
fontSize | 字号 | 只接受 0~100,超出报 INVALID_PARAM |
timeInSecForIosWeb | 秒数 | 只对 iOS/Web 生效,鸿蒙端忽略 |
设置好参数发射一条,文案、颜色、字号跟发射台上配的一致:

接口二:把正在显示的 Toast 立刻撤掉,调用 Fluttertoast.cancel():
final bool? ok = await Fluttertoast.cancel();
返回 Future<bool?>。原生端收到调用就把当前 Toast 的窗口、定时器一起清掉。适合"用户已经在下一步操作了,上一条提示还在杵着"的场景。
接口三:查现在有没有 Toast 在显示,读静态属性 Fluttertoast.isCurrentlyShowingToast:
final bool showing = Fluttertoast.isCurrentlyShowingToast;
它是 bool,不是 Future,拿来当界面状态或者调试参考很方便。但要知道它的局限:Dart 层在每次发射后把它置 true,按 timeInSecForIosWeb 的秒数(默认 1 秒)延迟复位,跟鸿蒙端实际显示的 2 秒或 3.5 秒对不上。拿它做严格业务判断前先想清楚这一点。
接口四:发自定义样式的 Toast,用 FToast。先在页面初始化时把上下文交给它,再随时发射:
final FToast _fToast = FToast(); // 内部是单例,整个应用共用一份队列
@override
void initState() {
super.initState();
_fToast.init(context); // 把页面 context 交给它,一次就够
}
void _fire() {
_fToast.showToast(
child: _customToastCard('同步完成'), // child 随便拼,Container 也行
gravity: ToastGravity.BOTTOM,
toastDuration: const Duration(seconds: 2), // 停留时长
fadeDuration: const Duration(milliseconds: 250), // 淡出时长
);
}
showToast() 的参数里有两个值得注意:isDismissible 设为 true 时点一下 Toast 就能提前关掉;gravity 传 BOTTOM 时如果键盘正开着,库会自动改成从中间弹,避免被键盘挡住。另外它带一个队列,连发三条不会叠在一起,前一条消失后下一条才上。如果想在全局用,把 FToastBuilder() 塞进 MaterialApp 的 builder 里,任何页面拿 context 都能初始化。
接口五:撤掉当前这条或清空整个队列,两个方法配合用:
_fToast.removeCustomToast(); // 只撤当前这条,队列里的接着放
_fToast.removeQueuedCustomToasts(); // 正在显示的和排队的全部清掉
接到发射台上
ToastBox 就一个主页。上半部分是参数区:文案输入框、时长两档选择、位置三选一(顶部、居中、底部)、四种背景色、字号滑杆,下面放一个预览框,系统 Toast 弹出来大概什么样子,调参数时直接能看见。中间是六个触发按钮:系统 Toast 的发射和取消、自定义 Toast 的发射、连发三条测排队、撤当前、清队列。底部是发射记录区,每条记下走的哪套接口、文案、参数摘要和时间,最多留五条。
页面里和库打交道的代码就两段。初始化在 initState:
@override
void initState() {
super.initState();
_fToast.init(context); // FToast 是单例,init 一次就够了
}
发射系统 Toast 在按钮回调里,参数全部来自发射台上的当前状态:
Future<void> _fireSystemToast() async {
final String text = _msgController.text;
await Fluttertoast.showToast(
msg: text,
toastLength: _longToast ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT,
gravity: _gravity,
backgroundColor: _bgColor,
textColor: Colors.white,
fontSize: _fontSize,
);
final bool showing = Fluttertoast.isCurrentlyShowingToast; // 读一下标志位记进卡片
_record('系统 Toast', text, '...参数摘要...');
}
这是点"发一条自定义"后的样子,蓝底圆角带图标的卡片,跟系统 Toast 的样式完全不同:

关键文件就一个:lib/main.dart,页面、参数状态和六个按钮的回调全在里面。如果要搬进多页面应用,FToast 是单例,init 做一次就行;系统 Toast 走静态方法,连实例都不用建,哪个页面直接调。
下载源码,签名后装到手机
可以直接把 ToastBox 拉下来运行,依赖已经写进工程:
git clone https://atomgit.com/gcw_uKGkQkkd/toastbox.git
cd toastbox
flutter pub get
然后在 DevEco Studio 里完成调试签名:
-
用 DevEco Studio 打开
ohos目录。 -
进入 File → Project Structure → Signing Configs。
-
勾选 Automatically generate signature,给当前工程和设备生成签名,点 Apply/OK。
签名完成后构建、安装:
flutter build hap --release
hdc list targets
hdc -t <设备ID> install -r build/ohos/hap/entry-default-signed.hap
hdc -t <设备ID> shell aa start -a EntryAbility -b com.example.toastbox
平时调页面需要热重载,可用 flutter run -d <设备ID>。
手机上实际试了什么
本机静态检查与测试结果:
-
flutter analyze:通过,无问题。 -
flutter test:冒烟测试通过(验证提示发射台页面能正常创建并渲染标题"ToastBox 提示发射台")。 -
flutter build hap --debug --no-codesign:构建成功,产物build/ohos/hap/entry-default-unsigned.hap。
模拟器上逐项操作后的结果如下:
| 操作 | 结果 |
|---|---|
| 输入文案,点"发射一条" | 屏幕底部浮出系统 Toast,颜色字号随发射台设置,约 2 秒自动消失 |
| 时长选"长"再发射 | Toast 停留约 3.5 秒 |
| 位置选"顶部"或"居中" | Toast 分别贴顶或居中显示 |
| Toast 还在显示时点"立刻撤掉" | Toast 立即消失,记录区显示"原生端已执行 cancel" |
| 点"发一条自定义" | 蓝底圆角卡片带图标弹出,停留 2 秒(选长则 4 秒)后淡出 |
| 点"连发三条测排队" | 三条依次显示,前一条消失后下一条才上,不叠放 |
| 排队未放完时点"清空队列" | 正在显示的和排队的全部消失,记录区留痕 |


FAQ:接入时遇到的问题
传了 ToastGravity.BOTTOM_LEFT,怎么还是在底部正中
Dart 层只把 TOP 和 CENTER 翻译成原生的 top、center,其余取值一律归到 bottom;鸿蒙原生端也只认这三个位置。想要九宫格式的精确落位,别用系统 Toast,改用 FToast.showToast 的 gravity,它在 Dart 层自己摆位置,九个方位都支持。
timeInSecForIosWeb 填了 5,鸿蒙上怎么还是 2 秒
这个参数名字里就写明了,只管 iOS 和 Web。鸿蒙端的显示时长由 toastLength 决定,短 2000 毫秒、长 3500 毫秒,写死在原生实现里。想让提示停更久,选 Toast.LENGTH_LONG,或者换 FToast 自己定 toastDuration。
fontSize 传了个大数字,直接返回 null
原生端对字号做了校验,只接受 0 到 100 之间的值,超出范围回 INVALID_PARAM,Dart 层接住后 showToast 返回 null。滑动条或输入框给用户开放字号设置时,调用前先夹一下范围。
isCurrentlyShowingToast 变回 false 的时机不对
Dart 层是按 timeInSecForIosWeb(默认 1 秒)延迟复位的,而鸿蒙端实际显示 2 秒或 3.5 秒,所以会出现"标志位已经 false、Toast 还在屏上"的窗口期。它适合做调试参考,做"提示是否还在"的精确判断时,自己起一个等长的计时器更可靠。
pub get 拉下来的版本对不上,或报 SDK 约束不满足
先看 pubspec.yaml 里的 ref 是不是写全了提交号 66d587d…(9.0.0-ohos-1.0.0)。这个仓库还有一个更早的 8.2.8-ohos-1.0.0-beta.1(提交 54a9cca…),是给旧 SDK 用的,新环境别混用。改完重新 flutter pub get,再到 pubspec.lock 里核对 resolved-ref,确认和 ref 一致。
发现库的问题,怎么反馈
1. 提交 Issue,说明问题和复现步骤
从文末链接进入 flutter_fluttertoast 仓库,打开 Issues 页面,先搜索有没有相同的问题。如果没有,点击 新建 Issue,填写问题标题和复现信息。

报告时把 pubspec.yaml 的依赖配置和 pubspec.lock 里实际解析的 resolved-ref 贴出来,说明预期什么、实际报什么错,以及 Flutter 版本、设备系统。
2. Fork 项目,在自己的仓库中修改
如果问题能自己修,点击仓库右上角的 Fork。在 Fork 页面确认项目名称、自己的账号和要复制的分支,再点击创建。

Fork 完成后,把自己的仓库克隆到本地,并添加上游仓库。下面以给 README 的鸿蒙依赖说明补一条版本备注为例,<你的账号> 换成自己的 GitCode 账号:
git clone https://gitcode.com/<你的账号>/flutter_fluttertoast.git
cd flutter_fluttertoast
git remote add upstream https://gitcode.com/CPF-Flutter/flutter_fluttertoast.git
git fetch upstream
git switch -c docs/ohos-deps-note 66d587d5114c18cc5b8cdb23699d76dc812da393
改好后检查差异并推到自己的 Fork:
git diff --check
git add [想提交的文件]
git commit -m "docs: note ohos dependency pinning in README"
git push -u origin docs/ohos-deps-note
3. 创建 PR,把修改提交给上游
推送后回到自己 Fork 的仓库页面,在 Pull Requests 里新建 PR:源仓库选自己的 Fork、源分支选刚推的 docs/ohos-deps-note,目标仓库选 CPF-Flutter/flutter_fluttertoast 上游,目标分支按维护者要求选。说明里写清原来哪里不对、改了什么、用哪个版本验证过;有对应 Issue 就关联上,确认差异后提交。

相关链接
更多推荐



所有评论(0)