Most of the desktop software we use on a daily basis takes up space in the system tray. Of course, we can also achieve this effect with Flutter via third-party plug-ins.

tray_manager

Install 🛠

Click tray_Manager to get the latest version. The following is the latest version at the time of writing:

tray_manager: ^ 0.1.4
Copy the code

👻 Note: Additional operations are required when developing programs for Linux, which can be found here

Using 🥩

First instantiate the TrayManager object:

final TrayManager _trayManager = TrayManager.instance;
Copy the code

Set the icon 🐱💻

In the system tray display, of course, is the first to set an icon.

After instantiating TrayManager, we can use setIcon to set ICONS.

Here, we need to prepare images in two formats:.png and.ico. PNG images are displayed in trays on other platforms, and ICOs are displayed on Windows platforms.

final String _iconPathWin = 'assets/images/tray_manager.ico';
final String _iconPathOther = 'assets/images/tray_manager.png';
Copy the code

What if we display it in PNG format on Windows 🤔?

await _trayManager.setIcon(_iconPathOther);
Copy the code

Although 😲 occupies the position, but will not display it at all! Let’s change it to the correct format:

await _trayManager.setIcon(Platform.isWindows ? _iconPathWin : _iconPathOther);
Copy the code

This will correctly display the icon we set up 😀

Since this is the case, then we can simulate QQ and wechat to the message of the flashing effect!

Prepare two fully transparent images in different formats.

final String _iconNullWin = 'assets/images/null.ico';
final String _iconNullOther = 'assets/images/null.png';
Copy the code

To see if the icon has been set, we also need a Boolean value:

bool _hasIcon = false;
Copy the code

Change the code for setting ICONS:

void _generateIcon() async {
  await _trayManager.setIcon(Platform.isWindows ? _iconPathWin : _iconPathOther);
  _hasIcon = true;
}
Copy the code

Now write a method that flashes the image every 300 milliseconds, using a Timer object:

Timer? _timer;
Copy the code

Don’t forget to dispose in the dispose method:

@override
void dispose() {
  // TODO: implement dispose_timer? .cancel();super.dispose();
}
Copy the code
void _iconFlash() {
  _timer = Timer.periodic(const Duration(milliseconds: 300), (timer) async {
    if (_hasIcon) {
      await _trayManager.setIcon(Platform.isWindows ? _iconNullWin : _iconNullOther);
    } else {
      await_trayManager.setIcon(Platform.isWindows ? _iconPathWin : _iconPathOther); } _hasIcon = ! _hasIcon; setState(() {}); }); }Copy the code

With the method to turn on the flicker animation, you also need a method to turn off the flicker effect:

void_closeIconFlash() { _timer? .cancel(); _generateIcon(); }Copy the code

All set. Let’s see what happens.

Perfect 😎

Set the prompt message 🐾

When we mouse over the software icon in the tray area, there is usually a prompt message. Here we can also set:

void _generateToolTip() async {
  await _trayManager.setToolTip('What do you want 😒');
}
Copy the code

Set the menu item 🕸

There is an icon in the tray area, but there seems to be nothing except the icon. Isn’t that pure gross? Standing pit not pull 💩. Now, let’s give it a hand.

We can set the menu using the setContextMenu method, which passes an array object of MenuItem, and then we have to figure out what the object is passing in. There are six parameters that can be passed to a MenuItem:

  • String? key: Enter or leave a key as a unique identifier
  • String? title: The title (content) of the passed option
  • String? toolTip: Passes the mouse over the option prompt
  • bool isEnabled = true: Indicates whether to enable the option
  • bool isSeparatorItem = false: Indicates whether to separate the line item
  • List<MenuItem>? items: MenuItem list of invalid nesting dolls

Ok, now that we’ve got the MenuItem sorted out, let’s use it:

List<MenuItem> items = [
  MenuItem(title: 'Chinese'),
  MenuItem(title: 'mathematics', toolTip: 'Inescapable'),
  MenuItem(title: 'English', isEnabled: false),
  MenuItem.separator,
  MenuItem(
    key: 'science',
    title: 'science',
    items: [
      MenuItem(title: 'physical'),
      MenuItem(title: 'chemistry'),
      MenuItem(title: 'biological'),
    ],
  ),
  MenuItem.separator,
  MenuItem(
    key: 'arts',
    title: 'liberal arts',
    items: [
      MenuItem(title: 'political'),
      MenuItem(title: 'history'),
      MenuItem(title: 'geographic'),],)];Copy the code

Load them up:

await trayManager.setContextMenu(items);
Copy the code

HMM 😶 how useless?? I looked at the console, and there was no error.

The menu is already set up, we just haven’t written a way to make it appear.

Register mouse events at 🐹

Under what circumstances do we make it display 🤔? Right mouse click icon! To listen for a right mouse click on an icon, let’s change the code:

class _UseTrayManagerPageState extends State<UseTrayManagerPage> with TrayListener {
  
  @override
  void initState() {
    super.initState();
    _trayManager.addListener(this);
  }

  @override
  voiddispose() { _timer? .cancel(); _trayManager.removeListener(this);
    super.dispose(); }... }Copy the code

By mixing the with keyword into the TrayListener class, we can do more event manipulation on the icon.

  • onTrayMenuItemClick: Menu option Click event
  • onTrayIconRightMouseUp: right click on the event, not try out 😕
  • onTrayIconRightMouseDown: icon right-click event
  • onTrayIconMouseUp: supposedly left click after the event, did not try out 😕
  • onTrayIconMouseDown: icon Left-click event

Set the right click event:

@override
void onTrayIconRightMouseDown() async {
  await _trayManager.popUpContextMenu();
}
Copy the code

Set menu options click events:

@override
void onTrayMenuItemClick(MenuItem menuItem) {
  BotToast.showText(text: 'You chose${menuItem.title}');
}
Copy the code

Set icon left mouse button down event (display program) :

@override
void onTrayIconMouseDown() {
  windowManager.show(a);// This method comes from the window_manager plug-in
}
Copy the code

Obtain the icon location 🐣

This method will return the position of the icon on the screen (Rect object)

await _trayManager.getBounds();
Copy the code

Remove tray area program 🦝

In addition to closing the application and removing it from the tray area, we can also actively remove it:

await _trayManager.destroy();
Copy the code

system_tray

Install 🛠

Click system_tray to get the latest version. The following is the latest version at the time of writing:

system_tray: ^ 0.1.0 from
Copy the code

👻 Note: Additional operations are required when developing programs for Linux, which can be found here

Using 🥩

Instantiate the SystemTray object:

final SystemTray _systemTray = SystemTray();
Copy the code

Initialize 🐱 💻

System_tray sets up ICONS and prompts all at once during initialization.

Let’s first prepare the constant resources to use:

final String _title = 'First Desktop Application';
final String _toolTip = 'What are you looking at?';
final String _iconPathWin = 'assets/images/system_tray.ico';
final String _iconPathOther = 'assets/images/system_tray.png';
Copy the code

The system_tray icon is set in the initialization method:

void _initTray() async {
  String _iconPath = Platform.isWindows ? _iconPathWin : _iconPathOther;
  await _systemTray.initSystemTray(title: _title, iconPath: _iconPath, toolTip: _toolTip);
}
Copy the code

These are all set during initialization, so how do we change them later? Don’t worry, you can use the following methods:

  • setImage: Modify icon
  • setTooltip: Modify prompt
  • setTitle: Modify the title
  • getTitle: Get the title

Get ready to change:

final String _newToolTip = "Why are you fat?";
final String _anotherIconWin = 'assets/images/another.ico';
final String _anotherIconOther = 'assets/images/another.png';
Copy the code

Get started:

void _modifyInfo() async {
  String _newPath = Platform.isWindows ? _anotherIconWin : _anotherIconOther;
  await _systemTray.setToolTip(_newToolTip);
  await _systemTray.setImage(_newPath);
  setState(() {});
}
Copy the code

Set the menu item 🕸

System_tray Sets menu items with MenuItem and SubMenu.

MenuItem can be passed three parameters:

  • required String label: Indicates the displayed label
  • bool enabled = true: Optional or not
  • void Function()? onClicked: Click event

SubMenu requires two parameters:

  • required String label: Menu label
  • required List children: the submenu
void _setMenu() async {
  List<MenuItem> menus = [
    MenuItem(label: '5', onClicked: () => _floorNumber('5')),
    MenuItem(label: ' 'on the fourth floor, onClicked: () => _floorNumber(' 'on the fourth floor)),
    SubMenu(
      label: 'on the third floor,
      children: [
        MenuItem(label: 'Third floor toilet', onClicked: () => _floorNumber('Third floor toilet')),
      ],
    ),
    MenuItem(label: ' 'on the second floor, onClicked: () => _floorNumber(' 'on the second floor)),
    MenuItem(label: 'on the first floor, enabled: false)];await _systemTray.setContextMenu(menus);
}
Copy the code

Of course, like tray_manager, this is only set, but it doesn’t tell when to display it. So we need an event for the right mouse button icon.

Register mouse events at 🐹

There are several mouse events:

  • leftMouseUp: Lift the left mouse button
  • leftMouseDown: Press the left mouse button
  • leftMouseDblClk: Double-click the left mouse button
  • rightMouseUp: Right mouse button is lifted
  • rightMouseDown: Press the right mouse button
void _registerEvent() {
  _systemTray.registerSystemTrayEventHandler((eventName) {
    if (eventName == 'leftMouseDown') {
      windowManager.show(a); }else  if (eventName == 'rightMouseDown') { _systemTray.popUpContextMenu(); }}); }Copy the code

🛫OK, that’s the end of this article, which is for the current version of the plug-in and is not guaranteed to be applicable to future iterations of plug-in usage.

Finally, thanks to lijy91 and Antler119 for the maintenance and development of the above plug-ins 😁.