[{"data":1,"prerenderedAt":22},["ShallowReactive",2],{"articolo-gestione-degli-overlay-in-flutter-tooltip-popup-e-menu-personalizzati-con-overlayportal":3,"comments-article-gestione-degli-overlay-in-flutter-tooltip-popup-e-menu-personalizzati-con-overlayportal":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},57,"Gestione degli Overlay in Flutter: tooltip, popup e menu personalizzati con OverlayPortal","gestione-degli-overlay-in-flutter-tooltip-popup-e-menu-personalizzati-con-overlayportal","Scopri come usare Overlay e il moderno OverlayPortal per creare tooltip, popup contestuali e menu fluttuanti in Flutter, con un controllo preciso su posizionamento e ciclo di vita.","## Cos'è un Overlay in Flutter\n\nIn Flutter, l'**Overlay** è uno stack di elementi (gli `OverlayEntry`) che vengono renderizzati sopra il resto dell'interfaccia. Ogni volta che apri un `Dialog`, un `SnackBar`, un `Tooltip` o un menu a tendina, dietro le quinte Flutter sta usando l'Overlay per posizionare quei widget al di sopra dell'albero principale.\n\nGestire manualmente un `OverlayEntry` è sempre stato possibile, ma richiedeva molta attenzione: bisognava inserirlo, rimuoverlo e gestirne il ciclo di vita a mano, con il rischio di lasciare entry orfane o di causare memory leak. Dalla versione 3.10, Flutter ha introdotto **`OverlayPortal`**, un widget che semplifica enormemente questo lavoro.\n\nIn questo articolo vedremo entrambi gli approcci, partendo dal classico `OverlayEntry` per poi passare al più moderno e sicuro `OverlayPortal`.\n\n## L'approccio classico: OverlayEntry\n\nPrima di `OverlayPortal`, per mostrare qualcosa in overlay si procedeva così:\n\n```dart\nclass ClassicOverlayExample extends StatefulWidget {\n  const ClassicOverlayExample({super.key});\n\n  @override\n  State\u003CClassicOverlayExample> createState() => _ClassicOverlayExampleState();\n}\n\nclass _ClassicOverlayExampleState extends State\u003CClassicOverlayExample> {\n  OverlayEntry? _entry;\n\n  void _show() {\n    _entry = OverlayEntry(\n      builder: (context) => Positioned(\n        top: 100,\n        left: 50,\n        child: Material(\n          elevation: 4,\n          borderRadius: BorderRadius.circular(8),\n          child: const Padding(\n            padding: EdgeInsets.all(16),\n            child: Text('Sono un overlay!'),\n          ),\n        ),\n      ),\n    );\n    Overlay.of(context).insert(_entry!);\n  }\n\n  void _hide() {\n    _entry?.remove();\n    _entry = null;\n  }\n\n  @override\n  void dispose() {\n    _hide(); \u002F\u002F fondamentale per evitare leak\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ElevatedButton(\n      onPressed: () => _entry == null ? _show() : _hide(),\n      child: const Text('Toggle overlay'),\n    );\n  }\n}\n```\n\nQuesto funziona, ma ha diversi punti critici: devi ricordarti di rimuovere l'entry nel `dispose`, gestire lo stato `null` manualmente e stare attento a non inserire due volte la stessa entry.\n\n## Il modo moderno: OverlayPortal\n\n`OverlayPortal` risolve tutti questi problemi. Il widget si occupa automaticamente di inserire e rimuovere l'entry seguendo il ciclo di vita del widget stesso. Il controllo avviene tramite un `OverlayPortalController`.\n\n```dart\nclass PortalExample extends StatefulWidget {\n  const PortalExample({super.key});\n\n  @override\n  State\u003CPortalExample> createState() => _PortalExampleState();\n}\n\nclass _PortalExampleState extends State\u003CPortalExample> {\n  final _controller = OverlayPortalController();\n\n  @override\n  Widget build(BuildContext context) {\n    return OverlayPortal(\n      controller: _controller,\n      overlayChildBuilder: (context) {\n        return const Positioned(\n          top: 120,\n          left: 40,\n          child: Material(\n            elevation: 4,\n            child: Padding(\n              padding: EdgeInsets.all(16),\n              child: Text('Overlay con OverlayPortal'),\n            ),\n          ),\n        );\n      },\n      child: ElevatedButton(\n        onPressed: _controller.toggle,\n        child: const Text('Toggle'),\n      ),\n    );\n  }\n}\n```\n\nNessun `dispose` manuale, nessuna variabile nullable da controllare. Il `controller` offre i metodi `show()`, `hide()` e `toggle()`, oltre alla proprietà `isShowing`.\n\n## Posizionare l'overlay accanto a un widget\n\nUno dei casi d'uso più frequenti è mostrare un popup **ancorato** a un altro widget (ad esempio un menu che appare sotto un pulsante). Per farlo si combina `OverlayPortal` con `CompositedTransformTarget` e `CompositedTransformFollower`, che sfruttano un `LayerLink` per collegare la posizione dei due widget.\n\n```dart\nclass AnchoredMenu extends StatefulWidget {\n  const AnchoredMenu({super.key});\n\n  @override\n  State\u003CAnchoredMenu> createState() => _AnchoredMenuState();\n}\n\nclass _AnchoredMenuState extends State\u003CAnchoredMenu> {\n  final _controller = OverlayPortalController();\n  final _link = LayerLink();\n\n  @override\n  Widget build(BuildContext context) {\n    return CompositedTransformTarget(\n      link: _link,\n      child: OverlayPortal(\n        controller: _controller,\n        overlayChildBuilder: (context) {\n          return Stack(\n            children: [\n              \u002F\u002F Barriera per chiudere toccando fuori\n              Positioned.fill(\n                child: GestureDetector(\n                  behavior: HitTestBehavior.translucent,\n                  onTap: _controller.hide,\n                ),\n              ),\n              CompositedTransformFollower(\n                link: _link,\n                targetAnchor: Alignment.bottomLeft,\n                followerAnchor: Alignment.topLeft,\n                offset: const Offset(0, 8),\n                child: Material(\n                  elevation: 6,\n                  borderRadius: BorderRadius.circular(8),\n                  child: Column(\n                    mainAxisSize: MainAxisSize.min,\n                    children: [\n                      ListTile(\n                        title: const Text('Modifica'),\n                        onTap: _controller.hide,\n                      ),\n                      ListTile(\n                        title: const Text('Elimina'),\n                        onTap: _controller.hide,\n                      ),\n                    ],\n                  ),\n                ),\n              ),\n            ],\n          );\n        },\n        child: ElevatedButton(\n          onPressed: _controller.toggle,\n          child: const Text('Apri menu'),\n        ),\n      ),\n    );\n  }\n}\n```\n\nCon `CompositedTransformFollower` il menu segue automaticamente la posizione del pulsante anche durante lo scroll, senza bisogno di calcolare coordinate globali con `RenderBox`.\n\n## Aggiungere una barriera e chiusura al tocco esterno\n\nComo mostrato nell'esempio, un pattern comune è avvolgere il contenuto in uno `Stack` con un `Positioned.fill` che intercetta i tap fuori dal popup e ne provoca la chiusura. Impostare `behavior: HitTestBehavior.translucent` garantisce che i tocchi vengano rilevati anche sulle aree trasparenti.\n\n## Quando usare OverlayPortal e quando no\n\n- **Usa `OverlayPortal`** per tooltip personalizzati, menu contestuali, dropdown, popover e badge fluttuanti legati a un widget specifico.\n- **Usa `showDialog` o `showMenu`** quando ti bastano i componenti Material standard: sono già ottimizzati e accessibili.\n- **Evita l'overlay** per elementi che fanno parte del flusso normale della UI: in quei casi è meglio un semplice `Stack` o un layout condizionale.\n\n## Buone pratiche\n\n1. **Preferisci `OverlayPortal` a `OverlayEntry`** per il ciclo di vita automatico e la sicurezza contro i leak.\n2. **Usa `LayerLink`** invece di calcolare posizioni manuali: è più robusto rispetto ai cambi di layout.\n3. **Gestisci la chiusura** con una barriera trasparente o intercettando il tasto \"indietro\" su Android.\n4. **Cura l'accessibilità** aggiungendo `Semantics` appropriati ai popup, poiché non vengono annunciati automaticamente come i dialog standard.\n\n## Conclusione\n\n`OverlayPortal` rappresenta l'evoluzione naturale della gestione degli overlay in Flutter: elimina il boilerplate del vecchio `OverlayEntry` e riduce drasticamente i rischi di errore. Combinato con `LayerLink`, `CompositedTransformTarget` e `CompositedTransformFollower`, permette di costruire menu, tooltip e popover ancorati con poche righe di codice pulito e manutenibile. La prossima volta che ti serve un elemento fluttuante sopra la UI, sai da dove partire.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Farticles\u002F5084df21-0e56-4758-9a9b-eb6245bd15d7.jpg",null,"published","2026-08-05T04:00:49+00:00","Overlay in Flutter con OverlayPortal: guida pratica","Impara a creare tooltip, popup e menu ancorati in Flutter con OverlayPortal, LayerLink e CompositedTransformFollower. Guida pratica con esempi in Dart.",{"id":16,"name":17,"slug":18},1,"Guide","guide",{"id":16,"name":20},"Flutter Bot",[],1785926042320]