Creare un Drawer di navigazione personalizzato in Flutter
GuidePrincipiante25 min Flutter 3.x

Creare un Drawer di navigazione personalizzato in Flutter

Il Drawer è il classico menu laterale che si apre scorrendo dal bordo dello schermo o toccando l'icona hamburger nella AppBar. È uno dei pattern di navigazione più diffusi nelle app mobile, ideale quando hai molte sezioni da raggiungere.

In questo tutorial costruiremo un Drawer completo e riutilizzabile: creeremo un header con le informazioni utente, un elenco di voci di menu con icone, gestiremo l'evidenziazione della sezione attualmente selezionata e collegheremo la navigazione tra le schermate.

Alla fine avrai un componente pulito e facilmente estendibile da integrare in qualsiasi progetto.

  1. 1

    Predisporre lo Scaffold con il Drawer

    Il Drawer va assegnato alla proprietà drawer dello Scaffold. Flutter aggiunge automaticamente l'icona hamburger nella AppBar quando è presente una AppBar.

    Partiamo da una struttura di base con una schermata Home. Il parametro drawer accetta un widget: per ora inseriamo un Drawer vuoto che riempiremo nei passi successivi.

    import 'package:flutter/material.dart';
    
    void main() => runApp(const MyApp());
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Drawer Demo',
          theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
          home: const HomePage(),
        );
      }
    }
    
    class HomePage extends StatelessWidget {
      const HomePage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Home')),
          drawer: const Drawer(), // lo popoleremo dopo
          body: const Center(child: Text('Contenuto della Home')),
        );
      }
    }

    Risultato atteso

    Avviando l'app vedi l'icona hamburger nella AppBar; toccandola si apre un pannello laterale (per ora vuoto).

  2. 2

    Creare l'header del Drawer

    Un buon Drawer inizia spesso con un header che mostra le informazioni dell'utente. Flutter offre DrawerHeader e UserAccountsDrawerHeader: quest'ultimo è pensato proprio per mostrare nome, email e avatar.

    Creiamo un widget dedicato per l'header, così da mantenere il codice ordinato.

    class DrawerHeaderWidget extends StatelessWidget {
      const DrawerHeaderWidget({super.key});
    
      @override
      Widget build(BuildContext context) {
        return UserAccountsDrawerHeader(
          decoration: BoxDecoration(
            color: Theme.of(context).colorScheme.primary,
          ),
          accountName: const Text('Mario Rossi'),
          accountEmail: const Text('mario.rossi@example.com'),
          currentAccountPicture: const CircleAvatar(
            backgroundColor: Colors.white,
            child: Text('MR', style: TextStyle(fontSize: 24)),
          ),
        );
      }
    }

    Risultato atteso

    Hai un widget riutilizzabile che mostra un header colorato con avatar, nome ed email.

  3. 3

    Definire le sezioni dell'app

    Per gestire in modo pulito le voci di menu conviene modellare ogni sezione come un dato. Creiamo un enum per le sezioni e una piccola classe di supporto con titolo e icona.

    Questo approccio rende semplice aggiungere o rimuovere voci senza duplicare codice nella UI.

    enum AppSection { home, profilo, impostazioni, info }
    
    extension AppSectionData on AppSection {
      String get titolo => switch (this) {
            AppSection.home => 'Home',
            AppSection.profilo => 'Profilo',
            AppSection.impostazioni => 'Impostazioni',
            AppSection.info => 'Informazioni',
          };
    
      IconData get icona => switch (this) {
            AppSection.home => Icons.home_outlined,
            AppSection.profilo => Icons.person_outline,
            AppSection.impostazioni => Icons.settings_outlined,
            AppSection.info => Icons.info_outline,
          };
    }

    Risultato atteso

    Hai un modello dati che descrive titolo e icona di ogni sezione dell'app.

  4. 4

    Costruire le voci di menu con evidenziazione della sezione attiva

    Ora costruiamo il Drawer completo. Usiamo ListTile per ogni voce e la proprietà selected per evidenziare la sezione corrente.

    Il widget riceve la sezione attualmente selezionata e una callback onSelect per notificare al genitore quale voce è stata toccata. Chiamiamo Navigator.pop(context) per chiudere il Drawer dopo la selezione.

    class AppDrawer extends StatelessWidget {
      final AppSection selezionata;
      final ValueChanged<AppSection> onSelect;
    
      const AppDrawer({
        super.key,
        required this.selezionata,
        required this.onSelect,
      });
    
      @override
      Widget build(BuildContext context) {
        final scheme = Theme.of(context).colorScheme;
        return Drawer(
          child: ListView(
            padding: EdgeInsets.zero,
            children: [
              const DrawerHeaderWidget(),
              for (final sezione in AppSection.values)
                ListTile(
                  leading: Icon(sezione.icona),
                  title: Text(sezione.titolo),
                  selected: sezione == selezionata,
                  selectedColor: scheme.primary,
                  selectedTileColor: scheme.primaryContainer.withOpacity(0.3),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(12),
                  ),
                  onTap: () {
                    Navigator.pop(context); // chiude il drawer
                    onSelect(sezione);
                  },
                ),
              const Divider(),
              ListTile(
                leading: const Icon(Icons.logout),
                title: const Text('Esci'),
                onTap: () => Navigator.pop(context),
              ),
            ],
          ),
        );
      }
    }

    Risultato atteso

    Il Drawer mostra tutte le voci con icone; la sezione attiva è evidenziata con colore e sfondo diversi.

  5. 5

    Collegare il Drawer alla schermata e cambiare contenuto

    Trasformiamo la HomePage in uno StatefulWidget per tenere traccia della sezione selezionata. Al tocco di una voce aggiorniamo lo stato con setState, cambiando sia il titolo della AppBar sia il corpo della pagina.

    Questo pattern è perfetto quando le sezioni condividono lo stesso Scaffold. Se invece vuoi schermate completamente separate, puoi usare Navigator.push dentro onSelect.

    class HomePage extends StatefulWidget {
      const HomePage({super.key});
    
      @override
      State<HomePage> createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      AppSection _sezione = AppSection.home;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text(_sezione.titolo)),
          drawer: AppDrawer(
            selezionata: _sezione,
            onSelect: (nuova) => setState(() => _sezione = nuova),
          ),
          body: Center(
            child: Text(
              'Sei nella sezione: ${_sezione.titolo}',
              style: Theme.of(context).textTheme.titleLarge,
            ),
          ),
        );
      }
    }

    Risultato atteso

    Selezionando una voce dal Drawer, la AppBar e il contenuto centrale cambiano in base alla sezione scelta.

  6. 6

    Migliorare l'accessibilità e l'apertura programmatica

    Per completare il tutorial vediamo due accorgimenti utili.

    1. Apertura programmatica: a volte vuoi aprire il Drawer da un pulsante custom. Usa una GlobalKey<ScaffoldState> oppure Scaffold.of(context).openDrawer().

    2. Drawer a destra: puoi usare endDrawer per un menu che si apre dal lato opposto.

    Ecco un esempio con la chiave globale per aprire il Drawer manualmente.

    final _scaffoldKey = GlobalKey<ScaffoldState>();
    
    Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        automaticallyImplyLeading: false,
        title: const Text('Home'),
        leading: IconButton(
          icon: const Icon(Icons.menu),
          tooltip: 'Apri menu',
          onPressed: () => _scaffoldKey.currentState?.openDrawer(),
        ),
      ),
      drawer: AppDrawer(
        selezionata: _sezione,
        onSelect: (nuova) => setState(() => _sezione = nuova),
      ),
      body: const Center(child: Text('Contenuto')),
    );

    Risultato atteso

    Puoi aprire il Drawer tramite un pulsante personalizzato e sai come posizionarlo anche a destra con endDrawer.

CondividiXLinkedInFacebookWhatsApp

Commenti (0)

Ancora nessun commento. Inizia tu!