---
name: flutter-catalogo-productos
description: >
  Skill para el proyecto Flutter "catalogo_productos". Úsala SIEMPRE que el usuario pida
  implementar la pantalla de detalle de producto (producto_detalle_screen.dart), agregar
  navegación desde ProductoCard, o implementar un sidebar/menú lateral responsive en
  home_screen.dart. Aplica cuando el usuario mencione: "detalle de producto", "Stack con
  botón flotante", "navegar al detalle", "sidebar en desktop", "menú lateral", "LayoutBuilder
  con 900px", "barra lateral con categorías", o cualquier mejora de navegación/responsividad
  en este proyecto Flutter específico.
---

# Flutter: Pantalla de Detalle + Sidebar Responsive

Proyecto: `catalogo_productos` — Flutter con Material 3, sin paquetes externos.
Estructura: `lib/models/`, `lib/screens/`, `lib/widgets/`.

---

## Tarea 1 — `lib/screens/producto_detalle_screen.dart` (archivo nuevo)

### Árbol de widgets

```
Scaffold
└── body: CustomScrollView
    ├── SliverAppBar (expandedHeight: 350, pinned: true)
    │   └── FlexibleSpaceBar → Stack (StackFit.expand)
    │       ├── Container fondo gris + Icon grande centrado
    │       ├── Positioned bottom: gradiente negro sutil
    │       ├── Positioned top-left:   CircleAvatar → botón volver
    │       ├── Positioned top-right:  CircleAvatar → IconButton favorito (toggle)
    │       ├── Positioned top-left+:  Container rojo → badge "20% OFF"
    │       └── Positioned bottom-right: FloatingActionButton.extended "Agregar"
    └── SliverToBoxAdapter → Padding → Column
        ├── Row: Text nombre + Chip categoría
        ├── Text precio (fontSize:28, color:green)
        ├── Divider
        ├── Text "Descripción" (bold) + Text descripción larga
        ├── _buildInfoRow × 3 (stock, envío, garantía)
        ├── SizedBox(height:24)
        └── ElevatedButton "Comprar ahora" (ancho completo)
```

### Código completo

```dart
import 'package:flutter/material.dart';
import '../models/producto.dart';

class ProductoDetalleScreen extends StatefulWidget {
  final Producto producto;
  const ProductoDetalleScreen({super.key, required this.producto});

  @override
  State<ProductoDetalleScreen> createState() => _ProductoDetalleScreenState();
}

class _ProductoDetalleScreenState extends State<ProductoDetalleScreen> {
  bool _esFavorito = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[100],
      body: CustomScrollView(
        slivers: [
          SliverAppBar(
            expandedHeight: 350,
            pinned: true,
            automaticallyImplyLeading: false,
            backgroundColor: Colors.grey[200],
            flexibleSpace: FlexibleSpaceBar(
              background: _buildImagenStack(context),
            ),
          ),
          SliverToBoxAdapter(child: _buildInfoProducto(context)),
        ],
      ),
    );
  }

  Widget _buildImagenStack(BuildContext context) {
    final topPad = MediaQuery.of(context).padding.top;
    return Stack(
      fit: StackFit.expand,
      children: [
        // Fondo con icono del producto
        Container(
          color: Colors.grey[200],
          child: Center(
            child: Icon(_getIcono(widget.producto.imagenUrl),
                size: 120, color: Colors.grey[500]),
          ),
        ),
        // Gradiente inferior sutil
        Positioned(
          bottom: 0, left: 0, right: 0, height: 80,
          child: Container(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment.topCenter,
                end: Alignment.bottomCenter,
                colors: [Colors.transparent, Colors.black.withOpacity(0.15)],
              ),
            ),
          ),
        ),
        // Botón volver (top-left)
        Positioned(
          top: topPad + 8, left: 16,
          child: CircleAvatar(
            backgroundColor: Colors.white,
            child: IconButton(
              icon: const Icon(Icons.arrow_back, color: Colors.black),
              onPressed: () => Navigator.of(context).pop(),
            ),
          ),
        ),
        // Botón favorito (top-right)
        Positioned(
          top: topPad + 8, right: 16,
          child: CircleAvatar(
            backgroundColor: Colors.white,
            child: IconButton(
              icon: Icon(
                _esFavorito ? Icons.favorite : Icons.favorite_border,
                color: _esFavorito ? Colors.red : Colors.black,
              ),
              onPressed: () => setState(() => _esFavorito = !_esFavorito),
            ),
          ),
        ),
        // Badge descuento (debajo del botón volver)
        Positioned(
          top: topPad + 68, left: 16,
          child: Container(
            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
            decoration: BoxDecoration(
              color: Colors.red,
              borderRadius: BorderRadius.circular(20),
            ),
            child: const Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(Icons.local_offer, color: Colors.white, size: 14),
                SizedBox(width: 4),
                Text('20% OFF',
                    style: TextStyle(
                        color: Colors.white,
                        fontSize: 12,
                        fontWeight: FontWeight.bold)),
              ],
            ),
          ),
        ),
        // FAB "Agregar al carrito" (bottom-right)
        Positioned(
          bottom: 16, right: 16,
          child: FloatingActionButton.extended(
            heroTag: 'fab_carrito',
            onPressed: () => _snack(context, 'Agregado al carrito ✓'),
            backgroundColor: Colors.blue,
            icon: const Icon(Icons.add_shopping_cart, color: Colors.white),
            label: const Text('Agregar',
                style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
          ),
        ),
      ],
    );
  }

  Widget _buildInfoProducto(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Expanded(
                child: Text(widget.producto.nombre,
                    style: const TextStyle(
                        fontSize: 24, fontWeight: FontWeight.bold)),
              ),
              const SizedBox(width: 12),
              Chip(
                label: Text(widget.producto.categoria,
                    style: const TextStyle(fontSize: 12)),
                backgroundColor: Colors.blue[50],
                side: BorderSide(color: Colors.blue[200]!),
              ),
            ],
          ),
          const SizedBox(height: 12),
          Text('\$${widget.producto.precio.toStringAsFixed(2)}',
              style: const TextStyle(
                  fontSize: 28, fontWeight: FontWeight.bold, color: Colors.green)),
          const SizedBox(height: 16),
          const Divider(),
          const SizedBox(height: 12),
          const Text('Descripción',
              style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
          const SizedBox(height: 8),
          Text(
            '${widget.producto.descripcion}. Este producto de alta calidad ha sido '
            'diseñado para satisfacer las necesidades de los usuarios más exigentes. '
            'Con materiales premium y tecnología de última generación, garantiza '
            'un rendimiento excepcional en todas las condiciones de uso.',
            style: TextStyle(fontSize: 14, color: Colors.grey[700], height: 1.6),
          ),
          const SizedBox(height: 20),
          _buildInfoRow(Icons.inventory_2_outlined, 'Stock disponible', 'En stock (23 unidades)'),
          _buildInfoRow(Icons.local_shipping_outlined, 'Envío', 'Gratis en compras mayores a \$50'),
          _buildInfoRow(Icons.verified_outlined, 'Garantía', '12 meses de garantía oficial'),
          const SizedBox(height: 24),
          SizedBox(
            width: double.infinity,
            child: ElevatedButton.icon(
              onPressed: () => _snack(context, '¡Compra realizada! 🎉'),
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.blue,
                foregroundColor: Colors.white,
                padding: const EdgeInsets.symmetric(vertical: 16),
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(12)),
              ),
              icon: const Icon(Icons.bolt),
              label: const Text('Comprar ahora',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildInfoRow(IconData icono, String titulo, String valor) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 6),
      child: Row(
        children: [
          Icon(icono, size: 20, color: Colors.blue),
          const SizedBox(width: 12),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(titulo,
                  style: const TextStyle(fontSize: 12, color: Colors.grey)),
              Text(valor,
                  style: const TextStyle(
                      fontSize: 13, fontWeight: FontWeight.w500)),
            ],
          ),
        ],
      ),
    );
  }

  void _snack(BuildContext context, String msg) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      content: Text(msg),
      behavior: SnackBarBehavior.floating,
      duration: const Duration(seconds: 2),
    ));
  }

  IconData _getIcono(String tipo) {
    switch (tipo) {
      case 'laptop':     return Icons.laptop;
      case 'headphones': return Icons.headphones;
      case 'watch':      return Icons.watch;
      case 'camera':     return Icons.camera_alt;
      case 'keyboard':   return Icons.keyboard;
      case 'mouse':      return Icons.mouse;
      default:           return Icons.shopping_bag;
    }
  }
}
```

---

## Tarea 2 — Navegación desde `ProductoCard`

En `lib/widgets/producto_card.dart`, añadir el import y envolver el `Container` raíz:

```dart
// Añadir al inicio:
import '../screens/producto_detalle_screen.dart';

// En build(), envolver el Container existente sin modificar su interior:
@override
Widget build(BuildContext context) {
  return GestureDetector(
    onTap: () => Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => ProductoDetalleScreen(producto: producto),
      ),
    ),
    child: Container(
      // ... código existente sin cambios ...
    ),
  );
}
```

---

## Tarea 3 — Sidebar responsive en `home_screen.dart`

### Paso 1 — Renombrar y extraer el scroll

Renombrar el método existente `_buildContenidoPrincipal()` a `_buildScrollableContent()` (sin cambiar su cuerpo).

### Paso 2 — Nuevo `_buildContenidoPrincipal()` con `LayoutBuilder`

```dart
Widget _buildContenidoPrincipal() {
  return LayoutBuilder(
    builder: (context, constraints) {
      if (constraints.maxWidth >= 900) {
        return Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildSidebar(),
            Expanded(child: _buildScrollableContent()),
          ],
        );
      }
      return _buildScrollableContent();
    },
  );
}
```

### Paso 3 — Nuevo método `_buildSidebar()`

```dart
Widget _buildSidebar() {
  final categorias = ['Todos', 'Electrónica', 'Fotografía', 'Accesorios'];
  return Container(
    width: 250,
    height: double.infinity,
    decoration: BoxDecoration(
      color: Colors.white,
      boxShadow: [
        BoxShadow(
          color: Colors.grey.withOpacity(0.15),
          blurRadius: 8,
          offset: const Offset(2, 0),
        ),
      ],
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.fromLTRB(16, 20, 16, 8),
          child: Text('Categorías',
              style: TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                  color: Colors.grey[800])),
        ),
        const Divider(height: 1),
        ...categorias.map((cat) => ListTile(
              leading: Icon(_iconCategoria(cat), color: Colors.blue, size: 22),
              title: Text(cat, style: const TextStyle(fontSize: 14)),
              onTap: () {}, // aquí va el filtrado cuando lo implementes
              hoverColor: Colors.blue[50],
              shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(8)),
            )),
        const Spacer(),
        Padding(
          padding: const EdgeInsets.all(16),
          child: Text('${productosEjemplo.length} productos',
              style: TextStyle(fontSize: 12, color: Colors.grey[500])),
        ),
      ],
    ),
  );
}

IconData _iconCategoria(String cat) {
  switch (cat) {
    case 'Electrónica': return Icons.devices;
    case 'Fotografía':  return Icons.camera_alt;
    case 'Accesorios':  return Icons.headset_mic;
    default:            return Icons.grid_view;
  }
}
```

---

## Orden de implementación

1. Crear `lib/screens/producto_detalle_screen.dart`.
2. Editar `lib/widgets/producto_card.dart`: import + `GestureDetector`.
3. Editar `lib/screens/home_screen.dart`:
   - Renombrar `_buildContenidoPrincipal` → `_buildScrollableContent`.
   - Agregar nuevo `_buildContenidoPrincipal` con `LayoutBuilder`.
   - Agregar `_buildSidebar()` y `_iconCategoria()`.

## Checklist

- [ ] Nuevo archivo `lib/screens/producto_detalle_screen.dart` creado
- [ ] `Stack` en `SliverAppBar` tiene los 4 `Positioned` (volver, favorito, badge, FAB)
- [ ] Tap en `ProductoCard` navega a `ProductoDetalleScreen`
- [ ] Ancho ≥ 900px → sidebar visible + contenido en `Expanded`
- [ ] Ancho < 900px → sin sidebar, categorías horizontales intactas
- [ ] Sin nuevas dependencias en `pubspec.yaml`