[{"data":1,"prerenderedAt":22},["ShallowReactive",2],{"articolo-menu-contestuali-e-scorciatoie-da-tastiera-in-flutter-guida-a-shortcuts-e-actions":3,"comments-article-menu-contestuali-e-scorciatoie-da-tastiera-in-flutter-guida-a-shortcuts-e-actions":21},{"id":4,"title":5,"slug":6,"excerpt":7,"body":8,"cover_image":9,"video_url":10,"status":11,"published_at":12,"meta_title":13,"meta_description":14,"category":15,"author":19},56,"Menu contestuali e scorciatoie da tastiera in Flutter: guida a Shortcuts e Actions","menu-contestuali-e-scorciatoie-da-tastiera-in-flutter-guida-a-shortcuts-e-actions","Rendi le tue app Flutter più professionali su desktop e web gestendo scorciatoie da tastiera e menu contestuali con Shortcuts, Actions, Intent e il widget MenuAnchor.","## Perché gestire tastiera e menu contestuali\n\nCon l'espansione di Flutter verso desktop (Windows, macOS, Linux) e web, offrire un'esperienza utente completa significa supportare anche input diversi dal tocco. Le scorciatoie da tastiera (`Ctrl+S`, `Ctrl+Z`) e i menu contestuali (tasto destro) sono ormai attesi dagli utenti di applicazioni desktop.\n\nFlutter offre un sistema potente e disaccoppiato per gestire tutto questo tramite i widget `Shortcuts`, `Actions` e le classi `Intent`, oltre a `MenuAnchor` per i menu. Vediamo come metterli insieme.\n\n## Il modello Intent-Action\n\nIl cuore del sistema si basa su tre concetti:\n\n- **Intent**: rappresenta *cosa* vogliamo fare (un'intenzione), senza specificare come.\n- **Action**: contiene la logica che *esegue* l'intent.\n- **Shortcuts**: mappa combinazioni di tasti sugli intent.\n\nQuesto disaccoppiamento permette di associare la stessa azione a più trigger (tastiera, pulsante, menu).\n\n```dart\n\u002F\u002F Definiamo un intent personalizzato\nclass SaveIntent extends Intent {\n  const SaveIntent();\n}\n\nclass UndoIntent extends Intent {\n  const UndoIntent();\n}\n```\n\n## Mappare le scorciatoie\n\nUsiamo il widget `Shortcuts` per associare combinazioni di tasti agli intent:\n\n```dart\nShortcuts(\n  shortcuts: \u003CShortcutActivator, Intent>{\n    SingleActivator(LogicalKeyboardKey.keyS, control: true):\n        const SaveIntent(),\n    SingleActivator(LogicalKeyboardKey.keyZ, control: true):\n        const UndoIntent(),\n  },\n  child: Actions(\n    actions: \u003CType, Action\u003CIntent>>{\n      SaveIntent: CallbackAction\u003CSaveIntent>(\n        onInvoke: (intent) {\n          debugPrint('Salvataggio in corso...');\n          return null;\n        },\n      ),\n      UndoIntent: CallbackAction\u003CUndoIntent>(\n        onInvoke: (intent) {\n          debugPrint('Annulla ultima azione');\n          return null;\n        },\n      ),\n    },\n    child: const Focus(\n      autofocus: true,\n      child: MyEditor(),\n    ),\n  ),\n)\n```\n\n> **Nota importante:** le scorciatoie funzionano solo se un widget nella gerarchia ha il focus. Per questo avvolgiamo il contenuto in un `Focus` con `autofocus: true`.\n\n## Action riutilizzabili con classi dedicate\n\nPer logica più complessa, conviene creare classi `Action` dedicate invece di `CallbackAction`:\n\n```dart\nclass SaveAction extends Action\u003CSaveIntent> {\n  SaveAction(this.controller);\n\n  final DocumentController controller;\n\n  @override\n  Object? invoke(SaveIntent intent) {\n    controller.save();\n    return null;\n  }\n\n  @override\n  bool isEnabled(SaveIntent intent) => controller.hasChanges;\n}\n```\n\nIl metodo `isEnabled` permette di abilitare o disabilitare l'azione dinamicamente: se restituisce `false`, la scorciatoia non verrà eseguita e i menu collegati appariranno disabilitati.\n\n## Menu contestuali con MenuAnchor\n\nDa Flutter 3.7+ è disponibile `MenuAnchor`, un widget Material 3 per creare menu a comparsa. Colleghiamolo al tasto destro del mouse:\n\n```dart\nclass ContextMenuArea extends StatelessWidget {\n  const ContextMenuArea({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final controller = MenuController();\n    return GestureDetector(\n      onSecondaryTapDown: (details) {\n        controller.open(position: details.localPosition);\n      },\n      child: MenuAnchor(\n        controller: controller,\n        menuChildren: [\n          MenuItemButton(\n            leadingIcon: const Icon(Icons.save),\n            shortcut: const SingleActivator(\n              LogicalKeyboardKey.keyS,\n              control: true,\n            ),\n            onPressed: () => Actions.invoke(context, const SaveIntent()),\n            child: const Text('Salva'),\n          ),\n          MenuItemButton(\n            leadingIcon: const Icon(Icons.undo),\n            onPressed: () => Actions.invoke(context, const UndoIntent()),\n            child: const Text('Annulla'),\n          ),\n        ],\n        child: Container(\n          color: Colors.grey.shade100,\n          alignment: Alignment.center,\n          child: const Text('Clic destro qui'),\n        ),\n      ),\n    );\n  }\n}\n```\n\nIl parametro `shortcut` di `MenuItemButton` mostra automaticamente la combinazione di tasti accanto alla voce, migliorando la scopribilità delle funzionalità.\n\n## Barra dei menu con MenuBar\n\nPer le app desktop possiamo aggiungere una vera barra dei menu in stile nativo con `MenuBar`:\n\n```dart\nMenuBar(\n  children: [\n    SubmenuButton(\n      menuChildren: [\n        MenuItemButton(\n          shortcut: const SingleActivator(\n            LogicalKeyboardKey.keyS,\n            control: true,\n          ),\n          onPressed: () => Actions.invoke(context, const SaveIntent()),\n          child: const Text('Salva'),\n        ),\n        MenuItemButton(\n          onPressed: () {},\n          child: const Text('Esci'),\n        ),\n      ],\n      child: const Text('File'),\n    ),\n  ],\n)\n```\n\n## Best practice\n\n- **Centralizza le Actions**: definisci gli `Intent` una volta e collega ogni trigger (menu, pulsante, scorciatoia) allo stesso intent con `Actions.invoke`.\n- **Rispetta le convenzioni di piattaforma**: usa `LogicalKeyboardKey.meta` (Cmd) su macOS al posto di `control`. Considera `SingleActivator(..., meta: true)` per macOS.\n- **Gestisci il focus**: le scorciatoie richiedono un `FocusNode` attivo. Verifica sempre che la sezione interattiva possa ricevere il focus.\n- **Sfrutta isEnabled**: mantieni coerente lo stato tra menu disabilitati e scorciatoie inattive.\n\n## Conclusione\n\nIl trittico `Shortcuts`, `Actions` e `Intent`, combinato con `MenuAnchor` e `MenuBar`, permette di costruire esperienze desktop e web professionali in Flutter, con un'architettura pulita e disaccoppiata. Iniziare a integrarli fin dalle prime fasi del progetto rende l'app più accessibile ed efficiente per gli utenti che usano la tastiera.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Farticles\u002F4fe58807-4b0b-4524-b3ce-63a6bf8baeed.jpg",null,"published","2026-08-04T04:00:40+00:00","Scorciatoie e menu contestuali in Flutter: guida pratica","Impara a gestire scorciatoie da tastiera e menu contestuali in Flutter con Shortcuts, Actions, Intent, MenuAnchor e MenuBar per app desktop e web.",{"id":16,"name":17,"slug":18},1,"Guide","guide",{"id":16,"name":20},"Flutter Bot",[],1785824426721]