Creando cards en Flutter

Este es el quinto tutorial de la serie Fundamentos de Flutter en el cual aprenderemos a utilizar y diseñar los cards, un componente muy usado dentro de las aplicaciones móviles para mostrar información. Un ejemplo de uso de cards podría ser Facebook que utiliza los cards en diversas partes de su aplicación móvil y web.

Índice

  1. Creando nuestro primer Card
  2. Diseñando un Card con imagen
  3. Card con animación de loading
  4. Tu turno
  5. El código

1. Creando nuestro primer Card

En Flutter un Card no es más que un widget que nos proporciona el aspecto visual de una tarjeta. Crear un card en Flutter se reduce a usar el widget Card(), al cual mediante sus propiedades le daremos el aspecto que queramos. A continuación creamos un método que nos devuelve un card de tal forma que podamos utilizar el card simplemente llamando a este método.

Es importante mencionar que un Card no es más que una especie de contenedor con algunas caracteristicas que lo hacen especial. Al fin al cabo tenemos que diseñar el Card utilizando Column o Row para colocar y ordenar elementos en su interior. En este ejemplo usamos el widget ListTile que ya lo vimos en el tutorial de ListView en Flutter.
Card miCard() {
  return Card(

    // Con esta propiedad modificamos la forma de nuestro card
    // Aqui utilizo RoundedRectangleBorder para proporcionarle esquinas circulares al Card
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),

    // Con esta propiedad agregamos margen a nuestro Card
    // El margen es la separación entre widgets o entre los bordes del widget padre e hijo
    margin: EdgeInsets.all(15),

    // Con esta propiedad agregamos elevación a nuestro card
    // La sombra que tiene el Card aumentará
    elevation: 10,

    // La propiedad child anida un widget en su interior
    // Usamos columna para ordenar un ListTile y una fila con botones
    child: Column(
      children: <Widget>[

        // Usamos ListTile para ordenar la información del card como titulo, subtitulo e icono
        ListTile(
          contentPadding: EdgeInsets.fromLTRB(15, 10, 25, 0),
          title: Text('Titulo'),
          subtitle: Text(
              'Este es el subtitulo del card. Aqui podemos colocar descripción de este card.'),
          leading: Icon(Icons.home),
        ),

        // Usamos una fila para ordenar los botones del card
        Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: <Widget>[
            FlatButton(onPressed: () => {}, child: Text('Aceptar')),
            FlatButton(onPressed: () => {}, child: Text('Cancelar'))
          ],
        )
      ],
    ),
  );
}

Por ultimo podemos llamar al método miCard() cada vez que queremos un Card dentro de nuestra aplicación. De esta forma podríamos crear un ListView de cards, por ejemplo.

2. Diseñando un Card con imagen

Este tipo de cards también son muy usados y de seguro lo viste en alguna aplicación. Tiene una imagen principal y debajo una descripción. Para lograr esto utilizaremos el widget Image para la imagen y Container para la descripción de la imagen. Además aquí haremos uso del widget ClipRRect que es un widget que recorta las esquinas de sus widgets hijos, si lo que queremos son bordes redondeados. Así que pasemos a explicar el código donde creamos un método que devuelve un Card con este diseño.

Card miCardImage() {
  return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
      margin: EdgeInsets.all(15),
      elevation: 10,

      // Dentro de esta propiedad usamos ClipRRect
      child: ClipRRect(

        // Los bordes del contenido del card se cortan usando BorderRadius
        borderRadius: BorderRadius.circular(30),

        // EL widget hijo que será recortado segun la propiedad anterior
        child: Column(
          children: <Widget>[

            // Usamos el widget Image para mostrar una imagen
            Image(

              // Como queremos traer una imagen desde un url usamos NetworkImage
              image: NetworkImage(
                  'https://www.yourtrainingedge.com/wp-content/uploads/2019/05/background-calm-clouds-747964.jpg'),
            ),

            // Usamos Container para el contenedor de la descripción
            Container(
              padding: EdgeInsets.all(10),
              child: Text('Montañas'),
            ),
          ],
        ),
      ));
}

El widget Container tiene la particularidad de adaptarse al widget padre que lo contiene como propiedad child o children, pero si no está dentro del child de algún Widget su tamaño se adapta al Widget que está dentro de su propio child. En nuestro ejemplo el Container se adapta al widget padre, el cual a su vez se adaptó al ancho de nuestra pantalla menos los márgenes que agregamos anteriormente.

Otro punto a aclarar es que seguimos usando RoundedRectangleBorder en el shape de nuestro Card porque si bien con ClipRRect cortamos las esquinas de la imagen y el Container, la esquina del Card sigue siendo recto. Así que si no usamos este método tendríamos una cosa rara con una imagen de esquinas redondeados pero fondo con esquinas cuadradas.

3. Card con animación de loading

Al traer imágenes de internet la descarga de la imagen puede demorar un poco y en nuestro Card no observaríamos nada hasta que la descarga haya finalizado. Esto puede parecer un poco raro en una aplicación, quizá algún usuario llegaría a creer que la aplicación se detuvo, cuando no es así. Por este motivo en Flutter existe el widget llamado FadeInImage() que por medio de su propiedad placeholder podemos colocar un gif o imagen almacenado localmente para mostrarlo mientras la imagen se descarga de internet.

Recuerda que al agregar imágenes o archivos externos a nuestro proyecto, debemos también agregar una referencia de este en el archivo pubspec.yaml, de esto ya hablamos en el tutorial ListView en Flutter, que te recomiendo revisarlo.
  Card miCardImageCarga() {
    return Card(
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
        margin: EdgeInsets.all(15),
        elevation: 10,
        child: ClipRRect(
          borderRadius: BorderRadius.circular(30),
          child: Column(
            children: <Widget>[
              FadeInImage(

                // En esta propiedad colocamos la imagen a descargar
                image: NetworkImage(
                    'https://staticuestudio.blob.core.windows.net/buhomag/2016/03/01195417/pexels-com.jpg'),

                // En esta propiedad colocamos el gif o imagen de carga
                // debe estar almacenado localmente
                placeholder: AssetImage('assets/loading.gif'),

                // En esta propiedad colocamos mediante el objeto BoxFit
                // la forma de acoplar la imagen en su contenedor
                fit: BoxFit.cover,

                // En esta propiedad colocamos el alto de nuestra imagen
                height: 260,
              ),
              Container(
                padding: EdgeInsets.all(10),
                child: Text('Paisaje con carga'),
              )
            ],
          ),
        ));
  }

Perfecto!! Como ves no es difícil crear Cards para tu aplicación o proyecto Flutter. Personalmente, creo que lo difícil es diseñar los cards y escoger la disposición correcta para los cards dentro de la pantalla.

4. Tu turno

Ya sabemos como crear un Card para diferentes contexto, para información, para una imagen local y para una imagen traída de internet con animación de carga. Ahora es tu turno de aplicar todos estos conocimientos y crear tu propia aplicación con varios de estos Cards. El ejemplo que yo tengo es el siguiente.

Cuando quieras puedes ver el código de este proyecto de ejemplo. Pero ándale inténtalo tu mismo!!

5. El código

El código fuente de la aplicación la podemos encontrar en el siguiente repositorio: github.com/andygeek/cards_app_flutter