Perché il responsive design conta in Flutter
Flutter permette di distribuire una singola codebase su smartphone, tablet, desktop e web. Ma un layout pensato per uno schermo da 5 pollici raramente funziona bene su un monitor da 27". Per questo è fondamentale costruire interfacce responsive (che si adattano allo spazio disponibile) e adattive (che cambiano comportamento a seconda della piattaforma).
In questa guida vedremo gli strumenti principali offerti dal framework per gestire la questione in modo pulito e scalabile.
MediaQuery: conoscere le dimensioni dello schermo
MediaQuery fornisce informazioni sul dispositivo: dimensioni, orientamento, densità dei pixel, padding di sistema (notch, barre di navigazione).
@override
Widget build(BuildContext context) {
final size = MediaQuery.sizeOf(context);
final orientation = MediaQuery.orientationOf(context);
return Text('Larghezza: ${size.width}, orientamento: $orientation');
}
Da Flutter 3.10 sono disponibili i metodi MediaQuery.sizeOf(context) e MediaQuery.orientationOf(context): sono più efficienti di MediaQuery.of(context) perché il widget viene ricostruito solo quando cambia la specifica proprietà osservata, non l'intero MediaQueryData.
LayoutBuilder: adattarsi allo spazio del genitore
Mentre MediaQuery guarda l'intero schermo, LayoutBuilder fornisce i vincoli imposti dal widget genitore. È lo strumento ideale per componenti riutilizzabili che devono adattarsi al contenitore in cui vengono inseriti.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const _LayoutMobile();
} else if (constraints.maxWidth < 1200) {
return const _LayoutTablet();
} else {
return const _LayoutDesktop();
}
},
)
Definire i breakpoint
Un approccio ordinato consiste nel centralizzare i breakpoint in una classe dedicata, così da riutilizzarli in tutta l'app.
class Breakpoints {
static const double mobile = 600;
static const double tablet = 1200;
}
enum DeviceType { mobile, tablet, desktop }
DeviceType getDeviceType(double width) {
if (width < Breakpoints.mobile) return DeviceType.mobile;
if (width < Breakpoints.tablet) return DeviceType.tablet;
return DeviceType.desktop;
}
Widget flessibili: Flexible ed Expanded
All'interno di Row e Column, Expanded e Flexible distribuiscono lo spazio in proporzione, evitando valori di larghezza fissi.
Row(
children: [
Expanded(flex: 2, child: SidebarWidget()),
Expanded(flex: 5, child: ContentWidget()),
],
)
Usa FractionallySizedBox quando vuoi che un figlio occupi una frazione dello spazio disponibile:
FractionallySizedBox(
widthFactor: 0.8,
child: Card(child: Text('Occupo l\'80% della larghezza')),
)
Un esempio pratico: lista che diventa griglia
Un pattern comune è mostrare una lista su mobile e una griglia su schermi più larghi.
LayoutBuilder(
builder: (context, constraints) {
final crossAxisCount = (constraints.maxWidth / 300).floor().clamp(1, 4);
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
),
itemCount: prodotti.length,
itemBuilder: (context, index) => ProductCard(prodotti[index]),
);
},
)
In alternativa, SliverGridDelegateWithMaxCrossAxisExtent calcola automaticamente il numero di colonne in base a una larghezza massima desiderata per cella.
Layout adattivi con i widget di Material
Flutter offre widget pensati per grandi schermi. NavigationRail sostituisce la BottomNavigationBar su tablet e desktop.
Scaffold(
body: Row(
children: [
if (isWideScreen)
NavigationRail(
selectedIndex: _index,
onDestinationSelected: (i) => setState(() => _index = i),
destinations: const [
NavigationRailDestination(icon: Icon(Icons.home), label: Text('Home')),
NavigationRailDestination(icon: Icon(Icons.settings), label: Text('Impostazioni')),
],
),
Expanded(child: _pagine[_index]),
],
),
bottomNavigationBar: isWideScreen ? null : BottomNavigationBar(/* ... */),
)
Best practice
- Evita dimensioni fisse in pixel: preferisci
Expanded,Flexiblee percentuali. - Usa
LayoutBuilderper i componenti,MediaQueryper decisioni a livello di schermata. - Gestisci il testo con
FittedBoxoAutoSizeTextper evitare overflow. - Testa su più dimensioni con Device Preview o semplicemente ridimensionando la finestra su desktop e web.
- Rispetta i SafeArea avvolgendo i contenuti in
SafeAreaper evitare notch e barre di sistema.
Conclusione
Costruire UI responsive in Flutter non richiede librerie esterne: MediaQuery, LayoutBuilder e i widget flessibili coprono la maggior parte dei casi d'uso. Centralizzando i breakpoint e ragionando in termini di spazio disponibile anziché di pixel fissi, otterrai app che appaiono curate su qualsiasi dispositivo, dallo smartphone al desktop.
