Panel
Build an IDE-style workspace in Flutter. Dock tabs, split the layout, or pull a panel into its own macOS window.
A dockable workspace
The real package:panel dock. Drag a tab to dock or split it, drag a splitter to resize, click a tab to focus.
A panel can become a real window
Flutter's new desktop windowing API lets one app draw into several native windows. They remain part of the same widget tree, so a detached panel keeps the same state as the dock it came from.
The API is still experimental and available on Flutter's main/master channel behind a flag. Enable it once:
fvm flutter config --enable-windowing
Start with Flutter's official article: Introducing the Desktop Windowing API. For more detail, see Canonical's write-up or the tracking issue.
Pop a panel into its own window
Pull a tab away and Panel opens it in a new macOS window. It keeps working with the same app state, and you can drag it back over the main window to dock it again.
runWidget(PanelScope( manager: manager, // shared by every window child: WindowManager( // hosts every window initialWindows: [WindowEntry( // the main window controller: WindowController(), builder: (_) => AppShell(home: PanelDock()), )], ), )); // detach → register another WindowEntry as a new view
Flutter for the dock, macOS for the windows
The dock itself is regular Flutter and even runs in this web page. The optional macOS package adds detachable windows without tying the core package to one platform.
package:panel
Tabs, docking, splits, saved layouts, themes, and keyboard shortcuts.
package:panel_macos
Turns detached panels into native macOS windows and lets them snap back.
example/
A complete macOS app showing the two packages together.
Add panels wherever they belong
Each side can hold one or more tab groups. Use the buttons to add panels, or drag a tab to an edge to place it beside another group.
Drag a tab, it knows where it'll land
A tab can move between groups. Dropping on an edge makes a new group (with a live preview); the center adds a tab.
Flutter creates the window; AppKit finishes it
Flutter now handles the windows themselves. A small Swift helper fills the few gaps needed for an IDE-style detachable panel:
- Custom appearance: remove the title bar and window buttons.
- Sensible placement: open near the pointer on the active desktop.
- Docking: follow the window while it is dragged and snap it back on release.
The helper ships inside package:panel_macos, so apps using Panel do not need to copy its Swift code.
Match the rest of your app
Choose the dock colors, tab shape, borders, and dividers with PanelTheme. Try one:
Pluggable persistence
Implement PanelStorage and the manager auto-saves (debounced) on every change; restore() reloads it on startup.
class MyStorage implements PanelStorage { FutureOr<Map<String, Object?>?> read() => /* load */; FutureOr<void> write(Map<String, Object?> m) => /* save */; } final manager = PanelManager(config: PanelDockConfig(storage: MyStorage())) ..registerPanel(/* … */); await manager.restore(); // applies the saved layout
Split & merge with the keyboard
Splitting/merging is exposed as Actions & Intents acting on the focused group (a group focuses on click and shows an accent border).
⌘ \ split the focused panel ⌘ ⇧ \ merge it back
Shortcuts( shortcuts: defaultPanelShortcuts(), child: Actions( actions: panelActions(manager), child: const Focus(autofocus: true, child: PanelDock()), ), );
A Flutter dock that feels at home on desktop
Use Flutter's windowing API for real windows and a small macOS helper for the final desktop details.
Splits, themes, saved layouts, and shortcuts stay in Dart. The live examples on this page are the same package:panel widgets running in your browser.
See MANUAL.md for the full build guide and the package READMEs for the API.
Occasional Flutter
Practical notes on Flutter desktop, native platform work, and projects like this one.