Flutter Deep Dive, Parte 4: “RenderFlex children have non-zero flex…”
Esta es la ultima de las 4, recuerden que esta traducción se realizo lo más apegada posible para la comprensión de su versión original escrita por Scott Stoll.
Continuemos con la traducción parte cuatro! 😃
Este es la parte cuatro de la serie de 4. Puedes encontrar los otros aquí:
- Parte 1: “RenderFlex children…”, entrando a la piscina de bebés
- Parte 2: Dando del salto
- Parte 3: Un Flex no es un flex
- Part 4:El algoritmo del layout Flex
Antes de comenzar, es posible que desees tomar un momento para ir al baño y rellenar tu café, té, vodka o cualquier otra cosa que estés tomando. Es posible que también quieras tomar el medicamento para el dolor de cabeza que prefieras, ahora. Un gramo de prevención se supone que vale una tonelada métrica de cura o algo así….
¿Listos? Vayamos.
Hay seis pasos para presentar a los hijos de un Flex.
Pegaré lo que dice la documentación sobre cada paso y luego haré lo mejor que pueda para traducir cada paso en humano:
Paso 1:
“Distribuya a cada niño un factor de flexibilidad nulo o nulo (por ejemplo, los que no son
Expanded
) con restricciones ilimitadas de los ejes principales y las restricciones de los ejes cruzados entrantes. Si la crossAxisAlignment es crossAxisAlignment.stretch, utilice en su lugar restricciones de ejes cruzados que coincidan con la extensión máxima de entrada en el eje cruzado.”
Primero, coloque a todos los hijos que no tienen ningún parámetro de flex
o un flex
parámetro que esté establecido en cero. Si el parámetro CrossAxisAlignment
está configurado para estirarse, entonces haga que el hijo sea tan grande como su padre lo permita en el eje transversal.
Paso 2:
“Divida el espacio del eje principal restante entre los hijos con factores de flexión distintos de cero (por ejemplo, los que son
Expanded
) de acuerdo con su factor de flexión. Por ejemplo, un hijo con un factor de flex de 2.0 recibirá el doble de espacio en el eje principal que un hijo con un factor de flex de 1.0.”
En Humano: Divida el espacio sobrante a lo largo del eje principal entre todos los hijos que tienen una flex
especificada y que la flex
no es cero. Sin embargo, no todos los niños reciben el mismo trato. Tal vez te gustan más algunos hijos que otros, y quieres darles más espacio que otros.
So how do you do this? You determine who gets how much space by their flex.
If you have 3 children and the flex
of each is 1, 4, and 5; then here’s what happens:
- La cantidad total de todos los
flex
es calculada. Aquí, 1 + 4 + 5 = 10, así tenemos unflex
de 10 en total. - El primer hijo tiene un
flex
de 1 y el totale es 10, entonces el primer hijo toma 1/10 del resto del espacio sobrante reservado para el.
Ponga un pin en esa frase, “reservado para él”. Volveremos a buscarla más tarde.
- El segundo hijo toma una reservación de 4/10 (40%) del total sobrante y el último nuestro favorito, toma 5/10 (mitad) reservado para su uso. of the total leftover space and the last one, your favorite, gets 5/10 (half)
Ahora calculamos el número real de dp que se reservará para cada uno, en función de cuál podría ser su parte del espacio sobrante.
No es. Puede ser.
Paso 3:
Distribuya cada uno de los hijos restantes con las mismas restricciones de los ejes transversales que en el paso 1, pero en lugar de utilizar las restricciones de los ejes principales ilimitadas, utilice las restricciones de los ejes máximos en función de la cantidad de espacio asignado en el paso 2". A los hijos con propiedades
Flexible.fit
se les imponen restricciones estrictas (es decir, se les obliga a llenar el espacio asignado), y a los hijos con propiedadesFlexible.fit
que están FlexFit.loose se les imponen restricciones flexibles (es decir, no se les obliga a llenar el espacio asignado)”
La primera cosa que un humano debe darse cuenta aquí es que no pusimos nada en el Paso 2; sólo hicimos un poco de matemáticas porque tenemos que saber exactamente cuántos dp (escritos como un double) cada uno de esos hijos va a tener reservado para que trabaje con ellos.
Cada hijo que tiene un flex entra en este proceso sin su eje principal de restricción establecido (el padre no ha puesto ningún límite a su tamaño todavía, por lo que sigue siendo “ilimitado”).
Así que el siguiente paso será hacer que las “restricciones del eje principal” máximo de cada niño sea el número máximo que se nos ocurrió en el Paso 2. Esto le permitirá estar a la altura de lo grande, pero nunca más grande que eso. Nunca olvides “nunca más grande que eso”. Se vuelve importante más adelante en este artículo e incluso más importante cada vez que se codifica una interfaz de usuario.
Pero espera, aún no hemos terminado. ¿Recuerdas esa caja de zapatos llamada fit
? Es la mitad del problema cuando todo esto sale horriblemente mal. Si el FlexFit
está suelto, básicamente significa que a nadie le importa. Sea tan grande como el hijo dice que quiere ser, como si ni siquiera tuviera unflex
.
Oye, ¿Scott? Si a nadie le importa y el Flexible
sólo va a hacer que el hijo tenga el mismo tamaño que iba a tener de todos modos, entonces, ¿por qué querría usar un ajuste suelto? ¿Por qué no usar al hijo sin envolverlo en un Flexible
?
Hey, ¿lector? Esa es una muy buena pregunta, ¿y sabes qué? La única razón que se me ocurre es que es una de las dos cosas que necesitamos hacer si queremos arreglar nuestro error Boogeyman RenderFlex. Aparte de tratar con “RenderFlex, los hijos tienen una flexión distinta a cero, pero las restricciones de altura no tienen límites”, nadie con quien he hablado ha podido pensar en una sola razón para usar un ajuste suelto, nunca.
Volviendo a nuestro ejemplo…. Si el FlexFit
está apretado, entonces el hijo intenta ocupar todo el espacio que le sobra. Recuerda, “apretado” significa empujar con fuerza contra cualquier límite que tus padres estén estableciendo. Una vez más, esto es lo que hace un Expanded
, porque en realidad es un Flexible
con su FlexFit
hard-coded a tight.
Paso 4:
“La extensión del eje transversal del Flex es la extensión máxima del eje transversal de los hijos (que siempre satisfará las restricciones entrantes).”
Para averiguarlo se necesitaron tres diccionarios y una gitana llamada Wanda con una baraja de Tarot.
¿La versión simple? El tamaño del eje transversal de una Colum / Row
va a ser tan grande como su hijo mayor en esa dirección. Si un hijo trata de ser más grande que eso, tira un error. Usualmente, esto será un desbordamiento que le mostrará las barras negras y amarillas, pero si anidó una Colum / Row
dentro de otra Colum / Row
, entonces podría ver a uno de los hermanos o hermanas de nuestro error Boogeyman:
“BoxConstraints forza una altura (o ancho) infinita”. (en el eje transversal)
Gracias Wanda.
Paso 6:
“Determine la posición para cada hijo de acuerdo con la alineación del eje principal y la
crossAxisAlignment.
Por ejemplo, si lamainAxisAlignment
esmainAxisAlignment.spaceBetween
, cualquier espacio del eje principal que no haya sido asignado a los hijos se dividirá de manera uniforme y se colocará entre los hijos.”
¿Has notado que todavía no hemos puesto nada en el Row
oColumn
? Hemos averiguado cuán grande se supone que debe ser cada niño tanto en el eje principal como en el eje transversal, y hemos averiguado cuán grande podría ser el Row
oColumn
. Pero todavía no hemos puesto a ningún hijo en él… y no podemos hacerlo todavía porque no tenemos ni idea del extremo de la Row
oColumn
por el que se supone que debemos empezar.
Ooops.
También vamos a necesitar saber si se supone que debemos agrupar a los niños, o separarlos y dejar espacio entre ellos.
Todas esas cosas están fuera del alcance de este artículo porque estamos aquí para tratar con esos mensajes de error y las cosas que los causan. No estamos cubriendo cada cosa sobre Rows
yColumns.
Dejaré que otro escriba ese Deep Dive.
En el código siguiente, ¿Cual es la altura de la Column
?
Entonces, ¿qué te parece? 100 +10 = 110 y no hay flexión. El tamaño de mainAxisSize
está configurado como mínimo, por lo que la Column
no intentará llenar todo el espacio que le da su padre. Por lo tanto, la Column
va a tener una altura de 110, ¿verdad?
Te equivocas.
Lee la letra pequeña
Repasemos:
- Paso 1: “Diseña para cada niño un factor de flexibilidad nulo o nulo (por ejemplo, los que no son
Expanded
) con restricciones ilimitadas de los ejes principales y las restricciones de los ejes cruzados entrantes.”
Ambos hijos tienen una estatura fija y ninguno de ellos utiliza un factor de flexibilidad, por lo que se colocan inmediatamente. No hay otros hijos que colocar, y la estatura total de ambos es de 110.0
- paso 5: “… Si la propiedad mainAxisSize es MainAxisSize.min, entonces la extensión del eje principal del Flex es la suma de las extensiones del eje principal de los hijos …”
¿Significa eso que la Column
tiene que ser del tamaño de la suma de sus hijos? No, eso no es lo que dice. Pero, ¿te olvidaste de la segunda parte de “nunca olvides esto”?
“sujeto a las restricciones entrantes.”
Es como si necesitaras ser abogado para escribir una solicitud en estos días…. tienes que leer la letra pequeña, gente. Cuando se vuelve a poner todo junto, la parte del Paso 5 que tiene que ver con MainAxisSize.min
es: “Si la propiedad mainAxisSize es MainAxisSize.min, entonces la extensión del eje principal del Flex es la suma de las extensiones del eje principal de los hijos (sujeto a las restricciones entrantes)”.
Lo que está pasando aquí es que las restricciones entrantes le están diciendo a MainAxisSize.min
que se siente y se calle porque van a forzar a nuestra Column
a ser tan grande como las restricciones máximas que el padre ha pasado. Así que aquí, el hecho de que mainAxisSize esté configurado en MainAxisSize.min
no importa, porque las restricciones fueron pasadas.
Pero espera, el Container
padre de nuestra Column
no tenía un tamaño específico, así que ¿de dónde vinieron las restricciones?
Pasando Constraints al árbol
La respuesta, querido lector que ahora tiene dolor de cabeza, se encuentra en el código fuente para el Container:
container.dart line 171:
… El widget tiene un [hijo] pero no tiene`height`, ni `width`, ni [constraints], y ni [alignment], y el [Container] pasa el constraints desde el padre hasta el hijo y se ajusta al tamaño del hijo.
Claramente, esto es sólo una parte de un conjunto de comentarios mucho más amplio (e incluso más confuso), pero la parte que nos importa está en negrita. Nuestro contenedor padre “no tiene `height`, ni `width`, ni [constraints], and ni [alignment]”. Así que, en nuestro Container
va a pasar las restricciones que le fueron dadas por su padre, y luego se dimensionará para que coincida con su hijo (nuestra Column
).
Vamos a desglosarlo todo para que sea más fácil de ver:
- Nuestro
SizedBox
tiene una altura de 500. - El primer hijo es el primer
Container.
No tiene altura específica y su padreSizedBox
le esta pasando un constraint de 500 dp de alto. ElContainer
le pasará estos 500 dp constraint de alto a nuestroColumn.
- El segundo hijo es nuestro
Column
, el cual necesita considerar a primero a su hijo antes de por calcular la altura. Tiene sumainAxisSize
establecido comoMainAxisSize.min,
entonces este intentará reducirse hasta empatar el tamaño de cual sea el tamaño decida ser. Pero intentar hacer esto no es lo mismo que hacerlo! - Hijos 3 y 4 son
Containers
dentro delColumn,
con altura de 100 y 10, para un total de 110. - El
Column
dirá que sis hijos suman 110 y sumainAxisSize
es al mínimo así que este quiere ser de 110… - Entonces el padre del
Column
dice, “Nope, estas sujeto a constrains de entrada que te he dado y este es el tamaño que seras. Ahora siéntate, callado y has lo que se te ha dicho” - Entonces, el primer
Container
sera de 100, el segundo de 10 y entonces habrá 390 dp de espacio muerto dentro de nuestraColumn.
Cuando miras a tu pantalla, parece que la Column
es de 110 dp y que hay 390 dp de espacio muerto debajo de la Columna. Pero esos 390 dp de espacio muerto están dentro de la Column
.
Y así es como un Container
con su mainAxisSize
configurado como mínimo y con un valor de 110 dp de hijos sigue siendo de 500 dp a pesar de que no hay flex en nada de esto.
¡Pero espera! ¡Hay más!
Ahora Alicia, asegúrate de que la cuerda de seguridad esté bien ajustada alrededor de la cintura porque a partir de aquí la madriguera del conejo empieza a ponerse verdaderamente rara….
¿Qué altura tiene la Column
esta vez? ¿Qué tamaño va a tener el Container
verde? ¿Qué tamaño va a tener el Container
amarillo?
Pensando a fondo….
Por lo tanto, el Flexible
con un ajuste apretado es realmente sólo un Expanded,
así que eso es fácil. Y el Flexible
con un ajuste suelto sólo ocupará 10, ya que un ajuste suelto significa que no tiene que ocupar todo el espacio que se le da.
El SizedBox
va a pasar una restricción de 200 por el Container
, así que sabemos que nuestra Column
va a ser de 200 dp de alto porque lo aprendimos en el último Pop Quiz. Y si el Container
amarillo toma 10, quedarán 190. Y todos sabemos que una Expandida es lo que usamos para ocupar cualquier espacio restante en una Column
, así que la verde tiene que ser 190, ¿verdad?
Ni siquiera cerca.
Bienvenida al País de las Maravillas, Alicia. Las Leyes de la Física no se aplican aquí.
Detrás de la cortina de los magos
¿Recuerdas aquella vez, hace mucho, mucho tiempo en una parte de este artículo muy, muy lejos? Aquella vez que te hice clavar un pin en la frase “reservado para el” durante el Paso 2?
También te dije que nunca olvidaras algo en el Paso 3. ¿Lo olvidaste?
En el paso 2 dijimos:
“Podemos calcular el número actual de dp que seran reservados por cada uno, basado en las partes de espacio sobrante compartido podría ser.”
En el paso Step 3 dijimos:
“Esto permitirá ser grande, pero nunca mas grande que eso. Nunca olvides “nunca mas grande que eso”. ”
Lo que sucedío aquí fue esto:
- Ambos
Flexibles
tienen una flex de 1, por lo que en el Paso 2 la cantidad de espacio que se reservó para cada uno fue la misma. Ambos tienen reservaciones para el 50% cada uno. Por lo tanto, se reservaron 100 dp para cada uno de ellos, y ahora cada uno de ellos nunca puede ser más grande que 100 dp. - El
Container
verde (en la parte superior) está rodeado de unFlexible
que tiene un ajuste apretado, por lo que va a tomar hasta el último dp que pueda obtener (100). - El
Container
amarillo (en la parte inferior) está rodeado de unFlexible
que tiene un ajuste suelto, por lo que no importa que se hayan reservado 100 dp para él. Sólo va a usar 10.
Resultado final:
- El
Column
es 200 dp - El
Container
verde es 100 dp - El
Container
amarillo es 10 dp - Hay 90 dp de espacio muerto dentro el
Column
So when people tell you to use an Expanded
to eat up all of the leftover space in a Row / Column,
you need to remember that if there is a loose fit Flexible
in there then you need to throw everything most people know about Rows, Columns
and Expandeds
… wait for it…
Así que cuando la gente te dice que uses un Expanded
para comerte todo el espacio sobrante en una Rows/Columns
, necesitas recordar que si hay un ajuste suelto Flexible
ahí dentro entonces necesitas tirar todo lo que la mayoría de la gente sabe sobre Rows, Columns
y Expandeds
…. espera…
Justo el Hoyo del Conejo.
Y vivieron con una horrible migraña para siempre.
FIN
Puedes seguir al autor en Twitter en:
Y al traductor en: