Layouts en Flutter

El corazón del mecanismo de layout de Flutter son los widgets. En Flutter, casi todo es un widget—incluso los modelos de layout son widgets. Las imágenes, iconos, y texto que ves en una app Flutter, son todo widgets. Pero cosas que no ves también son widgets, como son filas, columnas, y cuadrículas que organizan, restringen, y alinean los widgets visibles.

Creas un layout mediante la composición de widgets para construir widgets más complejos. Por ejemplo, la primera captura de pantalla abajo muestra tres iconos con una etiqueta sobre cada uno de ellos:

Sample layout Sample layout with visual debugging

La segunda captura de pantalla muestra el layout visualmente, mostrando una fila de 3 columnas donde cada columna contiene un icono y una etiqueta.

Aquí está un diagrama del árbol de widgets para este UI:

Node tree

La mayoría de esto podría verse como podría esperarse, pero puede que te estes preguntando sobre los Container (mostrados en rosa). Container es una clase widget que te permite personalizar su widget hijo. Usa un Container cuando quieras añadir padding, márgenes, bordes, o color de fondo, por nombrar algunas de sus capacidades.

En este ejemplo, cada widget Text se situa en un Container para añadir márgenes. La fila entera, Row, también esta colocada en un Container para añadir padding alrededor de la fila.

El resto del UI en este ejemplo es controlado por propiedades. Fija un color para Icon usando su propiedad color. Us la propiedad Text.style para fijar la fuente, su color, tamaño, y así sucesivamente. Columns y rows tienen propiedades que te permiten especificar como se alinearán sus hijos vertical u horizontalmente, y cuanto espacio deben ocupar los hijos.

Da layout a un widget

¿Cómo puedes configurar el layout de un único widget? Esta sección te enseña como crear y mostrar un simple widget. También muestra el código completo para una app Hello World simple.

En Flutter, solo toma unos pocos pasos poner un texto, un icono, o una imagen en la pantalla.

1. Selecciona un widget de layout

Elige entre una variedad de widgets de layout basándote en como quieres alinear o restringir el widget visible, ya que estas características son normalmente pasadas al widget contenido.

Este ejemplo usa Center el cual centra su contenido horizontal y verticalmente.

2. Crea un widget visible

Por ejemplo, crea un widget Text:

Text('Hello World'),

Crea un widget Image:

Image.asset(
  'images/lake.jpg',
  fit: BoxFit.cover,
),

Crea un widget Icon:

Icon(
  Icons.star,
  color: Colors.red[500],
),

3. Añade el widget visible al widget de layout

Todos los widgets de layout tiene alguna de las siguientes:

  • Una propiedad child que toma un único hijo – por ejemplo, Center o Container
  • Una propiedad children que toma una lista de widgets – por ejemplo, Row, Column, ListView, o Stack.

Añade un widget Text al widget Center:

Center(
  child: Text('Hello World'),
),

4. Añade el widget de layout a la página

Una app Flutter es en si misma un widget, y la mayoría de los widgets tienen un método build(). Instanciar y devolver un widget en el método build() de la app muestra el widget.

Apps Material

Para una app Material, puedes usar un widget Scaffold; este proporciona un banner por defecto, color de fondo, y tiene una API para añadir drawers, snack bars, y bottom sheets. Entonces puedes añadir el widget Center directamente a la propiedad body para la página principal.

lib/main.dart (MyApp)
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter layout demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter layout demo'),
        ),
        body: Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

Apps No-Material

Para una app no-Material, puedes añadir el widget Center al método build de la app:

lib/main.dart (MyApp)
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(color: Colors.white),
      child: Center(
        child: Text(
          'Hello World',
          textDirection: TextDirection.ltr,
          style: TextStyle(
            fontSize: 32,
            color: Colors.black87,
          ),
        ),
      ),
    );
  }
}

Por defecto una app no-Material no incluye un AppBar, título, o color de fondo. Si quieres estas características en una app no-Material, tienes que construirlas tu mismo. Esta app cambia el color de fondo a blanco y el texto a gris oscuro para imitar una app Material.

¡Esto es todo! Cuando ejecutes la app, debes ver Hello World.

Código fuente de la App:

Hello World

Organiza multiples widgets vertical y horizontalmente

Uno de los patrones más comunes de layout es organizar los widgets vertical u horizontalmente. Puedes usar un widget Row para organizar widgets horizontalmente, y un widget Column para organizar widgets verticalmente.

Para crear una fila o columna en Flutter, añades una lista de widgets hijo a un widget Row o Column. Sucesivamente, cada hijo puede ser en si mismo una fila o columna. El siguiente ejemplo muestra como es posible anidar filas o columnas dentro de filas o columnas.

Este layout está organizado como un Row. La fila contiene dos hijos: una columna en la izquierda, y una imagen en la derecha:

Screenshot with callouts showing the row containing two children

El árbol de widgets de la columna izquierda anida filas y columnas.

Diagram showing a left column broken down to its sub-rows and sub-columns

Implementarás algo del layout del código de Pavlova en Anidando filas y columnas.

Alineación de widgets

Controlas como una fila o columna alinea sus hijos usando las propiedades mainAxisAlignment y crossAxisAlignment. Para una fila, el eje principal corre horizontalmente y el eje transversal corre verticalmente. Para una columna, el eje principal corre verticalmente y el eje transversal corre horizontalmente.

Diagram showing the main axis and cross axis for a row Diagram showing the main axis and cross axis for a column

Las clases MainAxisAlignment y CrossAxisAlignment ofrecen una variedad de constantes para controlar la alineación.

En el siguiente ejemplo, cada una de las 3 imágenes tiene 100 pixels de ancho. La caja de renderizado (en este caso, la pantalla entera) tiene más de 300 pixeles de ancho, entonces fijando la alineación del eje principal a spaceEvenly divide el espacio libre horizontal igualitariamente entre, antes, y después de cada imagen.

Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      Image.asset('images/pic1.jpg'),
      Image.asset('images/pic2.jpg'),
      Image.asset('images/pic3.jpg'),
    ],
  );

Row with 3 evenly spaced images

App source: row_column

Las columnas trabajan de la misma manera que las filas. El siguiente ejemplo muestra una columna de 3 imágenes, cada una de 100 pixels de alto. la altura de la caja de renderizado (en este caso, la pantalla entera) tiene más de 300 pixels, entonces fijando la alineación en el eje principal a spaceEvenly divide el espacio libre vertical igualitariamente entre, por encima, y por debajo de cada imagen.

Column(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      Image.asset('images/pic1.jpg'),
      Image.asset('images/pic2.jpg'),
      Image.asset('images/pic3.jpg'),
    ],
  );

App source: row_column

Column showing 3 images spaced evenly

Dimensionando widgets

Cuando un layout es demasiado grande para ajustarse al dispositivo, una franja con un patrón de rayas amarillas y negras aparece a lo largo del limite afectado. Aquí hay un ejemplo de una fila que es demasiado ancha:

Overly-wide row

Los widgtets pueden ser dimensionados para encajar dentro de una fila o columna usando el widget Expanded. Para solucionar el ejemplo anterior donde una fila de imágenes es demasiado ancha para su caja de renderizado, envuelve cada imagen con un widget Expanded.

Row(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      Expanded(
        child: Image.asset('images/pic1.jpg'),
      ),
      Expanded(
        child: Image.asset('images/pic2.jpg'),
      ),
      Expanded(
        child: Image.asset('images/pic3.jpg'),
      ),
    ],
  );

Row of 3 images that are too wide, but each is constrained to take only 1/3 of the space

App source: sizing

Talvez quieras que un widget ocupe el doble de espacio que sus hermanos. Para esto, usa la propiedad flex del widget Expanded, un entero que determina el factor flex para un widget. El factor flex por defecto es 1. El siguiente código fija el factor flex de la imagen de enmedio en 2:

Row(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      Expanded(
        child: Image.asset('images/pic1.jpg'),
      ),
      Expanded(
        flex: 2,
        child: Image.asset('images/pic2.jpg'),
      ),
      Expanded(
        child: Image.asset('images/pic3.jpg'),
      ),
    ],
  );

Row of 3 images with the middle image twice as wide as the others

App source: sizing

Empaquetar widgets

Por defecto, una fila o columna ocupa tanto espacio como sea posible a lo largo de su eje principal, pero si quieres empaquetar los hijos todos juntos, fija su mainAxisSize a MainAxisSize.min. El siguiente ejemplo usa esta propiedad para empaquetar los iconos de estrella juntos.

Row(
    mainAxisSize: MainAxisSize.min,
    children: [
      Icon(Icons.star, color: Colors.green[500]),
      Icon(Icons.star, color: Colors.green[500]),
      Icon(Icons.star, color: Colors.green[500]),
      Icon(Icons.star, color: Colors.black),
      Icon(Icons.star, color: Colors.black),
    ],
  )

Row of 5 stars, packed together in the middle of the row

App source: pavlova

Anidar filas y columnas

El layout de flutter te permte anidar filas y columnas dentro de filas y columnas tan profundamente como necesites. Veamos el código para la sección bordeada del siguiente layout:

Screenshot of the pavlova app, with the ratings and icon rows outlined in red

La sección bordeada esta implementada como dos filas. La fila de valoraciones contiene cinco estrellas y el número de revisiones. La fila de iconos contiene tres columnas de iconos y texto.

El árbol de widget para la fila de valoraciones:

Ratings row widget tree

La variable ratings crea una fila conteniendo una fila mas pequeña de 5 iconos estrella, y texto:

var stars = Row(
  mainAxisSize: MainAxisSize.min,
  children: [
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.black),
    Icon(Icons.star, color: Colors.black),
  ],
);

final ratings = Container(
  padding: EdgeInsets.all(20),
  child: Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      stars,
      Text(
        '170 Reviews',
        style: TextStyle(
          color: Colors.black,
          fontWeight: FontWeight.w800,
          fontFamily: 'Roboto',
          letterSpacing: 0.5,
          fontSize: 20,
        ),
      ),
    ],
  ),
);

La fila de iconos, bajo la fila de valoraciones, contiene 3 columnas; cada columna contiene un icono y dos líneas de texto, como puedes ver en el árbolo de widgets:

Icon widget tree

La variable iconList define la fila de iconos:

final descTextStyle = TextStyle(
  color: Colors.black,
  fontWeight: FontWeight.w800,
  fontFamily: 'Roboto',
  letterSpacing: 0.5,
  fontSize: 18,
  height: 2,
);

// DefaultTextStyle.merge() te permite crear un estilo de texto por defecto
// que es heredado por sus hijos y todos los hijos subsecuentes.
final iconList = DefaultTextStyle.merge(
  style: descTextStyle,
  child: Container(
    padding: EdgeInsets.all(20),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Column(
          children: [
            Icon(Icons.kitchen, color: Colors.green[500]),
            Text('PREP:'),
            Text('25 min'),
          ],
        ),
        Column(
          children: [
            Icon(Icons.timer, color: Colors.green[500]),
            Text('COOK:'),
            Text('1 hr'),
          ],
        ),
        Column(
          children: [
            Icon(Icons.restaurant, color: Colors.green[500]),
            Text('FEEDS:'),
            Text('4-6'),
          ],
        ),
      ],
    ),
  ),
);

La variable leftColumn contiene las filas de valoraciones y la de iconos, también tiene el título y el texto que describe el Pavlova:

final leftColumn = Container(
  padding: EdgeInsets.fromLTRB(20, 30, 20, 20),
  child: Column(
    children: [
      titleText,
      subTitle,
      ratings,
      iconList,
    ],
  ),
);

La columna izquierda esta ubicada en un Container para resitringir su ancho. Finalmente, la UI es construida con la fila entera (conteniendo la columna izquierda y la imagen) dentro de un Card.

La imagen del Pavlova es de Pixabay. Puedes incrustar una imagen desde la red usando Image.network() pero, para este ejemplo, la imagen esta guardada en un directorio ‘images’ en el proyecto, añadido al fichero pubspec, y se accede usando Images.asset(). Para más información, mira Añadiendo assets e imágenes.

body: Center(
  child: Container(
    margin: EdgeInsets.fromLTRB(0, 40, 0, 30),
    height: 600,
    child: Card(
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            width: 440,
            child: leftColumn,
          ),
          mainImage,
        ],
      ),
    ),
  ),
),

App source: pavlova


Widgets de layout comunes

Flutter tiene una rica biblioteca de widgets de layout. Aquí hay algunos de los más comúnmente usados. La intención es ponerte en funcionamiento lo más rápidamente posible, antes que abrumarte con una lista completa. Para información de otros widgets disponibles, dirigete al catálogo de Widgets, o usa la búsqueda en la documentación de referencia de la API. También, las páginas de widget en la documentación de la API a menudo ofrece sugerencias sobre widgets similares que tal vez satisfagan mejor tus necesidades.

Los siguientes widgets caen en dos categorías: widgets estándard de la biblioteca de widgets, y widgets especializados de la biblioteca Material. Cualquier app puede usar la biblioteca de widgets pero solo las apps Material pueden usar la biblioteca Material Components.

Widgets estándar

  • Container: Añade padding, márgenes, bordes, color de fondo, o otras decoraciónes a un widget.
  • GridView: Organiza widgets en una cuadrícula con scroll.
  • ListView: Organiza en una lista con scroll.
  • Stack: Superpon un widget encima de otros.

Widgets Material

  • Card: Organiza información relacionada en una caja con bordes redondeados y una sombra proyectada.
  • ListTile: Organiza hasta 3 lineas de texto, e iconos opcionales al principio o al final, en una fila.

Container

Muchos layouts hace un uso libre de Containers para separar widgets usando padding, o para añadir bordes o márgenes. Puedes cambiar el fondo del dispositivo colocando el layout completo en un Container y cambiando su color o imagen de fondo.

Resumen (Container)

  • Añade padding, márgenes, bordes
  • Cambia color o imagen de fondo
  • Contiene un único hijo, pero este hijo puede ser un Row, Column, o incluso la raíz del árbol de widget
Diagram showing: margin, border, padding, and content

Ejemplos (Container)

Este layout consiste en una columna con dos filas, cada una conteniendo 2 imágenes. Un Container es usado para cambiar el color de fondo de la columna a un gris claro.

Widget _buildImageColumn() => Container(
        decoration: BoxDecoration(
          color: Colors.black26,
        ),
        child: Column(
          children: [
            _buildImageRow(1),
            _buildImageRow(3),
          ],
        ),
      );
Screenshot showing 2 rows, each containing 2 images

Un Container tambien es usado para añadir brodes redondeados y márgenes a cada imagen:

Widget _buildDecoratedImage(int imageIndex) => Expanded(
      child: Container(
        decoration: BoxDecoration(
          border: Border.all(width: 10, color: Colors.black38),
          borderRadius: const BorderRadius.all(const Radius.circular(8)),
        ),
        margin: const EdgeInsets.all(4),
        child: Image.asset('images/pic$imageIndex.jpg'),
      ),
    );

Widget _buildImageRow(int imageIndex) => Row(
      children: [
        _buildDecoratedImage(imageIndex),
        _buildDecoratedImage(imageIndex + 1),
      ],
    );

Puedes encontrar más ejemplos con Container en el tutorial y la Flutter Gallery.

App source: container


GridView

Usa GridView para organizar widgets como una lista bi-dimensional. GridView proporciona dos listas pre-fabricadas, o puedes construir tu propia cuadrícula personalizada. Cuando un GridView detecta que su contenido es muy grande para ajustarse a la caja de renderizado, este automáticamante permite hacer scroll.

Resumen (GridView)

  • Orgniza widgets en una cuadrícula
  • Detecta cuando el contenido de la columna excede la caja de renderizado y automáticamente proporciona scroll
  • Construye tu propia cuadrícula personalizada, o usa una de las proporcionadas:
    • GridView.count te permite especificar el número de columnas
    • GridView.extent te permite especificar el máximo número de píxeles de ancho de una celda

Ejemplos (GridView)

A 3-column grid of photos

Usa GridView.extent para crear una cuadrícula con celdas de un ancho máximo de 150 pixels.

App source: grid_and_list

A 2 column grid with footers

Usa GridView.count para crear una cuadrícula que tenga dos celdas de ancho en modo portrait, y 3 celdas de ancho en modo landscape. Las celdas son creadas fijando la propiedad footer para cada GridTile.

Dart code: grid_list_demo.dart de la Flutter Gallery

Widget _buildGrid() => GridView.extent(
    maxCrossAxisExtent: 150,
    padding: const EdgeInsets.all(4),
    mainAxisSpacing: 4,
    crossAxisSpacing: 4,
    children: _buildGridTileList(30));

// Las imágenes estan guardadas con nombres pic0.jpg, pic1.jpg...pic29.jpg.
// El constructor List.generate() permite una forma sencilla de crear
// una lista cuando los objetos tienen un patrón de nombre predecible.
List<Container> _buildGridTileList(int count) => List.generate(
    count, (i) => Container(child: Image.asset('images/pic$i.jpg')));

ListView

ListView, un widget similiar a una columna, automáticamente proporciona scroll cuando su contendido es demasiado grande para su caja de renderizado.

Resumen (ListView)

  • Un Column especializado para organizar una lista de cajas
  • Puede ser organizado horizontal o verticalmente
  • Detecta cuando su contenido no puede ser ajustado y proporciona scroll
  • Menos configurable que Column, pero más fácil de usar y con soporte para scroll

Ejemplos (ListView)

ListView containing movie theaters and restaurants

Usa ListView para mostrar una lista de negocios usando ListTiles. Un Divider separa los teatros de los restaurantes.

App source: grid_and_list

ListView containing shades of blue

Usa ListView para mostrar los Colors de la paleta de Material Design para una familia particular de color.

Dart code: colors_demo.dart de la Flutter Gallery

Widget _buildList() => ListView(
      children: [
        _tile('CineArts at the Empire', '85 W Portal Ave', Icons.theaters),
        _tile('The Castro Theater', '429 Castro St', Icons.theaters),
        _tile('Alamo Drafthouse Cinema', '2550 Mission St', Icons.theaters),
        _tile('Roxie Theater', '3117 16th St', Icons.theaters),
        _tile('United Artists Stonestown Twin', '501 Buckingham Way',
            Icons.theaters),
        _tile('AMC Metreon 16', '135 4th St #3000', Icons.theaters),
        Divider(),
        _tile('Kescaped_code#39;s Kitchen', '757 Monterey Blvd', Icons.restaurant),
        _tile('Emmyescaped_code#39;s Restaurant', '1923 Ocean Ave', Icons.restaurant),
        _tile(
            'Chaiya Thai Restaurant', '272 Claremont Blvd', Icons.restaurant),
        _tile('La Ciccia', '291 30th St', Icons.restaurant),
      ],
    );

ListTile _tile(String title, String subtitle, IconData icon) => ListTile(
      title: Text(title,
          style: TextStyle(
            fontWeight: FontWeight.w500,
            fontSize: 20,
          )),
      subtitle: Text(subtitle),
      leading: Icon(
        icon,
        color: Colors.blue[500],
      ),
    );

Stack

Usa Stack para organizar widgets encima de un widget base—a menudo una imagen. Los widgets pueden superponerse completa o parcialmente al widget base.

Resumen (Stack)

  • Usado por widgets que se superponen a otro widget
  • El primer widget en la lista de hijos es el widget base; los hijos siguientes son superpuestos encima de este widget base
  • El contenido de un Stack no puede hacer scroll
  • Puedes elegir recortar los hijos que excedan la caja de renderizado

Ejemplos (Stack)

Circular avatar image with a label

Usa Stack para suporponer un Container (que muestra su Text en un fondo negro tráslucido) encima de un CircleAvatar. El Stack ubica el texto usando la propiedad alignment y objetos Alignment.

App source: card_and_stack

An image with a grey gradient across the top

Usa Stack para superponer un gradiente encima de la imagen. El gradiente asegura que los iconos del toolbar son distintos de la imasgen.

Dart code: contacts_demo.dart de la Flutter Gallery

Widget _buildStack() => Stack(
    alignment: const Alignment(0.6, 0.6),
    children: [
      CircleAvatar(
        backgroundImage: AssetImage('images/pic.jpg'),
        radius: 100,
      ),
      Container(
        decoration: BoxDecoration(
          color: Colors.black45,
        ),
        child: Text(
          'Mia B',
          style: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
        ),
      ),
    ],
  );

Card

Un Card, de la biblioteca Material, contiene fragmentos relacionados de información y puede ser compuesto por casi cualquier widget, pero es a menudo usado con ListTile. Card tiene un solo hijo, pero su hijo puede ser una columna, fila, lista, cuadrícula, o otro widget que soporte múltiples hijos. Por defecto, un Card encoje su tamaño a 0 por 0 píxeles. Puedes usar SizedBox para restringir el tamaño de un card.

En Flutter, un Card tiene como característica esquinas ligeramente redondeadas y una sombra arrojada, dándole a este un efecto 3D. Cambiar la propiedad elevation de un Card te permite contolar el efecto de sombra arrojada. Configurando la elevación a 24, por ejemplo, visualmente eleva el Card de la superficie y causa que la sombra se vuelva más dispersa. Para una lista de valores permitidos de elevación, mira Elevation en las Material guidelines. Especificar un valor no soportado desactiva completamente la sombra arrojada.

Resumen (Card)

  • Implementa un Material card
  • Usado para repesentar fragementos relacionados de información
  • Acepta un úncio hijo, pero este hijo puede ser un Row, Column, o otro widget que albergue una lista de hijos
  • Mostrado con escquinas redondeadas y arroja una sombra
  • El contenido de un Card no puede hacer scroll
  • De la biblioteca Material

Ejemplos (Card)

Card containing 3 ListTiles

Un Card conteniendo 3 ListTiles y dimensionado envolviéndolo con un SizedBox. Un Divider separa el primer del segundo ListTiles.

App source: card_and_stack

Card containing an image, text and buttons

Un Card conteniendo una imagen y un texto.

Dart code: cards_demo.dart de la Flutter Gallery

Widget _buildCard() => SizedBox(
    height: 210,
    child: Card(
      child: Column(
        children: [
          ListTile(
            title: Text('1625 Main Street',
                style: TextStyle(fontWeight: FontWeight.w500)),
            subtitle: Text('My City, CA 99984'),
            leading: Icon(
              Icons.restaurant_menu,
              color: Colors.blue[500],
            ),
          ),
          Divider(),
          ListTile(
            title: Text('(408) 555-1212',
                style: TextStyle(fontWeight: FontWeight.w500)),
            leading: Icon(
              Icons.contact_phone,
              color: Colors.blue[500],
            ),
          ),
          ListTile(
            title: Text('costa@example.com'),
            leading: Icon(
              Icons.contact_mail,
              color: Colors.blue[500],
            ),
          ),
        ],
      ),
    ),
  );

ListTile

Usa ListTile, un widget de tipo fila especializado de la biblioteca Material, para una forma sencilla de crear una fila conteniendo hasta 3 líneas de texto e iconos opcionales al inicio o al final. ListTile es mayormente usado en un Card o ListView, pero puede ser usado en cualquier parte.

Resumen (ListTile)

  • Una fila especializada que contiene hasta 3 lineas de texto e iconos opcionales
  • Menos configurable que un Row, pero más fácil de usar
  • De la biblioteca Material

Ejemplos (ListTile)

Card containing 3 ListTiles

Un Card conteniendo 3 ListTiles.

App source: card_and_stack

3 ListTiles, each containing a pull-down button

Usa ListTile para listar 3 tipos de drop down button.
Dart code: buttons_demo.dart de la Flutter Gallery


Vídeos

Los siguientes vídeos, parte de la serie Flutter in Focus, explican los widgets Stateless y Stateful.

Flutter in Focus playlist


Cada episodio de la serie Widget of the Week series se enfoca en un widget. Muchos de ellos incluyen widgets de layout.

Flutter Widget of the Week playlist

Otros Recursos

Los siguientes recursos pueden ayudar cuando escribas código de layout.