Saltar al contenido principal

Construir un diseño en Flutter

Aprende cómo construir un diseño en Flutter.

Este tutorial explica cómo diseñar y construir diseños en Flutter.

Si utilizas el código de ejemplo proporcionado, puedes construir la siguiente aplicación.

La aplicación terminada.

La aplicación terminada.

Foto de Dino Reichmuth en Unsplash. Texto de Switzerland Tourism.

Para obtener una mejor visión general del mecanismo de diseño, comienza con el enfoque de diseño de Flutter.

Diagramar el diseño

#

En esta sección, considera qué tipo de experiencia de usuario quieres para los usuarios de tu aplicación.

Considera cómo posicionar los componentes de tu interfaz de usuario. Un diseño consiste en el resultado final total de estos posicionamientos. Considera planificar tu diseño para acelerar tu codificación. Usar pistas visuales para saber dónde va algo en la pantalla puede ser de gran ayuda.

Usa el método que prefieras, como una herramienta de diseño de interfaz o un lápiz y una hoja de papel. Descubre dónde quieres colocar los elementos en tu pantalla antes de escribir código. Es la versión en programación del proverbio: "Mide dos veces, corta una vez".

  1. Haz estas preguntas para desglosar el diseño en sus elementos básicos.

    • ¿Puedes identificar las filas y columnas?
    • ¿El diseño incluye una cuadrícula (grid)?
    • ¿Hay elementos superpuestos?
    • ¿La interfaz de usuario necesita pestañas (tabs)?
    • ¿Qué necesitas alinear, rellenar o bordear?
  2. Identifica los elementos más grandes. En este ejemplo, organizas la imagen, el título, los botones y la descripción en una columna.

    Elementos principales en el diseño: imagen, fila, fila y bloque de texto

    Elementos principales en el diseño: imagen, fila, fila y bloque de texto

  3. Diagrama cada fila.

    1. La fila 1, la sección Title, tiene tres hijos: una columna de texto, un icono de estrella y un número. Su primer hijo, la columna, contiene dos líneas de texto. Esa primera columna podría necesitar más espacio.

      Sección de título con bloques de texto y un icono

      Sección de título con bloques de texto y un icono

    2. La fila 2, la sección Button, tiene tres hijos: cada hijo contiene una columna que a su vez contiene un icono y texto.

      La sección de botones con tres botones etiquetados

      La sección de botones con tres botones etiquetados

Después de diagramar el diseño, considera cómo lo codificarías.

¿Escribirías todo el código en una sola clase? ¿O crearías una clase para cada parte del diseño?

Para seguir las mejores prácticas de Flutter, crea una clase, o Widget, para contener cada parte de tu diseño. Cuando Flutter necesita volver a renderizar parte de una interfaz de usuario, actualiza la parte más pequeña que cambia. Es por esto que Flutter hace que "todo sea un widget". Si solo cambia el texto en un widget Text, Flutter redibuja únicamente ese texto. Flutter cambia la menor cantidad posible de la interfaz de usuario en respuesta a la entrada del usuario.

Para este tutorial, escribe cada elemento que hayas identificado como su propio widget.

Crear el código base de la aplicación

#

En esta sección, bosqueja el código básico de la aplicación Flutter para comenzar tu aplicación.

  1. Configura tu entorno de Flutter.

  2. Crea una nueva aplicación de Flutter.

  3. Reemplaza el contenido de lib/main.dart con el siguiente código. Esta aplicación usa un parámetro para el título de la aplicación y el título mostrado en la appBar de la aplicación. Esta decisión simplifica el código.

    dart
    import 'package:flutter/material.dart';
    
    void main() => runApp(const MyApp());
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        const String appTitle = 'Flutter layout demo';
        return MaterialApp(
          title: appTitle,
          home: Scaffold(
            appBar: AppBar(title: const Text(appTitle)),
            body: const Center(
              child: Text('Hello World'),
            ),
          ),
        );
      }
    }
    

Agregar la sección de Título

#

En esta sección, crea un widget TitleSection que se asemeje a la siguiente disposición.

La sección de título como boceto y prototipo de interfaz de usuario

La sección de título como boceto y prototipo de interfaz de usuario

Agregar el Widget TitleSection

#

Agrega el siguiente código después de la clase MyApp.

dart
class TitleSection extends StatelessWidget {
  const TitleSection({super.key, required this.name, required this.location});

  final String name;
  final String location;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(32),
      child: Row(
        children: [
          Expanded(
            /*1*/
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                /*2*/
                Padding(
                  padding: const EdgeInsets.only(bottom: 8),
                  child: Text(
                    name,
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                ),
                Text(location, style: TextStyle(color: Colors.grey[500])),
              ],
            ),
          ),
          /*3*/
          Icon(Icons.star, color: Colors.red[500]),
          const Text('41'),
        ],
      ),
    );
  }
}
  1. Para usar todo el espacio libre restante en la fila, usa el widget Expanded para estirar el widget Column. Para colocar la columna al comienzo de la fila, establece la propiedad crossAxisAlignment en CrossAxisAlignment.start.
  2. Para agregar espacio entre las filas de texto, coloca esas filas en un widget Padding.
  3. La fila de título termina con un icono de estrella roja y el texto 41. Toda la fila queda dentro de un widget Padding y aplica un relleno en cada borde de 32 píxeles.

Cambiar el cuerpo de la aplicación a una vista desplazable

#

En la propiedad body, reemplaza el widget Center por un widget SingleChildScrollView. Dentro del widget SingleChildScrollView, reemplaza el widget Text por un widget Column.

dart
body: const Center(
  child: Text('Hello World'),
body: const SingleChildScrollView(
  child: Column(
    children: [

Estas actualizaciones de código cambian la aplicación de las siguientes maneras.

  • Un widget SingleChildScrollView se puede desplazar. Esto permite mostrar elementos que no caben en la pantalla actual.
  • Un widget Column muestra cualquier elemento dentro de su propiedad children en el orden indicado. El primer elemento indicado en la lista children se muestra en la parte superior de la lista. Los elementos en la lista children se muestran en orden de arreglo en la pantalla de arriba a abajo.

Actualizar la aplicación para mostrar la sección de título

#

Agrega el widget TitleSection como el primer elemento de la lista children. Esto lo coloca en la parte superior de la pantalla. Pasa el nombre y la ubicación proporcionados al constructor de TitleSection.

dart
children: [
  TitleSection(
    name: 'Oeschinen Lake Campground',
    location: 'Kandersteg, Switzerland',
  ),
],

Agregar la sección de Botones

#

En esta sección, agrega los botones que añadirán funcionalidad a tu aplicación.

La sección Button contiene tres columnas que usan el mismo diseño: un icono sobre una fila de texto.

La sección de botones como boceto y prototipo de interfaz de usuario

La sección de botones como boceto y prototipo de interfaz de usuario

Planifica distribuir estas columnas en una fila para que cada una ocupe la misma cantidad de espacio. Pinta todo el texto e iconos con el color primario.

Agregar el widget ButtonSection

#

Agrega el siguiente código después del widget TitleSection para contener el código para construir la fila de botones.

dart
class ButtonSection extends StatelessWidget {
  const ButtonSection({super.key});

  @override
  Widget build(BuildContext context) {
    final Color color = Theme.of(context).primaryColor;
    // ···
  }

}

Crear un widget para hacer botones

#

Como el código para cada columna podría usar la misma sintaxis, crea un Widget llamado ButtonWithText. El constructor del Widget acepta un color, datos de ícono y una etiqueta para el botón. Usando estos valores, el Widget construye una Column con un Icon y un Widget de Text estilizado como sus hijos. Para ayudar a separar estos hijos, un Widget Padding — el Widget Text se envuelve con un Widget Padding.

Agrega el siguiente código después de la clase ButtonSection.

dart
class ButtonSection extends StatelessWidget {
  const ButtonSection({super.key});
  // ···
}

class ButtonWithText extends StatelessWidget {
  const ButtonWithText({
    super.key,
    required this.color,
    required this.icon,
    required this.label,
  });

  final Color color;
  final IconData icon;
  final String label;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(icon, color: color),
        Padding(
          padding: const EdgeInsets.only(top: 8),
          child: Text(
            label,
            style: TextStyle(
              fontSize: 12,
              fontWeight: FontWeight.w400,
              color: color,
            ),
          ),
        ),
      ],
    );
  }
}

Posicionar los botones con un widget Row

#

Agrega el siguiente código dentro del widget ButtonSection.

  1. Agrega tres instancias del widget ButtonWithText, una para cada botón.
  2. Pasa el color, Icon y texto para ese botón específico.
  3. Alinea las columnas a lo largo del eje principal con el valor MainAxisAlignment.spaceEvenly. El eje principal para un widget Row es horizontal y el eje principal para un widget Column es vertical. Este valor, entonces, le dice a Flutter que organice el espacio libre en cantidades iguales antes, entre y después de cada columna a lo largo del Row.
dart
class ButtonSection extends StatelessWidget {
  const ButtonSection({super.key});

  @override
  Widget build(BuildContext context) {
    final Color color = Theme.of(context).primaryColor;
    return SizedBox(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          ButtonWithText(color: color, icon: Icons.call, label: 'CALL'),
          ButtonWithText(color: color, icon: Icons.near_me, label: 'ROUTE'),
          ButtonWithText(color: color, icon: Icons.share, label: 'SHARE'),
        ],
      ),
    );
  }

}

class ButtonWithText extends StatelessWidget {
  const ButtonWithText({
    super.key,
    required this.color,
    required this.icon,
    required this.label,
  });

  final Color color;
  final IconData icon;
  final String label;

  @override
  Widget build(BuildContext context) {
    return Column(
      // ···
    );
  }
}

Actualizar la aplicación para mostrar la sección de botones

#

Agrega la sección de botones a la lista children.

dart
  TitleSection(
    name: 'Oeschinen Lake Campground',
    location: 'Kandersteg, Switzerland',
  ),
  ButtonSection(),
],

Agregar la sección de Texto

#

En esta sección, agrega la descripción de texto a esta aplicación.

El bloque de texto como boceto y prototipo de interfaz de usuario

El bloque de texto como boceto y prototipo de interfaz de usuario

Agregar el widget TextSection

#

Agrega el siguiente código como un widget separado después del widget ButtonSection.

dart
class TextSection extends StatelessWidget {
  const TextSection({super.key, required this.description});

  final String description;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(32),
      child: Text(description, softWrap: true),
    );
  }
}

Al establecer softWrap en true, las líneas de texto llenan el ancho de la columna antes de ajustarse en el límite de una palabra.

Actualizar la aplicación para mostrar la sección de texto

#

Agrega un nuevo widget TextSection como hijo después de ButtonSection. Al agregar el widget TextSection, establece su propiedad description con el texto de la descripción de la ubicación.

dart
    location: 'Kandersteg, Switzerland',
  ),
  ButtonSection(),
  TextSection(
    description:
        'Lake Oeschinen lies at the foot of the Blüemlisalp in the '
        'Bernese Alps. Situated 1,578 meters above sea level, it '
        'is one of the larger Alpine Lakes. A gondola ride from '
        'Kandersteg, followed by a half-hour walk through pastures '
        'and pine forest, leads you to the lake, which warms to 20 '
        'degrees Celsius in the summer. Activities enjoyed here '
        'include rowing, and riding the summer toboggan run.',
  ),
],

Agregar la sección de Imagen

#

En esta sección, agrega el archivo de imagen para completar tu diseño.

Configurar tu aplicación para usar las imágenes suministradas

#

Para configurar tu aplicación para hacer referencia a imágenes, modifica su archivo pubspec.yaml.

  1. Crea un directorio images en la raíz del proyecto.

  2. Descarga la imagen lake.jpg y agrégala al nuevo directorio images.

  3. Para incluir imágenes, agrega una etiqueta assets al archivo pubspec.yaml en el directorio raíz de tu aplicación. Cuando agregas assets, este sirve como el conjunto de punteros a las imágenes disponibles para tu código.

    pubspec.yaml
    yaml
    flutter:
      uses-material-design: true
      assets:
        - images/lake.jpg
    

Crear el widget ImageSection

#

Define el siguiente widget ImageSection después de las otras declaraciones.

dart
class ImageSection extends StatelessWidget {
  const ImageSection({super.key, required this.image});

  final String image;

  @override
  Widget build(BuildContext context) {
    return Image.asset(image, width: 600, height: 240, fit: BoxFit.cover);
  }
}

El valor BoxFit.cover le dice a Flutter que muestre la imagen con dos restricciones. Primero, mostrar la imagen tan pequeña como sea posible. Segundo, cubrir todo el espacio asignado por el diseño, llamado la caja de renderizado (render box).

Actualizar la aplicación para mostrar la sección de imagen

#

Agrega un widget ImageSection como el primer hijo en la lista children. Establece la propiedad image en la ruta de la imagen que agregaste en Configurar tu aplicación para usar las imágenes suministradas.

dart
children: [
  ImageSection(
    image: 'images/lake.jpg',
  ),
  TitleSection(
    name: 'Oeschinen Lake Campground',
    location: 'Kandersteg, Switzerland',

¡Felicitaciones!

#

¡Eso es todo! Cuando realices un Hot Reload de la aplicación, tu aplicación debería verse así.

La aplicación terminada

La aplicación terminada

Recursos

#

Puedes acceder a los recursos utilizados en este tutorial desde estas ubicaciones:

Dart code: main.dart
Image: ch-photo
Pubspec: pubspec.yaml

Siguientes pasos

#

Para agregar interactividad a este diseño, sigue el tutorial de interactividad.