shortcut_atlas

Enterprise keyboard shortcuts for Flutter desktop: bind keys to actions, and reveal a contextual, iPadOS-style cheat-sheet HUD by holding Alt.

Declare a shortcut once as a ShortcutCommand. That single descriptor is the source of truth for both:

  • dispatch — compiled into real Flutter Shortcuts / Actions / Focus, so focus scoping, precedence, and contextual enable/disable are the framework's job (not a custom key router); and
  • discoverability — projected into a hold-Alt HUD that lists exactly the shortcuts live for the focused screen. Because the HUD resolves enablement with the same Action.isEnabled the keypress uses, the cheat-sheet can never advertise something the key wouldn't actually fire.

Status: desktop-first (Windows, macOS, Linux). Web is best-effort. The HUD, dispatch, cross-platform modifiers, the text-field auto-guard, the command palette, platform awareness, user-remappable keymaps (with pluggable persistence), and a menu bar bridge are all implemented.

Install

dependencies:
  shortcut_atlas: ^0.1.0

Quick start

Install ShortcutAtlas once, above your app's Navigator, via MaterialApp.builder:

MaterialApp(
  builder: (context, child) => ShortcutAtlas(child: child!),
  home: const HomeScreen(),
);

Then wrap a screen in a ShortcutScope and declare its commands. Hold Alt to see them.

Example 1 — switch tabs with 1 / 2 / 3

command(...) binds a key to a callback. Bare (modifier-less) keys are auto-guarded: they are disabled while a text field has focus, so typing 1 into a TextField types 1 instead of switching tabs.

ShortcutScope(
  kind: ScopeKind.screen,
  autofocus: true, // put the scope on the focus chain
  commands: [
    for (var i = 0; i < 3; i++)
      command(
        'tabs.open.${i + 1}',
        activator: CharacterActivator('${i + 1}'),
        label: 'Open Tab ${i + 1}',
        group: ShortcutGroup.tabs,
        onInvoke: () => _tabController.animateTo(i),
      ),
  ],
  child: /* Scaffold with a TabBar / TabBarView */,
);

Example 2 — Ctrl/Cmd+S submits the same thing the button does

There is one Intent and one Action. The button dispatches it via AtlasActionButton (which uses Actions.handler), and Ctrl/Cmd+S binds the same intent. isEnabled is the single gate: it lets the key fall through when disabled, auto-disables the button, and greys the HUD row — all from one signal.

class SubmitIntent extends Intent { const SubmitIntent(); }

class SubmitAction extends Action<SubmitIntent> {
  SubmitAction(this.onSubmit, this.canSubmit) {
    canSubmit.addListener(notifyActionListeners);
  }
  final VoidCallback onSubmit;
  final ValueListenable<bool> canSubmit;
  @override bool isEnabled(SubmitIntent i) => canSubmit.value;
  @override Object? invoke(SubmitIntent i) { onSubmit(); return null; }
}

// in build:
ShortcutScope(
  kind: ScopeKind.screen,
  autofocus: true,
  actions: {SubmitIntent: _submitAction},
  commands: [
    ShortcutCommand<SubmitIntent>(
      id: 'form.submit',
      activator: primary(LogicalKeyboardKey.keyS), // ⌘S on macOS, Ctrl+S elsewhere
      intent: const SubmitIntent(),
      label: 'Save',
      group: ShortcutGroup.file,
    ),
  ],
  child: Column(children: [
    const TextField(/* onChanged: () => dirty.value = true */),
    AtlasActionButton(
      intent: const SubmitIntent(),
      enableWhen: dirty, // rebuilds the button as enablement flips
      child: const Text('Save'),
    ),
  ]),
);

Core concepts

Type Role
ShortcutAtlas Root widget; hosts the controller, the root command action, the hold-Alt detector, and the HUD overlay.
ShortcutScope Declares the commands active in a region; compiles them to Shortcuts/Actions/Focus.
ShortcutCommand / command() The single descriptor: keys + label + group + behavior.
primary() / PlatformActivator Ctrl on Windows/Linux, Cmd on macOS — one declaration, correct everywhere.
ShortcutGroup HUD section / bucket (e.g. ShortcutGroup.tabs).
AtlasActionButton A button that shares one intent + enablement gate with a shortcut.
AtlasThemeData / AtlasTheme Theme the HUD panel, chips, header/footer, and animation.
AtlasConfig Hold threshold, trigger keys, disabled-row policy, the auto-guard toggle, the keymap store.
Keymap / KeyRecorder / KeymapStore User re-bindings (factory < admin < user), a "press a key" recorder, and pluggable persistence.
ShortcutMenuBar / ShortcutMenu A Material menu bar generated from the same commands.

How the hold-Alt HUD works (peek-then-act)

Alt is a reveal key, not a chord prefix. Hold Alt alone past a short threshold (~350 ms) and the panel lists the focused screen's shortcuts; release Alt and press the actual key. Pressing any other key while Alt is held aborts the peek so real combos (e.g. Alt+F) are untouched — the detector is a single non-consuming HardwareKeyboard handler and never blocks dispatch.

Command palette

A Cmd/Ctrl+K palette is installed by default (AtlasConfig.enablePalette). It lists the currently-active commands (those with surfaces.palette), filters as you type, and is fully keyboard-navigable (//Enter/Esc). Open it programmatically with ShortcutAtlas.of(context).openPalette().

Remappable keymaps

Bindings are layered factory < admin < user. The factory layer is each command's own activator; admin is a fixed override map; user is the runtime re-binding. The effective chord flows everywhere — dispatch, HUD, palette, and menu — from one place.

final controller = ShortcutAtlas.of(context);

// Re-bind "save" to whatever the user presses next:
KeyRecorder(
  initial: controller.effectiveActivator(saveCommand),
  onRecorded: (activator) => controller.keymap.rebind('save', activator),
);

controller.keymap.reset('save');  // back to default
controller.keymap.resetAll();

Persist re-bindings by passing a KeymapStore to AtlasConfig — implement the two-method interface over any backend (the example wires shared_preferences):

ShortcutAtlas(
  config: AtlasConfig(keymapStore: myStore),
  child: child!,
);

Encoding is handled for you by ActivatorCodec; the store only moves a Map<String, dynamic> in and out.

ShortcutMenuBar builds a Material MenuBar from the same ShortcutCommands, so a command appears in the menu with its (effective, re-bound) shortcut and runs the same behavior the key would. The hint is display-only, so it never double-fires with the ShortcutScope binding.

ShortcutMenuBar(menus: [
  ShortcutMenu('File', [newWindow, save, close]),
  ShortcutMenu('Edit', [find]),
]);

Platform awareness

AtlasPlatform exposes isDesktop, isApple, isWeb, isMobile, supportsHud, primaryModifierLabel, and reservedReason(activator) (a best-effort check for chords the OS or browser intercepts). The HUD and palette only activate on desktop or web by default — flip restrictToDesktop to change that. In debug builds, scopes warn about duplicate and reserved chords.

Theming

The HUD panel has a titled header and a footer that counts the active shortcuts and points at the palette. Three presets ship — the default glass, plus AtlasThemeData.minimal(context) (blur-free) and .accent(context) (seed-tinted) — and any field is overridable via copyWith:

ShortcutAtlas(
  theme: AtlasThemeData.fallback(context).copyWith(
    panelBlurSigma: 30,
    panelMaxWidth: 720,
    headerTitle: 'Commands',
    showFooter: false,
  ),
  child: child!,
);

Notes & limitations (0.1.0)

  • Bare keys + text fields: bare single-key shortcuts are auto-disabled while an EditableText has focus (AtlasConfig.guardBareKeysInEditables, on by default). For screens that mix typing and shortcuts, prefer a modified activator (e.g. primary(...)).
  • Web: browsers reserve some chords (Ctrl+S/W/T) and may not honor preventDefault; mark such commands webSafe: false to hide them on web.
  • Menu bar: ShortcutMenuBar renders an in-window Material MenuBar on every platform (including macOS). A true native top-of-screen macOS PlatformMenuBar is intentionally not used, to avoid the OS registering the chord a second time.
  • Planned: richer conflict-resolution UX (duplicate/reserved detection ships in debug today) and a re-binding conflict guard.

License

MIT © Luminest

Libraries

shortcut_atlas
Enterprise keyboard shortcuts for Flutter desktop.