Vista previa — comportamiento adaptativo

📱 Móvil (< 600px) — Bottom Navigation
NutriTrack Pro 🔔
Hoy, 16 jun 2026
Banco Santander · 345 empleados activos
Calorías
1.840 / 2.200
Proteína
98 / 120 g
Carbos
210 / 280 g
Agua
1.8 / 2.5 L
REGISTRO RÁPIDO
Alimento...
Almuerzo
+ Añadir
Dashboard
Comidas
Stats
Perfil
🖥️ Tablet / Desktop (≥ 600px) — Navigation Rail
NT
NutriTrack Pro
Dashboard
Comidas
Estadísticas
Perfil
Hoy, 16 jun 2026
Banco Santander · 345 empleados activos
Calorías
1.840 / 2.200 kcal
Proteína
98 / 120 g
Carbohidratos
210 / 280 g
Agua
1,8 / 2,5 L
Grasas
55 / 70 g
Fibra
22 / 30 g
Registro rápido — Añadir comida
Alimento...
g / ml
Almuerzo ▾
+ Añadir

Guías aplicadas — Do's & Don'ts

USAR
📐

LayoutBuilder

Evalúa constraints.maxWidth para decidir qué árbol de widgets devolver. Mide el espacio real disponible, no el dispositivo.

USAR
📦

ConstrainedBox + Center

Envuelve formularios y listas en ConstrainedBox(maxWidth: 560) centrado para evitar estiramiento en pantallas grandes.

USAR
🔲

GridView.builder lazy

Con SliverGridDelegateWithMaxCrossAxisExtent para que el número de columnas se ajuste automáticamente al ancho disponible.

EVITAR
🔄

MediaQuery.orientationOf

No uses la orientación del dispositivo para cambiar layouts. Un iPad en landscape puede estar en modo split-screen con poco ancho.

EVITAR
📱

Detección de hardware

No compruebes "es phone" vs "es tablet". Flutter corre en ventanas redimensionables. Usa siempre el ancho disponible real.

EVITAR
🔒

Bloquear orientación

Bloquear la orientación causa letterboxing en dispositivos plegables y viola los requisitos de Android Large Format Tiers.

Código clave — DashboardScreen

dashboard_screen.dart Dart
// LayoutBuilder detecta espacio disponible
// Breakpoint único: 600px
const double kLargeScreenMinWidth = 600.0;

@override
Widget build(BuildContext context) {
  return LayoutBuilder(
    builder: (context, constraints) {
      final bool isLarge =
        constraints.maxWidth > kLargeScreenMinWidth;

      if (isLarge) {
        return _buildLargeLayout();
      } else {
        return _buildSmallLayout();
      }
    },
  );
}
large_layout.dart Dart
// Tablet/Desktop: NavigationRail lateral
Widget _buildLargeLayout() {
  return Scaffold(
    body: Row(
      children: [
        // Sidebar 200px extendido
        NavigationRail(
          selectedIndex: _selectedIndex,
          extended: true,
          minExtendedWidth: 200,
          destinations: _railDestinations,
          onDestinationSelected:
            (i) => setState(
              () => _selectedIndex = i),
        ),
        const VerticalDivider(width: 1),
        // Contenido ocupa el resto
        Expanded(child: _buildContent()),
      ],
    ),
  );
}
metrics_grid.dart Dart
// GridView lazy con columnas automáticas
GridView.builder(
  shrinkWrap: true,
  physics: const NeverScrollableScrollPhysics(),
  itemCount: metrics.length,
  gridDelegate: const
    SliverGridDelegateWithMaxCrossAxisExtent(
      maxCrossAxisExtent: 220,
      crossAxisSpacing: 12,
      mainAxisSpacing: 12,
      childAspectRatio: 1.3,
    ),
  itemBuilder: (ctx, i) =>
    MetricCard(metric: metrics[i]),
)
constrained_form.dart Dart
// Formulario centrado con maxWidth
// Evita estiramiento en desktop
Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 560.0,
    ),
    child: const QuickLogForm(),
  ),
)

// Perfil también restringido
Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 480.0,
    ),
    child: const ProfileTab(),
  ),
)

Flujo de trabajo — Construcción del layout adaptativo

1

Identificar el widget raíz que necesita comportamiento adaptativo

En NutriTrack Pro, el widget es DashboardScreen. Necesita mostrar NavigationRail (tablet) o NavigationBar (móvil) según el espacio disponible.

2

Envolver en LayoutBuilder y extraer constraints.maxWidth

return LayoutBuilder(builder: (context, constraints) { ... });
3

Definir el breakpoint como constante global

const double kLargeScreenMinWidth = 600.0;
4

Devolver árbol distinto según breakpoint

Si maxWidth > 600: Row con NavigationRail + Expanded(content). Si no: Scaffold con body + NavigationBar bottom.

5

Optimizar listas: GridView.builder con SliverGridDelegateWithMaxCrossAxisExtent

Las métricas pasan de 2 columnas en móvil a 3-4 en tablet automáticamente, sin condicionales adicionales.

6

Restringir formularios y perfiles con ConstrainedBox + Center

maxWidth: 560 para el formulario de registro, maxWidth: 480 para el perfil. Evitan estiramiento en desktop sin romper el responsive.

7

Validar: redimensionar la ventana y revisar transiciones de layout

Probar en Flutter DevTools arrastrando el tamaño de ventana. Verificar sin errores de overflow. Confirmar lazy rendering de la lista.