Con Flutter puoi rilasciare la stessa app su mobile, desktop e web, ma una UI pensata solo per il telefono risulta scomoda su schermi ampi. In questo tutorial creeremo una shell di navigazione adattiva che mostra una NavigationBar in basso su dispositivi stretti e una NavigationRail laterale su schermi larghi. Useremo esclusivamente widget del framework, senza pacchetti esterni, applicando le linee guida di Material 3. Al termine avrai uno scaffold riutilizzabile per qualsiasi app multi-piattaforma.
Foto di Josh Davies su Unsplash
Costruire una UI adattiva per desktop e web in Flutter con NavigationRail
- 1
Definire i destination della navigazione
Per evitare duplicazione tra
NavigationBareNavigationRail, definiamo una sola volta le voci di navigazione in una struttura dati condivisa. Creiamo una classe semplice con etichetta, icona e icona selezionata.class NavDestinationData { const NavDestinationData({ required this.label, required this.icon, required this.selectedIcon, }); final String label; final IconData icon; final IconData selectedIcon; } const destinations = <NavDestinationData>[ NavDestinationData( label: 'Home', icon: Icons.home_outlined, selectedIcon: Icons.home, ), NavDestinationData( label: 'Cerca', icon: Icons.search_outlined, selectedIcon: Icons.search, ), NavDestinationData( label: 'Profilo', icon: Icons.person_outline, selectedIcon: Icons.person, ), ];Risultato atteso
Una lista costante di destinazioni riutilizzabile da entrambi i widget di navigazione.
- 2
Creare lo scaffold adattivo con LayoutBuilder
Usiamo un
LayoutBuilderper decidere quale layout mostrare in base alla larghezza disponibile. Consideriamo una soglia di 600px: sotto usiamo laNavigationBarin basso, sopra laNavigationRaila sinistra. Manteniamo l'indice selezionato in unoStatefulWidget.class AdaptiveScaffold extends StatefulWidget { const AdaptiveScaffold({super.key, required this.pages}); final List<Widget> pages; @override State<AdaptiveScaffold> createState() => _AdaptiveScaffoldState(); } class _AdaptiveScaffoldState extends State<AdaptiveScaffold> { int _selectedIndex = 0; void _onSelect(int index) { setState(() => _selectedIndex = index); } @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final isWide = constraints.maxWidth >= 600; final body = widget.pages[_selectedIndex]; if (isWide) { return _buildWideLayout(body); } return _buildNarrowLayout(body); }, ); } }Risultato atteso
Lo scaffold sa distinguere tra schermo stretto e largo, ma i due layout vanno ancora implementati.
- 3
Layout stretto con NavigationBar
Su schermi stretti (mobile) usiamo la classica
NavigationBardi Material 3 posizionata in basso. Convertiamo le nostreNavDestinationDatainNavigationDestination.Widget _buildNarrowLayout(Widget body) { return Scaffold( appBar: AppBar(title: Text(destinations[_selectedIndex].label)), body: body, bottomNavigationBar: NavigationBar( selectedIndex: _selectedIndex, onDestinationSelected: _onSelect, destinations: [ for (final d in destinations) NavigationDestination( icon: Icon(d.icon), selectedIcon: Icon(d.selectedIcon), label: d.label, ), ], ), ); }Risultato atteso
Riducendo la finestra sotto i 600px compare la barra di navigazione inferiore.
- 4
Layout largo con NavigationRail
Su schermi larghi usiamo una
NavigationRaillaterale affiancata al contenuto tramite unaRow. La proprietàlabelTypemostra le etichette sotto le icone. Aggiungiamo unVerticalDividercome separatore visivo e avvolgiamo il contenuto inExpanded.Widget _buildWideLayout(Widget body) { return Scaffold( body: Row( children: [ NavigationRail( selectedIndex: _selectedIndex, onDestinationSelected: _onSelect, labelType: NavigationRailLabelType.all, leading: const Padding( padding: EdgeInsets.symmetric(vertical: 16), child: FlutterLogo(size: 32), ), destinations: [ for (final d in destinations) NavigationRailDestination( icon: Icon(d.icon), selectedIcon: Icon(d.selectedIcon), label: Text(d.label), ), ], ), const VerticalDivider(thickness: 1, width: 1), Expanded( child: Scaffold( appBar: AppBar(title: Text(destinations[_selectedIndex].label)), body: body, ), ), ], ), ); }Risultato atteso
Allargando la finestra oltre 600px la navigazione si sposta lateralmente con la rail.
- 5
Rendere la rail estesa su schermi molto larghi
Su desktop con molto spazio (oltre 1000px) possiamo mostrare la rail estesa con etichette accanto alle icone usando
extended: true. Aggiorniamo_buildWideLayoutper accettare un parametro e leggere la larghezza dalLayoutBuilder.// Nel build principale, passa la larghezza: if (isWide) { return _buildWideLayout(body, extended: constraints.maxWidth >= 1000); } // Aggiorna la firma e la NavigationRail: Widget _buildWideLayout(Widget body, {required bool extended}) { return Scaffold( body: Row( children: [ NavigationRail( extended: extended, selectedIndex: _selectedIndex, onDestinationSelected: _onSelect, labelType: extended ? null : NavigationRailLabelType.all, leading: const Padding( padding: EdgeInsets.symmetric(vertical: 16), child: FlutterLogo(size: 32), ), destinations: [ for (final d in destinations) NavigationRailDestination( icon: Icon(d.icon), selectedIcon: Icon(d.selectedIcon), label: Text(d.label), ), ], ), const VerticalDivider(thickness: 1, width: 1), Expanded( child: Scaffold( appBar: AppBar(title: Text(destinations[_selectedIndex].label)), body: body, ), ), ], ), ); }Risultato atteso
Oltre i 1000px la rail si espande mostrando le etichette accanto alle icone, come nelle app desktop moderne.
- 6
Collegare tutto nell'app
Infine forniamo le pagine reali allo scaffold e attiviamo Material 3 nel
ThemeData. Ogni pagina è un semplice widget di contenuto; puoi sostituirle con le tue schermate.void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'UI Adattiva', theme: ThemeData( useMaterial3: true, colorSchemeSeed: Colors.indigo, ), home: AdaptiveScaffold( pages: const [ Center(child: Text('Contenuto Home')), Center(child: Text('Contenuto Cerca')), Center(child: Text('Contenuto Profilo')), ], ), ); } }Risultato atteso
Avviando l'app e ridimensionando la finestra (o cambiando dispositivo) la navigazione si adatta automaticamente: NavigationBar su mobile, NavigationRail su tablet, rail estesa su desktop.
Commenti (0)
Ancora nessun commento. Inizia tu!