Flutter España
Published in

Flutter España

Desafíos con Container de Flutter

En este artículo encontrarás 4 desafíos para ponerte a prueba en el Container widget de Flutter. Y en la segunda parte del artículo, podrás también revisar bastantes ejemplos de cómo alinear, decorar y componer Container widgets.

Tabla de Contenidos

Desafíos

Ejemplos y Teoría

  • Definición
  • Uso básico del widget Container
  • Definiendo altura y anchura, y alineando su widget hijo
  • Center y Align widgets
    • Center widget
    • Align widget usando un valor o posición predefinida
    • Align widget usando factores de altura y anchura
  • Decorando
    • Esquinas redondeadas
    • Esquinas redondeadas: forma de estadio
    • Esquinas redondeadas (solo algunas)
    • Forma circular
    • Borde
    • Sombra
    • Gradiente lineal de 2 colores
    • Gradiente lineal de 2 colores con puntos de parada (stops)
  • Componiendo containers
    • El widget hijo define su altura y su anchura
    • El widget hijo define su altura PERO no su anchura
    • El widget hijo no define su altura ni tampoco su anchura
    • El widget hijo no define su altura PERO tiene restricciones (margen)

NOTA: Tanto el widget Transform como la propiedad transform del widget Container, serán tratados en los siguientes artículos. ¡No te los pierdas!

DESAFÍOS

Disfruta estos desafíos de nivel BEGINNER e intenta hacerlos por tu cuenta sin mirar mi solución. ¡Verás cómo mejora tu nivel con cada uno de ellos!

Y recuerda que si tienes alguna duda o te bloqueas, puedes leer los ejemplos que vienen después de los desafíos. Leer los ejemplos también es muy interesante porque extienden el contenido de los desafíos (a veces hay ejemplos que muestran algo que no se cubre en los desafíos).

¿Preparado? ¿Sí? ¡Pues vamos allá!

El desafío

Consistirá en crear una Zona de aterrizaje para helicópteros:

No tienes por qué:

  • usar el mismo color y el mismo tamaño de letra que he usado yo

Deberías:

  • alinear el diseño arriba y al centro de la pantalla
  • introduce un margen superior para separarlo de la AppBar
  • centrar el texto “H” en el Container que lo contiene
  • usa únicamente los widgets: Align, Container y Text, y sus propiedades

Y ahora…. ¡ha llegado tu turno!

Deja de leer e inténtalo tú mismo sin mirar mi solución. ¡Buena Suerte!

Mi solución

No es la única solución, tan solo es la mía.

Align(
alignment: Alignment.topCenter,
child: Container(
margin: EdgeInsets.only(top: 20),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.orange,
width: 10,
),
),
width: 280,
height: 280,
alignment: Alignment.center,
child: Text(
'H',
style: TextStyle(
fontSize: 180,
color: Colors.orange,
),
),
),
)

El desafío

Implementar este diseño:

No tienes por qué:

  • usar el mismo color de background, el mismo tamaño de letra o el mismo radio en las esquinas

Deberías:

  • usar solamente los widgets: Container y Text (y sus propiedades)

Y ahora…. ¡ha llegado tu turno!

Deja de leer e inténtalo tú mismo sin mirar mi solución. ¡Buena Suerte!

Mi solución

No es la única solución, tan solo es la mía.

Container(
height: 130,
width: double.infinity,
decoration: BoxDecoration(
color: Color(0xFF57B3FC),
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(50),
bottomLeft: Radius.circular(50),
),
boxShadow: [
BoxShadow
(
color: Color(0xAA6EB1E6),
offset: Offset(9, 9),
blurRadius: 6,
),
],
),
alignment: Alignment.center,
child: Text(
'I am a header',
style: TextStyle(
fontSize: 38,
color: Colors.white,
),
),
)

El desafío

Intenta crear este diseño:

No tienes por qué:

  • usar los mismos colores ni los mismos tamaños

Deberías:

  • usar solamente instancias del widget Container(y sus propiedades)

Y ahora…. ¡ha llegado tu turno!

Deja de leer e inténtalo tú mismo sin mirar mi solución. ¡Buena Suerte!

Mi solución

No es la única solución, tan solo es la mía.

Container(
margin: EdgeInsets.all(40),
width: 300,
height: 90,
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
color: Color(0xFF4AAEFD), //blue
borderRadius: BorderRadius.circular(45),
),
child: Container(
width: 210,
height: 90,
decoration: BoxDecoration(
color: Color(0xFF94CCF9), //light blue
borderRadius: BorderRadius.only(
topLeft: Radius.circular(45),
bottomLeft: Radius.circular(45),
),
),
alignment: Alignment.center,
child: Text(
'Challenge',
style: TextStyle(
fontSize: 32,
color: Colors.white,
),
),
),
)

Para conseguir un efecto de forma de estadio, damos un valor suficientemente grande al radius, al menos la mitad del height.

El widget Container solo puede tener un hijo (child property). Así que la clave aquí es crear primero el contenedor azul y entonces crear el contenedor azul claro como hijo del anterior. Por último, el widget Text como hijo del contenedor azul claro.

El desafío

Implementar este diseño:

No tienes por qué:

  • usar los mismos colores, los mismos tamaños o el mismo radio de las esquinas

Deberías:

  • usar solamente instancias de los widgets: Container y Text (y sus propiedades)
  • alinear el diseño arriba y al centro de la pantalla
  • hacer el contenedor naranja lo más ancho posible dejando un margen alrededor de él de 30 dip

Atención!

  • el contenedor tiene una sombra (orientada hacia abajo e izquierda)

Y ahora…. ¡ha llegado tu turno!

Deja de leer e inténtalo tú mismo sin mirar mi solución. ¡Buena Suerte!

Mi solución

No es la única solución, tan solo es la mía.

Primero, creamos un contenedor padre para dar el color de background.

class Challenge extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Color(0xFF272A3C),
width: double.infinity,
height: double.infinity,
alignment: Alignment.topCenter, //to align its child
child: MyCardContainer(),
);
}
}

Ahora es el momento de implementar nuestro widget personalizado MyCardContainer:

class MyCardContainer extends StatelessWidget {

@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(30),
height: 160,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: new LinearGradient(
colors: [
Color(0xFFFF422C),
Color(0xFFFF9003),
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
stops: [0.25, 0.90],
),
boxShadow: [
BoxShadow(
color: Color(0xFF101012),
offset: Offset(-12, 12),
blurRadius: 8,
),
],
),
alignment: Alignment.centerLeft, //to align its child
padding: EdgeInsets.all(20),
child: Text(
'Challenge',
style: TextStyle(
fontSize: 46,
color: Colors.white,
fontWeight: FontWeight.w200,
fontStyle: FontStyle.italic,
),
),
);
}
}

Si has intentado hacer los desafíos, ¡¡ ENHORABUENA !!

Los siguientes ejemplos te podrán ayudar a solucionar los desafíos si has tenido algún problema.

EJEMPLOS Y TEORÍA

Definición

El widget Container es muy útil en tareas de layout. Podemos componerlo junto con un widget hijo (child property), posicionar su hijo a través de la alineación o bien usarlo para decorarlo (definiendo una forma, un color o gradiente, un borde, una sombra, etc).

Solo puede tener un hijo (child property).

Uso básico del widget Container

En el siguiente ejemplo usaremos solo un widget Text (no usaremos ningún widget Container):

Text(
'I am a text',
style: TextStyle(
fontSize: 38,
color: Color(0xFF04589A),
),
)

Vamos ahora a envolver el widget Text en un widget Container sin definir ningún color ni ninguna decoración, y veremos que no hay diferencia visual respecto al ejemplo anterior.

Container(
child: Text(
'I am a text',
style: TextStyle(
fontSize: 38,
color: Color(0xFF04589A),
),
),
)

Añadiendo color al widget Container:

Container(
color: Color(0xFF94CCF9),
child: Text(
'I am a text',
style: TextStyle(fontSize: 38, color: Color(0xFF04589A)),
),
)

Añadiendo margin para separar el widget Container de la esquina superior izquierda de la ventana, y padding para separar el widget Text del borde del Container.

Container(
color: Color(0xFF94CCF9),
padding: const EdgeInsets.all(20),
margin: const EdgeInsets.only(left: 40, top: 40),
child: Text(
'I am a text',
style: TextStyle(fontSize: 38, color: Color(0xFF04589A)),
),
)

Definiendo altura y anchura, y alineando su widget hijo

Un widget Container primero envuelve a su widget hijo con un padding (teniendo en cuenta si se ha definido algún borde en el objeto decoration), despúes considera el width y height en el caso de que sean no nulos, y por último, rodea todo con un espacio vacío equivalente al margin definido.

Por lo tanto, si no definimos el width y el height del widget Container, éste será tan pequeño como sea posible, envolviendo a su widget hijo con el padding definido.

Pero si definimos el width y el height del widget Container, éste intentará cumplir con las dimensiones, y el widget hijo se alineará por defecto a la esquina superior izquierda dentro del contenedor tal y como se puede ver aquí:

Container(
color: Color(0xFF94CCF9),
padding: const EdgeInsets.all(15),
margin: const EdgeInsets.only(left: 40, top: 40),
width: 250,
height: 250,
child: Text(
'Text',
style: TextStyle(fontSize: 32, color: Color(0xFF04589A)),
),
)

Ahora, podemos cambiar la alineación del hijo dentro del widget Container:

Container(
color: Color(0xFF94CCF9),
padding: const EdgeInsets.all(15),
margin: const EdgeInsets.only(left: 40, top: 40),
width: 250,
height: 250,
alignment: Alignment.bottomRight,
child: Text(
'Text',
style: TextStyle(fontSize: 32, color: Color(0xFF04589A)),
),
)

Center widget y Align widget

Para centrar o alinear nuestro Container dentro de su widget padre, lo envolvemos en un widget Center para centrar o Align para alinear.

Center widget

Center(
child: Container(
color: Color(0xFF94CCF9),
padding: const EdgeInsets.all(15),
child: Text(
'I am a text',
style: TextStyle(
fontSize: 38,
color: Color(0xFF04589A),
),
),
),
)

Align widget usando un valor o posición predefinida

Podemos alinear usando un valor o posición predefinida:

  • topLeft
  • topCenter
  • topRight
  • centerLeft
  • center
  • centerRight
  • bottomLeft
  • bottomCenter
  • bottomRight
Align(
alignment: Alignment.bottomLeft, //Alignment(-1.0, 1.0)
child: Container(
....
),
)

Align widget usando factores de altura y anchura

Si no es suficiente con las posiciones predefinidas, podemos personalizar más la alineación a una posición concreta usando valores de tipo double para widthFactor y heightFactor en el constructor.

Ambos toman valores desde -1.0 hasta 1.0.

Por ejemplo, para widthFactor :

  • -1.0 = borde izquierdo
  • 0.0 = la mitad de la anchura
  • 1.0 = borde derecho
  • -0.5 = punto medio entre el borde izquierdo y la mitad de la anchura
  • etc
Align(
alignment: Alignment(-0.5, 0.75),
child: Container(
....
),
)

Decorando

Para decorar un contenedor usaremos su propiedad decoration pudiendo elegir entre las siguientes opciones:

  • BoxDecoration
  • FlutterLogoDecoration
  • ShapeDecoration
  • UnderlineTabIndicator

En los siguientes ejemplos usaremos un BoxDecoration.

Un BoxDecoration es una descripción inmutable de cómo pintar una caja ofreciendo diferentes maneras para dibujarla.

Podemos elegir un color, una imagen o un gradiente para rellenar la caja. También se puede definir la forma de la caja, si tiene o no borde y si las esquinas son o no redondeadas. Sin olvidar que también se permite añadir una sombra.

Si se da valor a la propiedad decoration de un Container, entonces la propiedad color deberá estar definida dentro, y no fuera, del objeto decoration.

A continuación, se darán algunos ejemplos de uso del BoxDecoration (no es una descripción exhaustiva):

Esquinas redondeadas

Para definir la redondez de las esquinas usaremos la propiedadboderRadius del BoxDecoration.

Container(
margin: EdgeInsets.all(40),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFF9DF09E),
borderRadius: BorderRadius.circular(20),
),
child: Text(
'I am a text',
style: TextStyle(
fontSize: 38,
color: Color(0xFF1F9221),
),
),
)

Esquinas redondeadas: forma de estadio

Para conseguir que el Container tenga una forma de estadio, daremos un valor al radio que sea igual o mayor que la mitad de la altura del Container. Como en este ejemplo dicha altura no está definida explícitamente con la propiedad height, sino que depende del tamaño del texto, no conocemos su valor con exactitud. Por ello daremos un valor especialmente alto al radio (500) y así estaremos seguros de que será mayor o igual a la mitad de la altura.

Container(
margin: EdgeInsets.all(40),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFFF8DAA0),
borderRadius: BorderRadius.circular(500),
),
child: Text(
'I am a text',
style: TextStyle(
fontSize: 38,
color: Color(0xFFEC9B02),
),
),
)

Esquinas redondeadas (solo algunas)

Container(
margin: EdgeInsets.all(40),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.only(
topRight: Radius.circular(40.0),
bottomLeft: Radius.circular(40.0)
,
),
),
child: Text(
'I am a text',
style: TextStyle(
fontSize: 38,
color: Colors.white,
),
),
)

Forma circular

Usaremos la claseBoxShape para dar valor a la propiedad shape.

Container(
margin: EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.purpleAccent,
shape: BoxShape.circle,
),
width: 150,
height: 150,
)

Borde

Container(
margin: EdgeInsets.all(40),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFF94CCF9),
border: Border.all(
color: Color(0xFF04589A),
width: 4,
),
borderRadius: BorderRadius.circular(10.0),
),
child: Text(
'I am a text',
style: TextStyle(
fontSize: 38,
color: Color(0xFF04589A),
),
),
)

Sombra

Container(
margin: EdgeInsets.all(40),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFF94CCF9),
borderRadius: BorderRadius.circular(30.0),
boxShadow: [
BoxShadow(
color: Color(0xFF04589A),
offset: Offset(7, 7),
blurRadius: 6,
),
],
),
child: Text(
'I am a text',
style: TextStyle(
fontSize: 38,
color: Color(0xFF04589A),
),
),
)

Añadiremos una sombra dando valor a la propiedad boxShadow de la clase BoxDecoration.

Dentro de la clase BoxShadow tenemos la propiedad offset que nos permitirá definir el desplazamiento de la sombra desde el contenedor. Para ofrecer más realismo, podemos añadir un efecto borroso o efecto blur a través de la propiedad blurRadius.

Si nos fijamos, el diseño anterior tiene una sombra bastante fuerte (color 0xFF04589A).

Podemos reducir la opacidad (aumentar la transparencia) un poco, desde 0xFF04589A hasta por ejemplo 0x9904589A, y obtendremos un resultado más agradable. ¡Veámoslo!

Gradiente lineal de 2 colores

Container(
margin: EdgeInsets.all(40),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFF94CCF9),
border: Border.all(
color: Color(0xFF04589A),
width: 4,
),
borderRadius: BorderRadius.circular(10.0),
gradient: LinearGradient(
colors: [Colors.white, Color(0xFF75C0FC)],
begin: Alignment.centerLeft,
end: Alignment.centerRight),
),
child: Text(
'I am a text',
style: TextStyle(fontSize: 38, color: Color(0xFF04589A)),
),
)

Gradiente lineal de 2 colores con puntos de parada (stops)

Los puntos de parada de un gradiente son una lista de valores entre 0.0 y 1.0 que representan las fracciones de un gradiente.

En el ejemplo anterior, no usamos puntos de parada, lo cual es equivalente a usar [0.0, 1.0].

¿Y qué pasa si usamos diferentes puntos de parada? ¡Veámoslo con unos ejemplos!

Para dar más importancia al color de la izquiera (blanco) damos un valor mayor que 0.0 al primer punto de parada, por ejemplo [0.4, 1.0]:

Container(
margin: EdgeInsets.all(40),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFF94CCF9),
border: Border.all(
color: Color(0xFF04589A),
width: 4,
),
borderRadius: BorderRadius.circular(10.0),
gradient: LinearGradient(
colors: [Colors.white, Color(0xFF75C0FC)],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
stops: [0.4, 1.0],
),
),
child: Text(
'I am a text',
style: TextStyle(fontSize: 38, color: Color(0xFF04589A)),
),
)

Para conseguir el efecto contrario, es decir, ver más azul (color de la derecha) que blanco (color de la izquierda), daremos un valor menor que 1.0 al segundo punto de parada, por ejemplo [0.0, 0.6]:

Otro ejemplo de gradiente lineal:

Center(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomLeft,
end: Alignment.topRight,
colors: [
Color(0xFFFBA3660),
Color(0xFF1C256E),
],
stops: [0.3, 0.75],
),
),
),
)

Componiendo Containers

¿Qué es la composición de contenedores?

Pues consiste simplemente en definir un Container dentro de otro Container, o lo que es lo mismo, definir un Container como hijo (propiedad child) de otro Container.

Dentro de este contexto, tenemos diferentes escenarios:

El widget hijo define su altura y su anchura

Container(
margin: const EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(10.0),
),
width: 250,
height: 250,
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: Colors.cyan,
borderRadius: BorderRadius.circular(10.0),
),
height: 100,
width: 150,
),
)

El widget hijo define su altura PERO no su anchura

Container(
margin: const EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(10.0),
),
width: 250,
height: 250,
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: Colors.cyan,
borderRadius: BorderRadius.circular(10.0),
),
height: 100,
),
)

El Container hijo (cian) no define su anchura (y no tiene a su vez más hijos). Sin embargo, cubre completamente la anchura del widget padre. ¿Por qué?

Si un widget Container no tiene hijos, ni define su altura ni su anchura, ni otras restricciones, ni tampoco alineamiento, PERO su widget padre tiene dimensiones definidas, entonces el Container hijo se expande hasta ajustarse a los límites del padre.

Y esto es exactamente lo que le ocurre al Container hijo (cian) en el diseño anterior.

El widget hijo no define su altura ni tampoco su anchura

En este ejemplo, el widget hijo cubre completamente ambas dimensiones del padre por lo que el padre está oculto y no se le ve.

Container(
margin: const EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(10.0),
),
width: 250,
height: 250,
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: Colors.cyan,
borderRadius: BorderRadius.circular(10.0),
),
),
)

El widget hijo no define su altura PERO tiene restricciones (margen)

El widget hijo NO cubre completamente la anchura del padre porque define un margen de 10 dip.

Container(
margin: const EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(20.0),
),
width: 250,
height: 250,
alignment: Alignment.bottomCenter,
child: Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.cyan,
borderRadius: BorderRadius.circular(20.0),
),
height: 100,
),
)

Y así hemos llegado al final del artículo…

NOTA: Tanto el widget Transform como la propiedad transform del widget Container, serán tratados en los siguientes artículos. ¡No te los pierdas!

Si este artículo te ha parecido interesante o te ha ayudado, puedes agradecérmelo con unos cuantos 👏 👏 👏 👏 👏 👏 👏 👏.¡Muchas gracias por leerme!   ¡Hasta el próximo artículo!

--

--

Actualidad de Flutter en España y Español

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store