Flutter_OpenHarmony_三方库_fluttertoast消息提示适配详解

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net


一、fluttertoast 库简介

fluttertoast 是一个轻量级的消息提示插件,提供两种提示方式:原生 Toast(通过 MethodChannel 调用平台原生能力)和纯 Dart 实现的 FToast(基于 Overlay 的自定义 Toast)。无论是简单的操作反馈还是复杂的自定义提示,fluttertoast 都能满足需求。

📋 fluttertoast 核心特点

特点 说明
原生 Toast 通过 MethodChannel 调用平台原生 Toast 能力
自定义 Toast FToast 基于 Overlay 实现,完全自定义 UI
Toast 队列 自动管理多个 Toast 的显示顺序
位置控制 支持顶部、底部、居中等多种位置
样式定制 支持背景色、文字颜色、字体大小
动画效果 内置淡入淡出动画
跨平台兼容 支持 Android、iOS、Web、OpenHarmony

平台功能支持对比

功能 Android iOS Web OpenHarmony
原生 Toast ✔️ ✔️ ✔️
自定义 FToast ✔️ ✔️ ✔️ ✔️
Toast 队列 ✔️ ✔️ ✔️ ✔️
位置控制 ✔️ ✔️ ✔️ ✔️
样式定制 ✔️ ✔️ ✔️ ✔️
取消 Toast ✔️ ✔️ ✔️ ✔️

使用场景

  • 操作成功/失败反馈
  • 表单验证提示
  • 网络请求状态提示
  • 自定义通知卡片
  • 排队消息提示

二、OpenHarmony 适配版本

2.1 环境说明

组件 版本
Flutter 3.27.5
HarmonyOS 6.0
fluttertoast 8.2.8 (OpenHarmony 适配版本)

2.2 引入方式

pubspec.yaml 文件中添加以下依赖配置:

yaml 复制代码
dependencies:
  flutter:
    sdk: flutter

  # fluttertoast OpenHarmony 适配版本
  fluttertoast:
    git:
      url: https://atomgit.com/openharmony-sig/flutter_fluttertoast.git
      ref: br_8.2.8_ohos

2.3 获取依赖

配置完成后,在项目根目录执行:

bash 复制代码
flutter pub get

三、核心 API 讲解

3.1 Fluttertoast 类

Fluttertoast 提供原生 Toast 提示功能,通过 MethodChannel 与平台侧通信。

showToast 方法
dart 复制代码
static Future<bool?> showToast({
  required String msg,
  Toast? toastLength,
  int timeInSecForIosWeb = 1,
  double? fontSize,
  String? fontAsset,
  ToastGravity? gravity,
  Color? backgroundColor,
  Color? textColor,
  bool webShowClose = false,
  webBgColor = "linear-gradient(to right, #00b09b, #96c93d)",
  webPosition = "right",
})

参数说明:

参数 类型 必填 默认值 说明
msg String - Toast 显示的消息内容
toastLength Toast? Toast.LENGTH_SHORT Toast 显示时长
timeInSecForIosWeb int 1 iOS/Web 平台显示时间(秒)
fontSize double? null 字体大小
fontAsset String? null 自定义字体资源路径
gravity ToastGravity? ToastGravity.BOTTOM Toast 显示位置
backgroundColor Color? Colors.black 背景颜色
textColor Color? Colors.white 文字颜色
webShowClose bool false Web 平台是否显示关闭按钮
webBgColor String 渐变绿色 Web 平台背景样式
webPosition String "right" Web 平台显示位置
cancel 方法
dart 复制代码
static Future<bool?> cancel()

说明: 立即取消当前显示的 Toast。

3.2 Toast 枚举

dart 复制代码
enum Toast {
  LENGTH_SHORT,  // 短提示,约 1 秒
  LENGTH_LONG,   // 长提示,约 5 秒
}

3.3 ToastGravity 枚举

dart 复制代码
enum ToastGravity {
  TOP,           // 顶部
  BOTTOM,        // 底部
  CENTER,        // 居中
  TOP_LEFT,      // 左上
  TOP_RIGHT,     // 右上
  BOTTOM_LEFT,   // 左下
  BOTTOM_RIGHT,  // 右下
  CENTER_LEFT,   // 左中
  CENTER_RIGHT,  // 右中
  SNACKBAR,      // 底部(类似 Snackbar)
  NONE,          // 无位置偏移
}

3.4 FToast 类

FToast 是纯 Dart 实现的自定义 Toast,不依赖平台原生能力,基于 Overlay 实现。

主要方法
dart 复制代码
// 初始化(必须调用)
FToast init(BuildContext context)

// 显示自定义 Toast
void showToast({
  required Widget child,
  PositionedToastBuilder? positionedToastBuilder,
  Duration toastDuration = const Duration(seconds: 2),
  ToastGravity? gravity,
  Duration fadeDuration = const Duration(milliseconds: 350),
  bool ignorePointer = false,
  bool isDismissable = false,
})

// 立即移除当前 Toast
void removeCustomToast()

// 清除队列中所有 Toast
void removeQueuedCustomToasts()

参数说明:

参数 类型 必填 默认值 说明
child Widget - 自定义 Toast 内容
positionedToastBuilder 回调 null 自定义位置构建器
toastDuration Duration 2 秒 显示时长
gravity ToastGravity? null 显示位置
fadeDuration Duration 350 毫秒 淡入淡出动画时长
ignorePointer bool false 是否忽略点击事件
isDismissable bool false 是否可点击关闭
Toast 队列机制

FToast 内部维护一个队列,多次调用 showToast 会依次显示,不会重叠。


四、完整使用示例

dart 复制代码
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';

void main() {
  runApp(const FlutterToastApp());
}

class FlutterToastApp extends StatelessWidget {
  const FlutterToastApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Toast 消息中心',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6C63FF)),
        useMaterial3: true,
      ),
      home: const FlutterToastHomePage(),
      builder: FToastBuilder(),
    );
  }
}

class FlutterToastHomePage extends StatefulWidget {
  const FlutterToastHomePage({super.key});

  @override
  State<FlutterToastHomePage> createState() => _FlutterToastHomePageState();
}

class _FlutterToastHomePageState extends State<FlutterToastHomePage> {
  late FToast _fToast;

  @override
  void initState() {
    super.initState();
    _fToast = FToast();
    _fToast.init(context);
  }

  void _showNativeToast({
    required String msg,
    Toast length = Toast.LENGTH_SHORT,
    ToastGravity gravity = ToastGravity.BOTTOM,
    Color bgColor = Colors.black,
    Color textColor = Colors.white,
  }) {
    Fluttertoast.showToast(
      msg: msg,
      toastLength: length,
      gravity: gravity,
      backgroundColor: bgColor,
      textColor: textColor,
      fontSize: 16.0,
    );
  }

  void _showCustomToast({
    required String title,
    required String message,
    required IconData icon,
    required Color color,
    ToastGravity gravity = ToastGravity.BOTTOM,
  }) {
    _fToast.showToast(
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(12),
          color: Colors.white,
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(0.1),
              blurRadius: 8,
              offset: const Offset(0, 2),
            ),
          ],
        ),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              padding: const EdgeInsets.all(8),
              decoration: BoxDecoration(
                color: color.withOpacity(0.1),
                borderRadius: BorderRadius.circular(8),
              ),
              child: Icon(icon, color: color),
            ),
            const SizedBox(width: 12),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(
                  title,
                  style: const TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 14,
                  ),
                ),
                const SizedBox(height: 2),
                Text(
                  message,
                  style: TextStyle(
                    fontSize: 12,
                    color: Colors.grey[600],
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
      gravity: gravity,
      toastDuration: const Duration(seconds: 3),
      isDismissable: true,
    );
  }

  void _showQueuedToasts() {
    _fToast.removeQueuedCustomToasts();
    
    final items = [
      {'title': '步骤 1', 'msg': '正在初始化...', 'icon': Icons.settings, 'color': Colors.blue},
      {'title': '步骤 2', 'msg': '正在加载数据...', 'icon': Icons.download, 'color': Colors.orange},
      {'title': '步骤 3', 'msg': '正在处理...', 'icon': Icons.analytics, 'color': Colors.purple},
      {'title': '完成', 'msg': '所有操作已完成', 'icon': Icons.check_circle, 'color': Colors.green},
    ];

    for (final item in items) {
      _fToast.showToast(
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(12),
            color: Colors.white,
            boxShadow: [
              BoxShadow(
                color: Colors.black.withOpacity(0.1),
                blurRadius: 8,
                offset: const Offset(0, 2),
              ),
            ],
          ),
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              Icon(item['icon'] as IconData, color: item['color'] as Color),
              const SizedBox(width: 12),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    item['title'] as String,
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                  Text(
                    item['msg'] as String,
                    style: TextStyle(fontSize: 12, color: Colors.grey[600]),
                  ),
                ],
              ),
            ],
          ),
        ),
        gravity: ToastGravity.CENTER,
        toastDuration: const Duration(seconds: 2),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Toast 消息中心'),
        backgroundColor: const Color(0xFF6C63FF),
        foregroundColor: Colors.white,
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildSectionTitle('原生 Toast'),
          _buildActionCard(
            icon: Icons.check_circle,
            title: '成功提示',
            subtitle: '操作成功,短提示',
            color: const Color(0xFF4CAF50),
            onTap: () => _showNativeToast(
              msg: '操作成功!',
              length: Toast.LENGTH_SHORT,
              bgColor: const Color(0xFF4CAF50),
            ),
          ),
          _buildActionCard(
            icon: Icons.error,
            title: '错误提示',
            subtitle: '操作失败,长提示',
            color: const Color(0xFFF44336),
            onTap: () => _showNativeToast(
              msg: '操作失败,请重试!',
              length: Toast.LENGTH_LONG,
              bgColor: const Color(0xFFF44336),
            ),
          ),
          _buildActionCard(
            icon: Icons.info,
            title: '居中提示',
            subtitle: '消息居中显示',
            color: const Color(0xFF2196F3),
            onTap: () => _showNativeToast(
              msg: '这是一条居中的消息',
              gravity: ToastGravity.CENTER,
              bgColor: const Color(0xFF2196F3),
            ),
          ),
          _buildActionCard(
            icon: Icons.warning,
            title: '顶部提示',
            subtitle: '消息顶部显示',
            color: const Color(0xFFFF9800),
            onTap: () => _showNativeToast(
              msg: '请注意查看此消息',
              gravity: ToastGravity.TOP,
              bgColor: const Color(0xFFFF9800),
            ),
          ),
          
          const SizedBox(height: 24),
          _buildSectionTitle('自定义 FToast'),
          _buildActionCard(
            icon: Icons.notifications,
            title: '通知样式',
            subtitle: '自定义卡片样式 Toast',
            color: const Color(0xFF6C63FF),
            onTap: () => _showCustomToast(
              title: '新消息',
              message: '您有一条新的系统通知',
              icon: Icons.notifications,
              color: const Color(0xFF6C63FF),
            ),
          ),
          _buildActionCard(
            icon: Icons.security,
            title: '安全提示',
            subtitle: '带图标的自定义 Toast',
            color: const Color(0xFF00BCD4),
            onTap: () => _showCustomToast(
              title: '安全提醒',
              message: '请妥善保管您的账号信息',
              icon: Icons.security,
              color: const Color(0xFF00BCD4),
              gravity: ToastGravity.TOP,
            ),
          ),
          
          const SizedBox(height: 24),
          _buildSectionTitle('Toast 队列'),
          _buildActionCard(
            icon: Icons.queue_play_next,
            title: '队列演示',
            subtitle: '依次显示 4 条消息',
            color: const Color(0xFF9C27B0),
            onTap: _showQueuedToasts,
          ),
          _buildActionCard(
            icon: Icons.clear_all,
            title: '清除队列',
            subtitle: '移除所有待显示的 Toast',
            color: const Color(0xFF795548),
            onTap: () {
              _fToast.removeQueuedCustomToasts();
              _showNativeToast(
                msg: '已清除队列',
                bgColor: const Color(0xFF795548),
              );
            },
          ),
          
          const SizedBox(height: 32),
        ],
      ),
    );
  }

  Widget _buildSectionTitle(String title) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: Text(
        title,
        style: const TextStyle(
          fontSize: 18,
          fontWeight: FontWeight.bold,
          color: Color(0xFF6C63FF),
        ),
      ),
    );
  }

  Widget _buildActionCard({
    required IconData icon,
    required String title,
    required String subtitle,
    required Color color,
    required VoidCallback onTap,
  }) {
    return Card(
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: color.withOpacity(0.1),
          child: Icon(icon, color: color),
        ),
        title: Text(title),
        subtitle: Text(subtitle),
        trailing: const Icon(Icons.chevron_right),
        onTap: onTap,
      ),
    );
  }
}

五、适配要点

5.1 OpenHarmony 平台特性

  1. 原生 Toast

    • OpenHarmony 平台通过 MethodChannel 实现原生 Toast
    • 支持基本的消息显示、位置控制、样式定制
    • toastLength 控制显示时长
  2. FToast 自定义 Toast

    • FToast 是纯 Dart 实现,不依赖平台原生能力
    • 基于 Overlay 实现,所有平台行为一致
    • 支持完全自定义 Widget 作为 Toast 内容
  3. Toast 队列

    • FToast 内部维护队列,多次调用会依次显示
    • 使用 removeQueuedCustomToasts() 可清除队列
    • 适合需要顺序展示多个提示的场景
  4. MaterialApp 配置

    • 使用 FToast 需要在 MaterialApp 的 builder 中添加 FToastBuilder()
    • 否则可能无法正确获取 Overlay

5.2 与 Android/iOS 的差异

差异点 Android/iOS OpenHarmony
原生 Toast 完整支持 支持
自定义字体 支持 支持
FToast 完整支持 完整支持
Toast 队列 完整支持 完整支持
Web 特性 支持渐变背景等 不适用

5.3 注意事项

  1. FToast 初始化

    • 必须在 initState 中调用 _fToast.init(context)
    • MaterialApp 需要配置 builder: FToastBuilder()
  2. Context 问题

    • FToast 需要有效的 BuildContext
    • 避免使用 Navigator 或 MaterialApp 的顶层 context
  3. 队列管理

    • 多次调用 showToast 会加入队列依次显示
    • 使用 removeCustomToast() 可立即隐藏当前 Toast
    • 使用 removeQueuedCustomToasts() 可清除所有待显示 Toast
  4. 原生 Toast 限制

    • 原生 Toast 不支持自定义 Widget
    • 只能显示文本,样式有限
    • 需要自定义样式时使用 FToast

六、常见问题

Q1: FToast 提示 Context is null?

原因: 未调用 init(context) 或 context 无效。

解决方案:

dart 复制代码
@override
void initState() {
  super.initState();
  _fToast = FToast();
  _fToast.init(context);
}

Q2: FToast 提示 Overlay is null?

原因: MaterialApp 未配置 builder 或使用了顶层 context。

解决方案:

dart 复制代码
MaterialApp(
  builder: FToastBuilder(),
  home: const MyHomePage(),
)

Q3: 如何立即隐藏 Toast?

解决方案:

dart 复制代码
// 隐藏原生 Toast
Fluttertoast.cancel();

// 隐藏自定义 Toast
_fToast.removeCustomToast();

Q4: 如何让 Toast 可点击关闭?

解决方案:

dart 复制代码
_fToast.showToast(
  child: YourCustomWidget(),
  isDismissable: true,  // 允许点击关闭
  toastDuration: const Duration(seconds: 3),
);

Q5: 如何自定义 Toast 位置?

解决方案:

dart 复制代码
// 使用 gravity 参数
_fToast.showToast(
  child: YourCustomWidget(),
  gravity: ToastGravity.TOP,  // 或 BOTTOM, CENTER 等
);

// 使用 positionedToastBuilder 完全自定义
_fToast.showToast(
  child: YourCustomWidget(),
  positionedToastBuilder: (context, child) {
    return Positioned(
      top: 200,
      left: 50,
      child: child,
    );
  },
);

Q6: 原生 Toast 和 FToast 如何选择?

解决方案:

  • 原生 Toast:简单文本提示,不需要自定义样式
  • FToast:需要自定义样式、图标、队列管理
dart 复制代码
// 简单提示用原生
Fluttertoast.showToast(msg: '操作成功');

// 复杂提示用 FToast
_fToast.showToast(
  child: CustomToastWidget(),
  isDismissable: true,
);

七、总结

fluttertoast 是一个功能丰富的消息提示库,在 OpenHarmony 平台的适配已经非常成熟。通过本文的介绍,你应该已经掌握了:

  1. Fluttertoast 原生 Toast 的使用方法和参数配置
  2. FToast 自定义 Toast 的初始化和显示流程
  3. Toast 队列机制和管理方法
  4. 完整的应用级别消息提示中心实现
  5. OpenHarmony 平台的适配要点和注意事项
  6. 常见问题和解决方案

在实际开发中,建议根据具体需求选择合适的 Toast 类型。简单的文本提示使用原生 Toast,需要自定义样式或队列管理时使用 FToast。同时注意正确初始化 FToast 并配置 MaterialApp 的 builder。

💡 提示: 更多 OpenHarmony 适配的 Flutter 三方库信息,请访问 开源鸿蒙跨平台开发者社区 获取最新资源和技术支持。

相关推荐
seabirdssss2 小时前
Flutter 开发环境配置
android·windows·flutter·adb
2601_949593653 小时前
Flutter_OpenHarmony_三方库_webview_flutter网页内容嵌入与交互适配详解
flutter·harmonyos
tangweiguo030519873 小时前
Flutter 分页缓存实战:基于 Riverpod 的 SWR 策略实现
flutter
Ww.xh5 小时前
鸿蒙Flutter混合开发实战:跨平台UI无缝集成
flutter·华为·harmonyos
SoulRed5 小时前
Android Studio 调试flutter gradle的问题
android·flutter·android studio
blanks20205 小时前
为 Zed 编辑器 添加 flutter dart snippets
前端·flutter
blanks20205 小时前
使用 zed 和 使用 vscode 开发 flutter
flutter
2601_949593656 小时前
Flutter_OpenHarmony_三方库_file_selector文件选择适配详解
flutter
陆业聪6 小时前
跨端框架横评 2026:Flutter、React Native、KMP、Kuikly、小程序,谁是你下一个项目的正确答案?
flutter·大前端·跨端开发