Flutter桌面应用程序定义系统托盘Tray

文章目录

概念

系统托盘 :系统托盘是一种用户界面元素,通常出现在操作系统的任务栏或桌面顶部。它是一个水平的狭长区域,用于显示各种图标和通知,以提供快速访问和操作特定应用程序或系统功能。系统托盘通常包含操作系统或第三方应用程序的图标,这些图标可以显示有关应用程序状态、提醒和通知等信息。用户可以通过单击这些图标来打开应用程序的主窗口、执行特定功能或查看详细信息。系统托盘的设计旨在提供一种方便的方式来管理和访问常用的应用程序和系统功能,以提高用户的工作效率。
效果展示

作为现代操作系统中常见的一个组件,系统托盘能够让用户方便地访问常用的应用程序或者系统功能。对于Flutter桌面应用程序开发者来说,如何在应用程序中定义系统托盘是一个值得探讨的问题。本文将简介系统托盘的概念,并介绍两种可用的Flutter桌面应用程序系统托盘方案。

实现方案

1. tray_manager

依赖库

tray_manager

支持平台

Windows, macOS & Linux

实现步骤

  1. 在pubspec.yaml中添加依赖
dart 复制代码
dependencies:
  ...
  tray_manager: ^0.2.0
  1. 导入依赖
dart 复制代码
import 'package:flutter/material.dart' hide MenuItem;
import 'package:tray_manager/tray_manager.dart';
  1. 配置系统托盘特性
dart 复制代码
Future<void> _init() async {
  //设置系统托盘图标,Windows图标必须文件后缀必须是.ico
  await trayManager.setIcon(
    Platform.isWindows
        ? 'assets/images/tray_icon_original.ico'
        : 'assets/images/img_1.png',
  );
  //设置系统托盘的标题
  trayManager.setTitle("system tray");
  //设置系统托盘的标题
  trayManager.setToolTip("How to use system tray with Flutter:鼠标滑过提示");
  //设置系统托盘的菜单
  Menu menu = Menu(
    items: [
      //设置系统托盘的子菜单
      MenuItem.submenu(
          // key 
          key: 'window_settings',
          label: '窗口设置',
          //trayManager 不支持菜单项添加图标,该配置无效
          icon: Platform.isWindows
              ? 'assets/images/app_icon.bmp'
              : 'assets/images/img_1.png',
          submenu: Menu(items: [
            MenuItem.checkbox(
              checked: true,
              label: "毛玻璃效果",
              onClick: (MenuItem menuItem) {
                menuItem.checked = !(menuItem.checked == true);
                if (kDebugMode) {
                  print("毛玻璃效果 onClick ${menuItem.checked}");
                }
                
              },
            ),
            MenuItem.checkbox(
              checked: true,
              label: "窗口置顶",
              onClick: (MenuItem menuItem) {
                menuItem.checked = !(menuItem.checked == true);
                if (kDebugMode) {
                  print("窗口置顶 onClick ${menuItem.checked}");
                }
              
              },
            ),
            MenuItem.checkbox(
              checked: true,
              label: "自启动",
              onClick: (MenuItem menuItem) {
                menuItem.checked = !(menuItem.checked == true);
                if (kDebugMode) {
                  print("自启动 onClick ${menuItem.checked}");
                }
              },
            ),
            //可选类型的菜单栏
            MenuItem.checkbox(
              checked: true,
              label: "图标闪烁",
              onClick: (MenuItem menuItem) {
                menuItem.checked = !(menuItem.checked == true);
                if (kDebugMode) {
                  print("图标闪烁 onClick ${menuItem.checked}");
                }
              },
            ),
          ])),
      //分割线
      MenuItem.separator(),
      MenuItem(
          key: 'open_app',
          label: 'Open App',
          onClick: (MenuItem menuItem) {

          }),
      MenuItem(
          key: 'exit_app',
          label: 'Exit App',
          onClick: (MenuItem menuItem) {

          }),
    ],
  );
  if (kDebugMode) {
    print("menu:${menu.toJson()}");
  }
  //为系统托盘配置菜单
  await trayManager.setContextMenu(menu);
}
  1. 监听TrayListener
dart 复制代码
import 'package:flutter/material.dart';
import 'package:tray_manager/tray_manager.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with TrayListener {
  @override
  void initState() {
    trayManager.addListener(this);
    super.initState();
    _init();
  }

  @override
  void dispose() {
    trayManager.removeListener(this);
    super.dispose();
  }

  void _init() {
    // ...
  }

  @override
  Widget build(BuildContext context) {
    // ...
  }
//未触发该事件
 @override
void onTrayIconRightMouseUp() {
  // TODO: implement onTrayIconRightMouseUp
  super.onTrayIconRightMouseUp();
  if (kDebugMode) {
    print("onTrayIconMouseUp");
  }
}

//未触发该事件
@override
void onTrayIconMouseUp() {
  super.onTrayIconMouseUp();
  if (kDebugMode) {
    print("onTrayIconMouseUp");
  }
}
@override
void onTrayIconMouseDown() {
  if (kDebugMode) {
    print("onTrayIconMouseDown");
  }
  //弹出托盘的菜单栏
  trayManager.popUpContextMenu();
}

@override
void onTrayIconRightMouseDown() {
  if (kDebugMode) {
    print("onTrayIconRightMouseDown");
  }
  //弹出托盘的菜单栏
  trayManager.popUpContextMenu();
}
 //弹出托盘的菜单栏点击事件
@override
void onTrayMenuItemClick(MenuItem menuItem) {
  if (kDebugMode) {
    print("menuItem:${menuItem.key}-${menuItem.label}");
  }
}
}

2. system_tray

依赖库

system_tray

支持平台

Windows, macOS & Linux

实现步骤

  1. 在pubspec.yaml中添加依赖
dart 复制代码
dependencies:
  ...
  system_tray: ^2.0.3
  1. 导入依赖
dart 复制代码
import 'package:system_tray/system_tray.dart';
  1. 配置系统托盘特性
dart 复制代码
//创建SystemTray 对象
final SystemTray _systemTray = SystemTray();
Timer? _timer;

//配置系统托盘
Future<void> _initSystemTray() async {
  //设置系统托盘的图标,必须是.ico后缀的图片
  await _systemTray.initSystemTray(
    iconPath: Platform.isWindows
        ? 'assets/images/tray_icon_original.ico'
        : 'assets/images/img_1.png',
  );
  //设置系统托盘的标题
  _systemTray.setTitle("system tray");
  //设置系统托盘的提示
  _systemTray.setToolTip("How to use system tray with Flutter");
  //注册系统托盘事件
  _systemTray.registerSystemTrayEventHandler((eventName) {
    debugPrint("eventName: $eventName");
    //注册系统托盘事件:点击事件
    if (eventName == kSystemTrayEventClick) {
      //Windows系统:显示主窗口   其他系统弹出托盘菜单弹框
      Platform.isWindows
          ? windowManager.show()
          : _systemTray.popUpContextMenu();
      //注册系统托盘事件:鼠键右键
    } else if (eventName == kSystemTrayEventRightClick) {
      //Windows系统:弹出托盘菜单弹框  其他系统: 显示主窗口
      Platform.isWindows
          ? _systemTray.popUpContextMenu()
          : windowManager.show();
    }
  });
  //创建托盘的菜单
  final Menu _menuMain = Menu();
  await _menuMain.buildFrom([
    //创建子菜单
    SubMenu(
        label: "窗口设置",
        //创建为菜单子项添加图标,格式必须是bmp
        image: Platform.isWindows
            ? 'assets/images/app_icon.bmp'
            : 'assets/images/img_1.png',
        children: [
          //创建可选框类型的菜单项
          MenuItemCheckbox(
              label: "毛玻璃效果",
              checked: true,
              name: 'acrylic_cb',
              onClicked: (MenuItemBase menuItem) async {
                //更新MenuItemCheckbox的状态
                await menuItem.setCheck(!menuItem.checked);
                if (kDebugMode) {
                  print("毛玻璃效果 onClick ${menuItem.checked}");
                }
                if (menuItem.checked == true) {
                  showAcrylic(color);
                } else {
                  closeAcrylic();
                }
              }),
          MenuItemCheckbox(
              label: "窗口置顶",
              checked: true,
              onClicked: (MenuItemBase menuItem) async {
                await menuItem.setCheck(!menuItem.checked);
                if (kDebugMode) {
                  print("窗口置顶 onClick ${menuItem.checked}");
                }
                if (menuItem.checked == true) {
                  windowManager.setAlwaysOnTop(true);
                } else {
                  windowManager.setAlwaysOnTop(false);
                }
              }),
          MenuItemCheckbox(
              label: "自启动",
              checked: true,
              name: 'auto_start_cb',
              onClicked: (MenuItemBase menuItem) async {
                // menuItem.checked = !(menuItem.checked == true);
                await menuItem.setCheck(!menuItem.checked);
                if (kDebugMode) {
                  print("自启动 onClick ${menuItem.checked}");
                }
              }),
          MenuItemCheckbox(
              label: "图标闪烁",
              checked: true,
              name: "flash_cb",
              onClicked: (MenuItemBase menuItem) async {
                MenuItemCheckbox? flashCb =
                    _menuMain.findItemByName<MenuItemCheckbox>("flash_cb");
                await flashCb?.setCheck(!menuItem.checked);
                if (kDebugMode) {
                  print("图标闪烁 onClick ${menuItem.checked}");
                }
                if (menuItem.checked) {
                  startFlashIcon();
                } else {
                  stopFlashIcon();
                }
              }),
        ]),
    //菜单分割线
    MenuSeparator(),
    //菜单项
    MenuItemLabel(
        label: 'Open App',
        image: Platform.isWindows
            ? 'assets/images/app_icon.bmp'
            : 'assets/images/img_1.png',
        onClicked: (MenuItemBase menuItem) {
          windowManager.show();
        }),
    MenuItemLabel(
        label: 'Exit App',
        image: Platform.isWindows
            ? 'assets/images/app_icon.bmp'
            : 'assets/images/img_1.png',
        onClicked: (MenuItemBase menuItem) {
          windowManager.close();
        }),
  ]);
  if (kDebugMode) {
    print("menu:${_menuMain.toString()}");
  }
  //为系统托盘设置菜单项
  await _systemTray.setContextMenu(_menuMain);
}
  1. 完整代码
dart 复制代码
import 'package:flutter/material.dart';
import 'package:tray_manager/tray_manager.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>  {
 Timer? _timer;
 final SystemTray _systemTray = SystemTray();
  @override
  void initState() {
    super.initState();
    _initSystemTray();
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }

  void _initSystemTray() {
    // ...
  }

//开始图标闪烁
void startFlashIcon() {
  if (kDebugMode) {
    print("startFlashIcon");
  }
  var imageList = const [
    "assets/images/tray_icon_original.ico",
    "assets/images/tray_icon.ico"
  ];
  var index = 0;
  _timer =
      Timer.periodic(const Duration(milliseconds: 500), (Timer timer) async {
    if (kDebugMode) {
      print("path:${imageList[index]}");
    }
    await _systemTray.setImage(imageList[index]);
    index = (index == 0) ? 1 : 0;
  });
}

//停止图标闪烁
void stopFlashIcon() async {
  if (kDebugMode) {
    print("stopFlashIcon");
  }
  _timer?.cancel();
  _timer = null;
  await _systemTray.setImage("assets/images/tray_icon_original.ico");
}
  @override
  Widget build(BuildContext context) {
    // ...
  }
}

3. 两种方案对比

复制代码
   system_tray 支持菜单项添加图标,tray_manager不支持菜单项添加图标

4. 注意事项

Windows平台系统托盘图标需要是以.ico后缀的图片,菜单项图标需要是.bmp后缀的图片,否则图片无法显示;

5. 话题拓展

  • BMP格式(Bitmap):BMP是一种无损的位图图像格式,最初由Microsoft开发。它可以存储图像的像素颜色和位置信息,并支持不同的色彩深度。BMP文件通常较大,因为它们不经过压缩,保留了图像的每个像素的完整信息。BMP格式适用于Windows系统和一些图像编辑软件。
  • ICO格式(Icon):ICO是一种用于存储图标的文件格式。ICO文件通常用于表示计算机系统上的各种图标,例如文件夹、应用程序和网站等的图标。ICO文件可以包含多个图标大小和颜色深度的版本,以适应不同的显示需求。ICO文件可以在Windows系统中直接使用,也可以在网页或应用程序中使用。
相关推荐
maaath2 小时前
【无标题】Flutter for OpenHarmony 的文具手账应用开发实践
flutter·华为·harmonyos
里欧跑得慢2 小时前
Flutter 主题管理:构建一致的用户界面
前端·css·flutter·web
liulian091617 小时前
Flutter for OpenHarmony 跨平台开发:单位转换功能实战指南
flutter
千码君201618 小时前
Trae:一些关于flutter和 go前后端开发构建的分享
android·flutter·gradle·android-studio·trae·vibe code
maaath19 小时前
【maaath】Flutter for OpenHarmony 手表配饰应用实战开发
flutter·华为·harmonyos
maaath20 小时前
【maaath】Flutter for OpenHarmony 跨平台计算器应用开发实践
flutter·华为·harmonyos
maaath1 天前
【maaath】Flutter for OpenHarmony 闹钟时钟应用开发实战
flutter·华为·harmonyos
maaath1 天前
【maaath】Flutter for OpenHarmony 短信管理应用实战
flutter·华为·harmonyos
maaath1 天前
【maaath】Flutter for OpenHarmony打造跨平台便签备忘录应用
flutter·华为·harmonyos
千码君20161 天前
flutter:与Android Studio模拟器的调试分享
android·flutter