LayoutBuilder y diseños adaptativos
Aprende cómo usar el widget LayoutBuilder.
Aprende cómo crear layouts que se adapten a diferentes anchos de pantalla.
Qué lograrás
Pasos
1
Introducción
Introducción
Modern apps need to work well on screens of all sizes. On this page, you'll learn how to create layouts that adapt to different screen widths. This app shows a sidebar on large screens and a navigation-based UI on small screens. Specifically, this app handles two screen sizes:
- Pantallas grandes (tablets, escritorio): Shows contact groups and contact details side-by-side.
- Pantallas pequeñas (teléfonos): Uses navigation to move between contact groups and details.
2
Crear la página de grupos de contactos
Crear la página de grupos de contactos
Primero, crea la estructura básica del widget ContactGroupsPage
para tu pantalla de grupos de contactos.
Crea lib/screens/contact_groups.dart y añade
la siguiente estructura básica:
import 'package:flutter/cupertino.dart';
class ContactGroupsPage extends StatelessWidget {
const ContactGroupsPage({super.key});
@override
Widget build(BuildContext context) {
return const CupertinoPageScaffold(
backgroundColor: CupertinoColors.extraLightBackgroundGray,
child: Center(child: Text('Contact Groups will go here')),
);
}
}
3
Crear la página de contactos
Crear la página de contactos
Del mismo modo, crea lib/screens/contacts.dart para mostrar
eventualmente contactos individuales:
import 'package:flutter/cupertino.dart';
class ContactListsPage extends StatelessWidget {
const ContactListsPage({super.key, required this.listId});
final int listId;
@override
Widget build(BuildContext context) {
return const CupertinoPageScaffold(
backgroundColor: CupertinoColors.extraLightBackgroundGray,
child: Center(child: Text('Lists of contacts will go here')),
);
}
}
El widget ContaactsListPage y el widget ContactGroupsPage son
páginas temporales que se necesitan para implementar el widget de layout adaptativo,
lo cual harás a continuación.
4
Construir la base del layout adaptativo
Construir la base del layout adaptativo
Crea lib/screens/adaptive_layout.dart
y comienza con la siguiente estructura básica:
import 'package:flutter/cupertino.dart';
import 'contact_groups.dart';
class AdaptiveLayout extends StatefulWidget {
const AdaptiveLayout({super.key});
@override
State<AdaptiveLayout> createState() => _AdaptiveLayoutState();
}
class _AdaptiveLayoutState extends State<AdaptiveLayout> {
@override
Widget build(BuildContext context) {
return const ContactGroupsPage(); // Temporary placeholder
}
}
Este es un StatefulWidget porque el layout adaptativo gestionará
eventualmente qué grupo de contactos está seleccionado actualmente.
A continuación, añade la lógica de detección de tamaño de pantalla a lib/screens/adaptive_layout.dart:
import 'package:flutter/cupertino.dart';
import 'contact_groups.dart';
const largeScreenMinWidth = 600;
class AdaptiveLayout extends StatefulWidget {
const AdaptiveLayout({super.key});
@override
State<AdaptiveLayout> createState() => _AdaptiveLayoutState();
}
class _AdaptiveLayoutState extends State<AdaptiveLayout> {
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final isLargeScreen = constraints.maxWidth > largeScreenMinWidth;
if (isLargeScreen) {
return const Text('Large screen layout'); // Temporary
} else {
return const ContactGroupsPage();
}
},
);
}
}
El widget LayoutBuilder proporciona información sobre
las restricciones de tamaño del padre.
En el callback builder, recibes un objeto BoxConstraints
que te indica
el ancho y el alto máximos disponibles.
Al comprobar si constraints.maxWidth > largeScreenMinWidth,
puedes decidir qué layout mostrar.
El umbral de 600 píxeles es un punto de ruptura común que
separa las pantallas de tamaño de teléfono de las de tamaño de tablet.
5
Actualizar la aplicación principal
Actualizar la aplicación principal
Actualiza main.dart para usar el layout adaptativo,
de modo que puedas ver tus cambios:
import 'package:flutter/cupertino.dart';
import 'data/contact_group.dart';
import 'screens/adaptive_layout.dart';
final contactGroupsModel = ContactGroupsModel();
void main() {
runApp(const RolodexApp());
}
class RolodexApp extends StatelessWidget {
const RolodexApp({super.key});
@override
Widget build(BuildContext context) {
return const CupertinoApp(
title: 'Rolodex',
theme: CupertinoThemeData(
barBackgroundColor: CupertinoDynamicColor.withBrightness(
color: Color(0xFFF9F9F9),
darkColor: Color(0xFF1D1D1D),
),
),
home: AdaptiveLayout(),
);
}
}
Si lo estás ejecutando en Chrome, puedes cambiar el tamaño de la ventana del navegador para ver los cambios de layout.
6
Añadir funcionalidad de selección de lista
Añadir funcionalidad de selección de lista
El layout de pantalla grande necesita rastrear qué grupo de contactos está seleccionado.
Actualiza el objeto state en lib/screens/adaptive_layout.dart
con el siguiente código:
import 'package:flutter/cupertino.dart';
import 'contact_groups.dart';
const largeScreenMinWidth = 600;
class AdaptiveLayout extends StatefulWidget {
const AdaptiveLayout({super.key});
@override
State<AdaptiveLayout> createState() => _AdaptiveLayoutState();
}
class _AdaptiveLayoutState extends State<AdaptiveLayout> {
int selectedListId = 0;
void _onContactListSelected(int listId) {
setState(() {
selectedListId = listId;
});
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final isLargeScreen = constraints.maxWidth > largeScreenMinWidth;
if (isLargeScreen) {
return const Text('Large screen layout');
} else {
return const ContactGroupsPage();
}
},
);
}
}
La variable selectedListId rastrea el grupo de contactos actualmente seleccionado,
y _onContactListSelected actualiza este valor cuando el usuario realiza una elección.
7
Construir el layout para pantalla grande
Construir el layout para pantalla grande
Ahora, implementa el layout lado a lado para pantallas grandes en lib/screens/adaptive_layout.dart.
Primero, reemplaza el texto temporal con un widget que
contenga el layout adecuado.
import 'package:flutter/cupertino.dart';
import 'contact_groups.dart';
const largeScreenMinWidth = 600;
class AdaptiveLayout extends StatefulWidget {
const AdaptiveLayout({super.key});
@override
State<AdaptiveLayout> createState() => _AdaptiveLayoutState();
}
class _AdaptiveLayoutState extends State<AdaptiveLayout> {
int selectedListId = 0;
void _onContactListSelected(int listId) {
setState(() {
selectedListId = listId;
});
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final isLargeScreen = constraints.maxWidth > largeScreenMinWidth;
if (isLargeScreen) {
return _buildLargeScreenLayout();
} else {
// For small screens, use the original, navigation-style approach.
return const ContactGroupsPage();
}
},
);
}
Widget _buildLargeScreenLayout() {
return const CupertinoPageScaffold(
backgroundColor: CupertinoColors.extraLightBackgroundGray,
child: SafeArea(child: Row(children: [Text('Sidebar'), Text('Details')])),
);
}
}
El layout de pantalla grande utiliza un Row para
colocar la barra lateral y los detalles lado a lado.
SafeArea garantiza que el contenido no se superponga con
elementos de la UI del sistema, como la barra de estado.
Ahora, establece los tamaños de los dos paneles y
añade un divisor visual en lib/screens/adaptive_layout.dart:
Widget _buildLargeScreenLayout() {
return CupertinoPageScaffold(
backgroundColor: CupertinoColors.extraLightBackgroundGray,
child: SafeArea(
child: Row(
children: [
const SizedBox(width: 320, child: Text('Sidebar placeholder')),
Container(width: 1, color: CupertinoColors.separator),
const Expanded(child: Text('Details placeholder')),
],
),
),
);
}
Este layout crea lo siguiente:
- Una barra lateral de ancho fijo (320 píxeles) para grupos de contactos.
- Un divisor de 1 píxel entre los paneles.
- Un panel de detalles que utiliza un widget
Expandedpara ocupar el espacio restante.
8
Probar el layout adaptativo
Probar el layout adaptativo
Usa Hot Reload en tu aplicación y prueba el comportamiento responsivo. Si la estás ejecutando en Chrome, puedes cambiar el tamaño de la ventana del navegador para ver el cambio de layout:
- Ventana ancha (> 600px): Muestra texto provisional para la barra lateral y los detalles lado a lado.
- Ventana estrecha (< 600px): Muestra solo la página de grupos de contactos.
Tanto la barra lateral como el área de contenido principal muestran texto provisional por ahora.
En la siguiente lección, implementarás slivers para completar el contenido de la lista de contactos.
9
Revisión
Revisión
Qué lograste
Aquí tienes un resumen de lo que construiste y aprendiste en esta lección.Layouts responsivos creados con LayoutBuilder
LayoutBuilder proporciona las restricciones de tamaño del padre en su callback builder. Al comprobar
constraints.maxWidth, puedes decidir qué layout mostrar en función del espacio disponible.
Tamaño de pantalla detectado para elegir diferentes layouts
Utilizaste un punto de ruptura de 600 píxeles para distinguir las pantallas de tamaño de teléfono de las de tamaño de tablet. Este umbral común ayuda a tu aplicación a adaptar su UI para brindar la mejor experiencia en cada dispositivo.
Construcción de una barra lateral y un layout de detalles para pantallas grandes
En pantallas grandes, mostraste una barra lateral de ancho fijo y un panel de detalles Expanded lado a lado usando un
Row. Este patrón clásico maximiza el espacio útil de la pantalla en tablets y escritorios.
10
Ponte a prueba
Ponte a prueba
Quiz de Layout Adaptativo
1 / 2El sistema operativo del dispositivo y la orientación de la pantalla.
No exactamente.
LayoutBuilder proporciona restricciones de tamaño, no información del sistema operativo o de la orientación.
Las restricciones de tamaño del padre, incluyendo el ancho y el alto máximos.
¡Así es!
El builder de LayoutBuilder recibe BoxConstraints que te indican el espacio disponible del padre.
El número de widgets hijos en el árbol.
No exactamente.
LayoutBuilder proporciona restricciones de layout, no información sobre el árbol de widgets.
Los colores y la tipografía del tema actual.
No exactamente.
Theme data viene de Theme.of(context), no de LayoutBuilder.
Column
No exactamente.
Column organiza los widgets verticalmente, no lado a lado.
Stack
No exactamente.
Stack superpone widgets unos encima de otros, no lado a lado.
ListView
No exactamente.
ListView es para listas con desplazamiento, no para layouts lado a lado.
Row
¡Así es!
Row organiza a sus hijos horizontalmente, lo que lo hace ideal para colocar una barra lateral y un panel de detalles lado a lado.
A menos que se indique lo contrario, la documentación de este sitio refleja Flutter 3.44.0. Página actualizada por última vez el 2026-05-05. Ver código fuente oreportar un problema.