What does Flutter's PlatformMenuBar do on macOS, and why does a Flutter app on Windows or Linux need a different menu?
answer
- the system menu bar at the top
- native rendering through a delegate
- macOS support out of the box only
- PlatformProvidedMenuItem for About and Quit
- MenuBar widget in the window
basics
~20 sPlatformMenuBar describes PlatformMenu and PlatformMenuItem entries that macOS renders in its native menu bar; Flutter only supports that out of the box on macOS. Windows and Linux need an in-window menu such as Material's MenuBar.
solid answer
~40 s`PlatformMenuBar` takes `menus`, a list of `PlatformMenu` and `PlatformMenuItem` objects, and hands them to the platform through `WidgetsBinding.platformMenuDelegate`. It draws nothing itself and just returns its `child`; the host renders the menu. Flutter only includes support for **macOS** out of the box, where every app is expected to have the system menu bar with items like About, Hide and Quit, provided through `PlatformProvidedMenuItem` types. On Windows and Linux the widget has no menu to show unless a plugin supplies a delegate, and `PlatformProvidedMenuItem` asserts in debug for types the platform lacks; check `PlatformProvidedMenuItem.hasMenu`. So cross-platform apps use `PlatformMenuBar` on macOS and a Material `MenuBar` with `SubmenuButton` and `MenuItemButton` inside the window elsewhere, sharing the same actions. Only one `PlatformMenuBar` may be active per delegate.
code
dart · 35 linesimport 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class InvoiceMenus extends StatelessWidget {
const InvoiceMenus({super.key, required this.onNewInvoice, required this.child});
final VoidCallback onNewInvoice;
final Widget child;
@override
Widget build(BuildContext context) {
return PlatformMenuBar(
menus: <PlatformMenuItem>[
const PlatformMenu(
label: 'Shop Invoices',
menus: <PlatformMenuItem>[
PlatformProvidedMenuItem(type: PlatformProvidedMenuItemType.about),
PlatformProvidedMenuItem(type: PlatformProvidedMenuItemType.quit),
],
),
PlatformMenu(
label: 'File',
menus: <PlatformMenuItem>[
PlatformMenuItem(
label: 'New Invoice',
shortcut: const SingleActivator(LogicalKeyboardKey.keyN, meta: true),
onSelected: onNewInvoice,
),
],
),
],
child: child,
);
}
}go deeper
Recall that PlatformMenuBar creates the macOS system menu and that Windows and Linux use an in-window MenuBar.
Explain that PlatformMenuBar only forwards data through the platform menu delegate, which macOS implements, and what PlatformProvidedMenuItem.hasMenu guards.
Show how you share one set of actions between the macOS system menu and the in-window menus elsewhere, including shortcut behaviour.
Decide how far each desktop build follows its platform's menu conventions and what that costs in duplicated menu definitions.
## Two kinds of menu bar Desktop platforms put the application menu in different places: | Platform | Where the menu lives | Flutter widget | |---|---|---| | macOS | the **system menu bar** at the top of the screen, shared by all apps | `PlatformMenuBar` | | Windows, Linux | usually **inside the window**, under the title bar | `MenuBar` from the Material library | A Mac app without a proper menu bar feels broken: users look for About, Preferences, Hide and Quit there. A Windows or Linux user expects File and Edit menus inside the window. ## How PlatformMenuBar works - It takes `menus`: `PlatformMenu(label:, menus:)` for top-level and nested menus, `PlatformMenuItem(label:, shortcut:, onSelected:)` for actions, `PlatformMenuItemGroup` to group items between separators, and `PlatformProvidedMenuItem(type:)` for system-supplied items. - It **draws nothing**. Its build returns its `child`; the menu data goes to the platform through `WidgetsBinding.platformMenuDelegate`, and the host platform renders the menu and handles its shortcuts. - The widget is in the tree mainly as a convenient way to **update** the menu: rebuild it with new items, such as a checked state, and the delegate sends the change. - There can be only **one** `PlatformMenuBar` using a given delegate at a time; a second one asserts. ## Platform-provided items `PlatformProvidedMenuItemType` covers items the OS implements, such as `about`, `quit`, `servicesSubmenu`, `hide`, `hideOtherApplications`, `toggleFullScreen` and `minimizeWindow`. They are only available on macOS: `PlatformProvidedMenuItem.hasMenu(type)` returns `false` on Windows, Linux and the mobile platforms, and sending an unsupported type asserts in debug mode. Call `hasMenu` before adding one in shared code. ## Why Windows and Linux need something else Flutter ships a platform menu delegate that macOS understands. On Windows and Linux there is no built-in native implementation, so a `PlatformMenuBar` there shows no menu unless a plugin installs its own delegate. The practical pattern: 1. Define the app's actions once, for example as callbacks or `Intent`s. 2. On macOS, build a `PlatformMenuBar` whose items call those actions, including `PlatformProvidedMenuItem`s for About and Quit. 3. On Windows and Linux, place a Material `MenuBar(children: [...])` at the top of the window, with `SubmenuButton(menuChildren: [...], child: ...)` for File and Edit and `MenuItemButton(onPressed:, shortcut:, child:)` for actions. 4. Choose between them with `defaultTargetPlatform`. ## Shortcuts in menus Both `PlatformMenuItem.shortcut` and `MenuItemButton.shortcut` accept a `SingleActivator`, for example Command-N on macOS or Control-N elsewhere. On macOS the system menu handles the key combination itself. With `MenuItemButton`, the shortcut is shown as a label; making the key combination work when the menu is closed is a separate job for shortcut and action widgets. ## Common mistakes - Expecting `PlatformMenuBar` to draw a menu strip in the window on Windows; it shows nothing there without a plugin delegate. - Placing a `PlatformMenuBar` on several screens at once; only one may use the delegate, and a second asserts. - Adding `PlatformProvidedMenuItem`s in shared code without `hasMenu`, which asserts in debug builds off macOS. - Defining menu actions twice with different behaviour on each platform instead of sharing one set of callbacks. ## Worked example A small-shop invoice tool on macOS shows "Shop Invoices" with About and Quit, a File menu with New Invoice and Export, and an Edit menu. The Windows build shows the same File and Edit menus as a `MenuBar` inside the window. Both call the same `newInvoice()` and `exportCsv()` functions.
- Shared Flutter code adds PlatformProvidedMenuItem(type: PlatformProvidedMenuItemType.quit) and a debug build asserts on Windows. Why?Platform-provided items are only available on macOS. `PlatformProvidedMenuItem.hasMenu` returns `false` on Windows and Linux, and serialising an unsupported type throws an `ArgumentError` in debug mode. Guard it with `hasMenu`, or build the Windows menu with Material's `MenuBar` instead.
saying these in an interview costs you the question
- PlatformMenuBar draws a menu bar inside the Flutter window on every platform.
- PlatformMenuBar gives Windows a native menu with no plugin.
- You can nest several PlatformMenuBars for different screens.
- About and Quit must be hand-built on macOS.