Saltar al contenido principal

Entendiendo las restricciones

El modelo de Flutter para restricciones de Widget, dimensionamiento, posicionamiento y cómo interactúan.

Imagen héroe del artículo

Cuando alguien que está aprendiendo Flutter te pregunta por qué algún Widget con width: 100 no tiene 100 píxeles de ancho, la respuesta por defecto es decirle que ponga ese Widget dentro de un Center, ¿verdad?

Don't do that.

Si lo haces, volverán una y otra vez, preguntando por qué algún FittedBox no funciona, por qué ese Column se desborda, o qué se supone que debe hacer IntrinsicWidth.

En su lugar, primero diles que el layout de Flutter es muy diferente del layout de HTML (que es de donde probablemente provienen), y luego haz que memoricen la siguiente regla:

Constraints go down. Sizes go up. Parent sets position.

El layout de Flutter no se puede entender realmente sin conocer esta regla, por lo que los desarrolladores de Flutter deberían aprenderla desde el principio.

En más detalle:

  • Un Widget obtiene sus propias restricciones de su padre. Una restricción es solo un conjunto de 4 doubles: un ancho mínimo y máximo, y una altura mínima y máxima.
  • Luego, el Widget recorre su propia lista de hijos. Uno por uno, el Widget les dice a sus hijos cuáles son sus restricciones (que pueden ser diferentes para cada hijo), y luego le pregunta a cada hijo qué tamaño quiere tener.
  • Luego, el Widget posiciona a sus hijos (horizontalmente en el eje x, y verticalmente en el eje y), uno por uno.
  • Y, finalmente, el Widget le informa a su padre sobre su propio tamaño (dentro de las restricciones originales, por supuesto).

Por ejemplo, si un Widget compuesto contiene una columna con algo de padding, y quiere organizar a sus dos hijos de la siguiente manera:

Layout visual

La negociación es algo como esto:

Widget: "Oye padre, ¿cuáles son mis restricciones?"

Padre: "Debes tener de 0 a 300 píxeles de ancho, y de 0 a 85 de alto."

Widget: "Hmmm, como quiero tener 5 píxeles de padding, entonces mis hijos pueden tener como máximo 290 píxeles de ancho y 75 píxeles de alto."

Widget: "Oye primer hijo, debes tener de 0 a 290 píxeles de ancho, y de 0 a 75 de alto."

Primer hijo: "Vale, entonces quiero tener 290 píxeles de ancho y 20 píxeles de alto."

Widget: "Hmmm, como quiero colocar a mi segundo hijo debajo del primero, esto deja solo 55 píxeles de altura para mi segundo hijo."

Widget: "Oye segundo hijo, debes tener de 0 a 290 de ancho, y de 0 a 55 de alto."

Segundo hijo: "Vale, quiero tener 140 píxeles de ancho, y 30 píxeles de alto."

Widget: "Muy bien. Mi primer hijo tiene la posición x: 5 e y: 5, y mi segundo hijo tiene x: 80 e y: 25."

Widget: "Oye padre, he decidido que mi tamaño va a ser de 300 píxeles de ancho, y 60 píxeles de alto."

Limitaciones

#

El motor de layout de Flutter está diseñado para ser un proceso de una sola pasada. Esto significa que Flutter organiza sus Widgets de manera muy eficiente, pero da como resultado algunas limitaciones:

  • Un Widget puede decidir su propio tamaño solo dentro de las restricciones que le da su padre. Esto significa que un Widget usualmente no puede tener cualquier tamaño que quiera.

  • Un Widget no puede conocer y no decide su propia posición en la pantalla, ya que es el padre del Widget quien decide la posición del Widget.

  • Como el tamaño y la posición del padre, a su vez, también dependen de su propio padre, es imposible definir con precisión el tamaño y la posición de cualquier Widget sin tener en cuenta el árbol en su conjunto.

  • Si un hijo quiere un tamaño diferente al de su padre y el padre no tiene suficiente información para alinearlo, entonces el tamaño del hijo podría ignorarse. Sé específico al definir la alineación.

En Flutter, los Widgets son renderizados por sus objetos RenderBox subyacentes. Muchas cajas en Flutter, especialmente aquellas que solo aceptan un hijo, transmiten su restricción a sus hijos.

En general, hay tres tipos de cajas, en términos de cómo manejan sus restricciones:

  • Aquellas que intentan ser lo más grandes posible. Por ejemplo, las cajas utilizadas por Center y ListView.
  • Aquellas que intentan ser del mismo tamaño que sus hijos. Por ejemplo, las cajas utilizadas por Transform y Opacity.
  • Aquellas que intentan ser de un tamaño en particular. Por ejemplo, las cajas utilizadas por Image y Text.

Algunos Widgets, por ejemplo Container, varían de un tipo a otro según los argumentos de su constructor. El constructor de Container intenta por defecto ser lo más grande posible, pero si le das un width, por ejemplo, intenta respetar eso y tener ese tamaño en particular.

Otros, por ejemplo Row y Column (cajas flex) varían según las restricciones que reciben, como se describe en la sección Flex.

Ejemplos

#

Para una experiencia interactiva, usa el siguiente DartPad. Usa la barra de desplazamiento horizontal numerada para cambiar entre 29 ejemplos diferentes.

Si lo prefieres, puedes tomar el código de este repositorio de GitHub.

Los ejemplos se explican en las siguientes secciones.

Ejemplo 1

#
Layout del Ejemplo 1
dart
Container(color: red)

La pantalla es el padre del Container, y fuerza al Container a tener exactamente el mismo tamaño que la pantalla.

Así que el Container llena la pantalla y la pinta de rojo.

Ejemplo 2

#
Layout del Ejemplo 2
dart
Container(width: 100, height: 100, color: red)

El Container rojo quiere ser de 100 × 100, pero no puede, porque la pantalla lo fuerza a tener exactamente el mismo tamaño que la pantalla.

Así que el Container llena la pantalla.

Ejemplo 3

#
Layout del Ejemplo 3
dart
Center(child: Container(width: 100, height: 100, color: red))

La pantalla fuerza al Center a tener exactamente el mismo tamaño que la pantalla, así que el Center llena la pantalla.

El Center le dice al Container que puede tener cualquier tamaño que quiera, pero no más grande que la pantalla. Ahora el Container puede ser efectivamente de 100 × 100.

Ejemplo 4

#
Layout del Ejemplo 4
dart
Align(
  alignment: Alignment.bottomRight,
  child: Container(width: 100, height: 100, color: red),
)

Esto es diferente del ejemplo anterior en que usa Align en lugar de Center.

Align también le dice al Container que puede tener cualquier tamaño que quiera, pero si hay espacio vacío no centrará el Container. En su lugar, alinea el contenedor en la parte inferior derecha del espacio disponible.

Ejemplo 5

#
Layout del Ejemplo 5
dart
Center(
  child: Container(
    width: double.infinity,
    height: double.infinity,
    color: red,
  ),
)

La pantalla fuerza al Center a tener exactamente el mismo tamaño que la pantalla, así que el Center llena la pantalla.

El Center le dice al Container que puede tener cualquier tamaño que quiera, pero no más grande que la pantalla. El Container quiere tener un tamaño infinito, pero como no puede ser más grande que la pantalla, simplemente llena la pantalla.

Ejemplo 6

#
Layout del Ejemplo 6
dart
Center(child: Container(color: red))

La pantalla fuerza al Center a tener exactamente el mismo tamaño que la pantalla, así que el Center llena la pantalla.

El Center le dice al Container que puede tener cualquier tamaño que quiera, pero no más grande que la pantalla. Dado que el Container no tiene hijo ni tamaño fijo, decide que quiere ser lo más grande posible, así que llena toda la pantalla.

¿Pero por qué el Container decide eso? Simplemente porque esa es una decisión de diseño tomada por quienes crearon el Widget Container. Podría haberse creado de manera diferente, y tienes que leer la documentación de la API de Container para entender cómo se comporta, según las circunstancias.

Ejemplo 7

#
Layout del Ejemplo 7
dart
Center(
  child: Container(
    color: red,
    child: Container(color: green, width: 30, height: 30),
  ),
)

La pantalla fuerza al Center a tener exactamente el mismo tamaño que la pantalla, así que el Center llena la pantalla.

El Center le dice al Container rojo que puede tener cualquier tamaño que quiera, pero no más grande que la pantalla. Dado que el Container rojo no tiene tamaño pero tiene un hijo, decide que quiere ser del mismo tamaño que su hijo.

El Container rojo le dice a su hijo que puede tener cualquier tamaño que quiera, pero no más grande que la pantalla.

El hijo es un Container verde que quiere tener 30 × 30. Dado que el Container rojo se dimensiona a sí mismo al tamaño de su hijo, también es de 30 × 30. El color rojo no es visible porque el Container verde cubre completamente al Container rojo.

Ejemplo 8

#
Layout del Ejemplo 8
dart
Center(
  child: Container(
    padding: const EdgeInsets.all(20),
    color: red,
    child: Container(color: green, width: 30, height: 30),
  ),
)

El Container rojo se dimensiona al tamaño de sus hijos, pero toma en consideración su propio padding. Así que también es de 30 × 30 más padding. El color rojo es visible debido al padding, y el Container verde tiene el mismo tamaño que en el ejemplo anterior.

Ejemplo 9

#
Layout del Ejemplo 9
dart
ConstrainedBox(
  constraints: const BoxConstraints(
    minWidth: 70,
    minHeight: 70,
    maxWidth: 150,
    maxHeight: 150,
  ),
  child: Container(color: red, width: 10, height: 10),
)

Podrías adivinar que el Container tiene que estar entre 70 y 150 píxeles, pero estarías equivocado. El ConstrainedBox solo impone restricciones adicionales a las que recibe de su padre.

Aquí, la pantalla fuerza al ConstrainedBox a ser exactamente del mismo tamaño que la pantalla, por lo que le dice a su hijo Container que también asuma el tamaño de la pantalla, ignorando así su parámetro constraints.

Ejemplo 10

#
Layout del Ejemplo 10
dart
Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      minWidth: 70,
      minHeight: 70,
      maxWidth: 150,
      maxHeight: 150,
    ),
    child: Container(color: red, width: 10, height: 10),
  ),
)

Ahora, Center permite a ConstrainedBox tener cualquier tamaño hasta el tamaño de la pantalla. El ConstrainedBox impone restricciones adicionales de su parámetro constraints sobre su hijo.

El Container debe estar entre 70 y 150 píxeles. Quiere tener 10 píxeles, por lo que termina teniendo 70 (el mínimo).

Ejemplo 11

#
Layout del Ejemplo 11
dart
Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      minWidth: 70,
      minHeight: 70,
      maxWidth: 150,
      maxHeight: 150,
    ),
    child: Container(color: red, width: 1000, height: 1000),
  ),
)

Center permite a ConstrainedBox tener cualquier tamaño hasta el tamaño de la pantalla. El ConstrainedBox impone restricciones adicionales de su parámetro constraints sobre su hijo.

El Container debe estar entre 70 y 150 píxeles. Quiere tener 1000 píxeles, por lo que termina teniendo 150 (el máximo).

Ejemplo 12

#
Layout del Ejemplo 12
dart
Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      minWidth: 70,
      minHeight: 70,
      maxWidth: 150,
      maxHeight: 150,
    ),
    child: Container(color: red, width: 100, height: 100),
  ),
)

Center permite a ConstrainedBox tener cualquier tamaño hasta el tamaño de la pantalla. El ConstrainedBox impone restricciones adicionales de su parámetro constraints sobre su hijo.

El Container debe estar entre 70 y 150 píxeles. Quiere tener 100 píxeles, y ese es el tamaño que tiene, ya que está entre 70 y 150.

Ejemplo 13

#
Layout del Ejemplo 13
dart
UnconstrainedBox(
  child: Container(color: red, width: 20, height: 50),
)

La pantalla fuerza al UnconstrainedBox a tener exactamente el mismo tamaño que la pantalla. Sin embargo, el UnconstrainedBox permite que su hijo Container tenga cualquier tamaño que quiera.

Ejemplo 14

#
Layout del Ejemplo 14
dart
UnconstrainedBox(
  child: Container(color: red, width: 4000, height: 50),
)

La pantalla fuerza al UnconstrainedBox a tener exactamente el mismo tamaño que la pantalla, y UnconstrainedBox permite que su hijo Container tenga cualquier tamaño que quiera.

Desafortunadamente, en este caso el Container tiene 4000 píxeles de ancho y es demasiado grande para caber en el UnconstrainedBox, por lo que el UnconstrainedBox muestra la muy temida "advertencia de desbordamiento".

Ejemplo 15

#
Layout del Ejemplo 15
dart
OverflowBox(
  minWidth: 0,
  minHeight: 0,
  maxWidth: double.infinity,
  maxHeight: double.infinity,
  child: Container(color: red, width: 4000, height: 50),
)

La pantalla fuerza al OverflowBox a tener exactamente el mismo tamaño que la pantalla, y OverflowBox permite que su hijo Container tenga cualquier tamaño que quiera.

OverflowBox es similar a UnconstrainedBox; la diferencia es que no mostrará ninguna advertencia si el hijo no cabe en el espacio.

En este caso, el Container tiene 4000 píxeles de ancho, y es demasiado grande para caber en el OverflowBox, pero el OverflowBox simplemente muestra tanto como puede, sin dar ninguna advertencia.

Ejemplo 16

#
Layout del Ejemplo 16
dart
UnconstrainedBox(
  child: Container(color: Colors.red, width: double.infinity, height: 100),
)

Esto no renderizará nada y verás un error en la consola.

El UnconstrainedBox permite que su hijo tenga cualquier tamaño que quiera, sin embargo su hijo es un Container con tamaño infinito.

Flutter no puede renderizar tamaños infinitos, por lo que arroja un error con el siguiente mensaje: BoxConstraints forces an infinite width.

Ejemplo 17

#
Layout del Ejemplo 17
dart
UnconstrainedBox(
  child: LimitedBox(
    maxWidth: 100,
    child: Container(
      color: Colors.red,
      width: double.infinity,
      height: 100,
    ),
  ),
)

Aquí ya no obtendrás un error, porque cuando el UnconstrainedBox le da a LimitedBox un tamaño infinito; este transmite un ancho máximo de 100 a su hijo.

Si cambias el UnconstrainedBox por un Widget Center, el LimitedBox ya no aplicará su límite (ya que su límite solo se aplica cuando recibe restricciones infinitas), y el ancho del Container puede crecer más allá de 100.

Esto explica la diferencia entre un LimitedBox y un ConstrainedBox.

Ejemplo 18

#
Layout del Ejemplo 18
dart
const FittedBox(child: Text('Some Example Text.'))

La pantalla fuerza al FittedBox a tener exactamente el mismo tamaño que la pantalla. El Text tiene cierto ancho natural (también llamado su ancho intrínseco) que depende de la cantidad de texto, el tamaño de su fuente, etc.

El FittedBox permite que el Text tenga cualquier tamaño que quiera, pero después de que el Text le informa su tamaño al FittedBox, el FittedBox escala el Text hasta que llena todo el ancho disponible.

Ejemplo 19

#
Layout del Ejemplo 19
dart
const Center(child: FittedBox(child: Text('Some Example Text.')))

¿Pero qué pasa si pones el FittedBox dentro de un Widget Center? El Center permite que el FittedBox tenga cualquier tamaño que quiera, hasta el tamaño de la pantalla.

El FittedBox luego se dimensiona a sí mismo al Text, y permite que el Text tenga cualquier tamaño que quiera. Dado que tanto el FittedBox como el Text tienen el mismo tamaño, no ocurre ningún escalado.

Ejemplo 20

#
Layout del Ejemplo 20
dart
const Center(
  child: FittedBox(
    child: Text(
      'This is some very very very large text that is too big to fit a regular screen in a single line.',
    ),
  ),
)

Sin embargo, ¿qué sucede si FittedBox está dentro de un Widget Center, pero el Text es demasiado grande para caber en la pantalla?

FittedBox intenta dimensionarse a sí mismo al Text, pero no puede ser más grande que la pantalla. Luego asume el tamaño de la pantalla, y redimensiona el Text para que también quepa en la pantalla.

Ejemplo 21

#
Layout del Ejemplo 21
dart
const Center(
  child: Text(
    'This is some very very very large text that is too big to fit a regular screen in a single line.',
  ),
)

Sin embargo, si eliminas el FittedBox, el Text obtiene su ancho máximo de la pantalla, y rompe la línea para que quepa en la pantalla.

Ejemplo 22

#
Layout del Ejemplo 22
dart
FittedBox(
  child: Container(height: 20, width: double.infinity, color: Colors.red),
)

FittedBox solo puede escalar un Widget acotado (que tenga ancho y alto no infinitos). De lo contrario, no renderizará nada, y verás un error en la consola.

Ejemplo 23

#
Layout del Ejemplo 23
dart
Row(
  children: [
    Container(
      color: red,
      child: const Text('Hello!', style: big),
    ),
    Container(
      color: green,
      child: const Text('Goodbye!', style: big),
    ),
  ],
)

La pantalla fuerza al Row a tener exactamente el mismo tamaño que la pantalla.

Al igual que un UnconstrainedBox, el Row no impondrá ninguna restricción a sus hijos, y en su lugar permite que tengan cualquier tamaño que quieran. El Row luego los coloca uno al lado del otro, y cualquier espacio extra permanece vacío.

Ejemplo 24

#
Layout del Ejemplo 24
dart
Row(
  children: [
    Container(
      color: red,
      child: const Text(
        'This is a very long text that '
        'won\'t fit the line.',
        style: big,
      ),
    ),
    Container(
      color: green,
      child: const Text('Goodbye!', style: big),
    ),
  ],
)

Como Row no impondrá ninguna restricción a sus hijos, es muy posible que los hijos sean demasiado grandes para caber en el ancho disponible del Row. En este caso, al igual que un UnconstrainedBox, el Row muestra la "advertencia de desbordamiento".

Ejemplo 25

#
Layout del Ejemplo 25
dart
Row(
  children: [
    Expanded(
      child: Center(
        child: Container(
          color: red,
          child: const Text(
            'This is a very long text that won\'t fit the line.',
            style: big,
          ),
        ),
      ),
    ),
    Container(
      color: green,
      child: const Text('Goodbye!', style: big),
    ),
  ],
)

Cuando el hijo de un Row se envuelve en un Widget Expanded, el Row ya no permitirá que este hijo defina su propio ancho.

En su lugar, define el ancho del Expanded de acuerdo con los otros hijos, y solo entonces el Widget Expanded fuerza al hijo original a tener el ancho del Expanded.

En otras palabras, una vez que usas Expanded, el ancho del hijo original se vuelve irrelevante y se ignora.

Ejemplo 26

#
Layout del Ejemplo 26
dart
Row(
  children: [
    Expanded(
      child: Container(
        color: red,
        child: const Text(
          'This is a very long text that won\'t fit the line.',
          style: big,
        ),
      ),
    ),
    Expanded(
      child: Container(
        color: green,
        child: const Text('Goodbye!', style: big),
      ),
    ),
  ],
)

Si todos los hijos del Row están envueltos en Widgets Expanded, cada Expanded tiene un tamaño proporcional a su parámetro flex, y solo entonces cada Widget Expanded fuerza a su hijo a tener el ancho del Expanded.

En otras palabras, Expanded ignora el ancho preferido de sus hijos.

Ejemplo 27

#
Layout del Ejemplo 27
dart
Row(
  children: [
    Flexible(
      child: Container(
        color: red,
        child: const Text(
          'This is a very long text that won\'t fit the line.',
          style: big,
        ),
      ),
    ),
    Flexible(
      child: Container(
        color: green,
        child: const Text('Goodbye!', style: big),
      ),
    ),
  ],
)

La única diferencia si usas Flexible en lugar de Expanded, es que Flexible permite que su hijo tenga el mismo ancho o uno menor que el Flexible mismo, mientras que Expanded fuerza a su hijo a tener exactamente el mismo ancho del Expanded. Pero tanto Expanded como Flexible ignoran el ancho de sus hijos al dimensionarse a sí mismos.

Ejemplo 28

#
Layout del Ejemplo 28
dart
Scaffold(
  body: Container(
    color: blue,
    child: const Column(children: [Text('Hello!'), Text('Goodbye!')]),
  ),
)

La pantalla fuerza al Scaffold a tener exactamente el mismo tamaño que la pantalla, así que el Scaffold llena la pantalla. El Scaffold le dice al Container que puede tener cualquier tamaño que quiera, pero no más grande que la pantalla.

Ejemplo 29

#
Layout del Ejemplo 29
dart
Scaffold(
  body: SizedBox.expand(
    child: Container(
      color: blue,
      child: const Column(children: [Text('Hello!'), Text('Goodbye!')]),
    ),
  ),
)

Si quieres que el hijo del Scaffold tenga exactamente el mismo tamaño que el Scaffold mismo, puedes envolver a su hijo con SizedBox.expand.

Restricciones estrictas vs laxas

#

Es muy común escuchar que alguna restricción es "estricta" o "laxa", entonces ¿qué significa eso?

Restricciones estrictas

#

Una restricción estricta ofrece una sola posibilidad, un tamaño exacto. En otras palabras, una restricción estricta tiene su ancho máximo igual a su ancho mínimo; y tiene su altura máxima igual a su altura mínima.

Un ejemplo de esto es el Widget App, que está contenido por la clase RenderView: a la caja utilizada por el hijo devuelto por la función build de la aplicación se le da una restricción que la fuerza a llenar exactamente el área de contenido de la aplicación (típicamente, toda la pantalla).

Otro ejemplo: si anidas un grupo de cajas dentro de otras en la raíz del árbol de renderizado de tu aplicación, todas se adaptarán exactamente entre sí, forzadas por las restricciones strictly estrictas de la caja.

Si vas al archivo box.dart de Flutter y buscas los constructores de BoxConstraints, encontrarás lo siguiente:

dart
BoxConstraints.tight(Size size)
   : minWidth = size.width,
     maxWidth = size.width,
     minHeight = size.height,
     maxHeight = size.height;

Si vuelves a visitar el Ejemplo 2, la pantalla fuerza al Container rojo a ser exactamente del mismo tamaño que la pantalla. La pantalla logra eso, por supuesto, pasando restricciones estrictas al Container.

Restricciones laxas

#

Una restricción laxa es aquella que tiene un mínimo de cero y un máximo diferente de cero.

Algunas cajas relajan las restricciones entrantes, lo que significa que el máximo se mantiene pero el mínimo se elimina, por lo que el Widget puede tener un ancho y alto mínimo ambos iguales a cero.

En última instancia, el propósito de Center es transformar las restricciones estrictas que recibió de su padre (la pantalla) en restricciones laxas para su hijo (el Container).

Si vuelves a visitar el Ejemplo 3, el Center permite que el Container rojo sea más pequeño, pero no más grande que la pantalla.

Restricciones sin límites

#

En ciertas situaciones, la restricción de una caja es sin límites, o infinita. Esto significa que ya sea el ancho máximo o la altura máxima está configurada en double.infinity.

Una caja que intenta ser lo más grande posible no funcionará de manera útil cuando reciba una restricción sin límites y, en modo de depuración, arrojará una excepción.

El caso más común donde una caja de renderizado termina con una restricción sin límites es dentro de una caja flex (Row o Column), y dentro de una región con desplazamiento (como ListView y otras subclases de ScrollView).

ListView, por ejemplo, intenta expandirse para adaptarse al espacio disponible en su dirección cruzada (quizás es un bloque que se desplaza verticalmente e intenta ser tan ancho como su padre). Si anidas un ListView que se desplaza verticalmente dentro de un ListView que se desplaza horizontalmente, la lista interna intenta ser tan ancha como sea posible, lo cual es infinitamente ancha, ya que la externa es desplazable en esa dirección.

La siguiente sección describe el error que podrías encontrar con restricciones sin límites en un Widget Flex.

Flex

#

Una caja flex (Row y Column) se comporta de manera diferente dependiendo de si su restricción está acotada o sin límites en su dirección primaria.

Una caja flex con una restricción acotada en su dirección primaria intenta ser lo más grande posible.

Una caja flex con una restricción sin límites en su dirección primaria intenta adaptar sus hijos a ese espacio. El valor flex de cada hijo debe ser configurado en cero, lo que significa que no puedes usar Expanded cuando la caja flex está dentro de otra caja flex o de un elemento desplazable; de lo contrario, arroja una excepción.

La dirección cruzada (ancho para Column o altura para Row), nunca debe estar sin límites, o no podrá alinear razonablemente a sus hijos.

Aprender las reglas de layout para Widgets específicos

#

Conocer la regla general de layout es necesario, pero no es suficiente.

Cada Widget tiene mucha libertad al aplicar la regla general, so no hay forma de saber cómo se comporta simplemente leyendo el nombre del Widget.

Si intentas adivinar, probablemente adivines mal. No puedes saber exactamente cómo se comporta un Widget a menos que hayas leído su documentación o estudiado su código fuente.

El código fuente del layout suele ser complejo, por lo que probablemente sea mejor leer simplemente la documentación. Sin embargo, si decides estudiar el código fuente del layout, puedes encontrarlo fácilmente usando las capacidades de navegación de tu IDE.

Aquí hay un ejemplo:

  • Encuentra un Column en tu código y navega a su código fuente. Para hacer esto, usa command+B (macOS) o control+B (Windows/Linux) en Android Studio o IntelliJ. Serás llevado al archivo basic.dart. Como Column extiende de Flex, navega al código fuente de Flex (también en basic.dart).

  • Desplázate hacia abajo hasta encontrar un método llamado createRenderObject(). Como puedes ver, este método devuelve un RenderFlex. Este es el objeto de renderizado para el Column. Ahora navega al código fuente de RenderFlex, lo que te lleva al archivo flex.dart.

  • Desplázate hacia abajo hasta encontrar un método llamado performLayout(). Este es el método que realiza el layout para el Column.

Un layout de despedida

Artículo original por Marcelo Glasberg

Marcelo publicó originalmente este contenido como Flutter: La regla de layout avanzada que incluso los principiantes deben saber en Medium. Nos encantó y le pedimos que nos permitiera publicarlo en docs.flutter.dev, a lo cual accedió amablemente. ¡Gracias, Marcelo! Puedes encontrar a Marcelo en GitHub y pub.dev.

Además, gracias a Simon Lightfoot por crear la imagen de cabecera en la parte superior del artículo.