1. Introducción
Los componentes de Material (MDC) ayudan a los desarrolladores a implementar Material Design. MDC, creado por un equipo de ingenieros y diseñadores de UX en Google, cuenta con decenas de componentes de IU atractivos y funcionales, y está disponible para Android, iOS, la Web y Flutter.material.io/develop. |
En el codelab MDC-101, usaste dos componentes de Material para crear una página de acceso: campos de texto y botones con efectos de tinta. Ahora vamos a agregar navegación, estructura y datos para expandir esta base.
Qué compilarás
En este codelab, compilarás una pantalla principal para una app llamada Shrine, una aplicación de comercio electrónico en la que se vende ropa y artículos para el hogar. Contiene lo siguiente:
- Una barra superior de la app
- Una lista de cuadrícula llena de productos
Android | iOS |
Componentes y subsistemas de Flutter de Material en este codelab
- Barra superior de la app
- Cuadrículas
- Tarjetas
¿Cómo calificarías tu nivel de experiencia con el desarrollo de Flutter?
2. Configura tu entorno de desarrollo de Flutter
Para completar este lab, necesitas dos programas de software: el SDK de Flutter y un editor.
Puedes ejecutar el codelab con cualquiera de estos dispositivos o modalidades:
- Un dispositivo físico Android o iOS conectado a tu computadora y configurado en el Modo de desarrollador
- El simulador de iOS (requiere instalar las herramientas de Xcode)
- Android Emulator (requiere configuración en Android Studio)
- Un navegador (se requiere Chrome para la depuración)
- Como una aplicación para computadoras que ejecuten Windows, Linux o macOS (debes desarrollarla en la plataforma donde tengas pensado realizar la implementación; por lo tanto, si quieres desarrollar una app de escritorio para Windows, debes desarrollarla en ese SO a fin de obtener acceso a la cadena de compilación correcta; encuentra detalles sobre los requisitos específicos del sistema operativo en docs.flutter.dev/desktop).
3. Descarga la app de partida del codelab
¿Vienes de MDC-101?
Si completaste MDC-101, tu código debería estar preparado para este codelab. Ve al paso Cómo agregar una barra superior de la app.
¿Empiezas de cero?
Descarga la app de inicio del codelab
La app de inicio se encuentra en el directorio material-components-flutter-codelabs-102-starter_and_101-complete/mdc_100_series
.
… o clónalo desde GitHub
Para clonar este codelab desde GitHub, ejecuta los siguientes comandos:
git clone https://github.com/material-components/material-components-flutter-codelabs.git cd material-components-flutter-codelabs/mdc_100_series git checkout 102-starter_and_101-complete
Abre el proyecto y ejecuta la app
- Abre el proyecto en el editor que prefieras.
- Sigue las instrucciones para “ejecutar la app” en Get Started: Test drive en el editor que elegiste.
Listo Deberías ver la página de acceso a Shrine del codelab MDC-101 en tu dispositivo.
Android | iOS |
Ahora que la pantalla de acceso se ve bien, vamos a propagar la app con algunos productos.
4. Cómo agregar una barra superior de la app
En este momento, si haces clic en el botón "Next", podrás ver la pantalla principal que dice "You did it!". ¡Muy bien! Sin embargo, ahora el usuario no tiene ninguna acción que realizar o que no sepa en qué parte de la app se encuentra. Para ayudarlo, es momento de agregar la navegación.
Material Design ofrece patrones de navegación que garantizan un alto grado de usabilidad. Uno de los componentes más visibles es la barra superior de la app.
Para posibilitar la navegación y brindar a los usuarios acceso rápido a otras acciones, agregaremos una barra superior de la app.
Cómo agregar un widget de AppBar
En home.dart
, agrega una AppBar a Scaffold y quita el elemento const
destacado:
return const Scaffold(
// TODO: Add app bar (102)
appBar: AppBar(
// TODO: Add buttons and title (102)
),
Agregar la AppBar al campo appBar:
de Scaffold nos brinda un diseño perfecto gratis, con AppBar en la parte superior de la página y el cuerpo debajo.
Guarda el proyecto. Cuando se actualice la app de Shrine, haz clic en Next para ver la pantalla principal.
Android | iOS |
AppBar se ve muy bien, pero necesita un título.
Cómo agregar un widget de texto
En home.dart
, agrega un título a AppBar:
// TODO: Add app bar (102)
appBar: AppBar(
// TODO: Add buttons and title (102)
title: const Text('SHRINE'),
// TODO: Add trailing buttons (102)
Guarda el proyecto.
Android | iOS |
Muchas barras de la app tienen un botón junto al título. Agreguemos un ícono de menú a nuestra app.
Cómo agregar un IconButton inicial
Mientras estás en home.dart
, configura un IconButton para el campo leading:
de AppBar. Colócalo antes del campo title:
para imitar el orden de principal a final:
// TODO: Add buttons and title (102)
leading: IconButton(
icon: const Icon(
Icons.menu,
semanticLabel: 'menu',
),
onPressed: () {
print('Menu button');
},
),
Guarda el proyecto.
Android | iOS |
El ícono de menú (también conocido como "ícono de opciones") aparece justo donde lo esperarías.
También puedes agregar botones en el extremo final del título. En Flutter, esto se denomina "acciones".
Cómo agregar acciones
Hay espacio para dos IconButtons más.
Agrégalos a la instancia de AppBar después del título:
// TODO: Add trailing buttons (102)
actions: <Widget>[
IconButton(
icon: const Icon(
Icons.search,
semanticLabel: 'search',
),
onPressed: () {
print('Search button');
},
),
IconButton(
icon: const Icon(
Icons.tune,
semanticLabel: 'filter',
),
onPressed: () {
print('Filter button');
},
),
],
Guarda el proyecto. La pantalla principal debería verse de la siguiente manera:
Android | iOS |
Ahora la app tiene un botón inicial, un título y dos acciones a la derecha. La barra de la app también muestra el valor de elevación mediante una sombra sutil que indica que se encuentra en una capa diferente a la del contenido.
5. Cómo agregar una tarjeta en una cuadrícula
Ahora que nuestra app tiene cierta estructura, coloquemos el contenido en tarjetas para organizarlo.
Cómo agregar una GridView
Comencemos por agregar una tarjeta debajo de la barra superior de la app. El widget de tarjeta por sí solo no cuenta con información suficiente para colocarse donde podamos verlo, por lo que deberíamos encapsularlo en un widget de GridView.
Reemplaza el centro en el cuerpo de Scaffold con una GridView:
// TODO: Add a grid view (102)
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16.0),
childAspectRatio: 8.0 / 9.0,
// TODO: Build a grid of cards (102)
children: <Widget>[Card()],
),
Vamos a desglosar ese código. GridView invoca al constructor count()
, ya que el número de elementos que muestra es contable y no infinito. Pero necesita más información para definir su diseño.
crossAxisCount:
especifica la cantidad de elementos. Queremos 2 columnas.
El campo padding:
proporciona espacio en los 4 lados de GridView. Por supuesto, no puedes ver el relleno en los extremos final o inferior porque todavía no hay elementos secundarios de GridView junto a ellos.
El campo childAspectRatio:
identifica el tamaño de los elementos según una relación de aspecto (ancho sobre altura).
De forma predeterminada, GridView crea mosaicos que tienen el mismo tamaño.
Tenemos una tarjeta, pero está vacía. Vamos a agregarle widgets secundarios.
Cómo diseñar el contenido
Las tarjetas deben tener regiones para una imagen, un título y un texto secundario.
Actualiza los elementos secundarios de GridView:
// TODO: Build a grid of cards (102)
children: <Widget>[
Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 18.0 / 11.0,
child: Image.asset('assets/diamond.png'),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Title'),
const SizedBox(height: 8.0),
Text('Secondary Text'),
],
),
),
],
),
)
],
Este código agrega un widget de columna que se usa para distribuir los widgets secundarios de forma vertical.
crossAxisAlignment: field
especifica CrossAxisAlignment.start
, que significa "alinear el texto al extremo inicial".
El widget de AspectRatio decide qué forma toma la imagen sin importar el tipo de imagen que se proporciona.
El relleno acerca un poco el texto desde el borde.
Los dos widgets de texto se apilan verticalmente con 8 puntos de espacio vacío entre ellos (SizedBox). Creamos otra columna para colocarlos dentro del relleno.
Guarda el proyecto.
Android | iOS |
En esta vista previa, puedes ver que la tarjeta está insertada desde el extremo, con esquinas redondeadas y una sombra (que expresa la elevación de la tarjeta). Toda la forma se denomina "contenedor" en Material. (No debe confundirse con la clase de widget llamada Container).
Las tarjetas suelen mostrarse en una colección con otras tarjetas. Vamos a distribuirlas como una colección en una cuadrícula.
6. Cómo crear una colección de tarjetas
Cuando hay varias tarjetas presentes en una pantalla, se agrupan en una o más colecciones. Las tarjetas en una colección son coplanarias, lo que significa que comparten la misma elevación en reposo que las otras (a menos que se recojan o se arrastren, pero no lo haremos aquí).
Cómo multiplicar la tarjeta en una colección
En este momento, nuestra tarjeta está construida en línea con el campo children:
de GridView. Hay mucho código anidado que puede ser difícil de leer. Vayamos a una función que pueda generar tantas tarjetas vacías como queramos y mostrar una lista de tarjetas.
Crea una nueva función privada encima de la función build()
(recuerda que las funciones que comienzan con un guion bajo son API privadas):
// TODO: Make a collection of cards (102)
List<Card> _buildGridCards(int count) {
List<Card> cards = List.generate(
count,
(int index) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 18.0 / 11.0,
child: Image.asset('assets/diamond.png'),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Text('Title'),
SizedBox(height: 8.0),
Text('Secondary Text'),
],
),
),
],
),
);
},
);
return cards;
}
Asigna las tarjetas generadas al campo children
de GridView. Recuerda reemplazar todo en la GridView con este código nuevo:
// TODO: Add a grid view (102)
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16.0),
childAspectRatio: 8.0 / 9.0,
children: _buildGridCards(10) // Replace
),
Guarda el proyecto.
Android | iOS |
Las tarjetas están allí, pero todavía no muestran nada. Es el momento de agregar datos de productos.
Cómo agregar datos de productos
La app tiene algunos productos con imágenes, nombres y precios. Agreguemos eso a los widgets que ya tengamos en la tarjeta.
Luego, en home.dart
, importa un paquete nuevo y algunos archivos que proporcionamos para un modelo de datos:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'model/product.dart';
import 'model/products_repository.dart';
Por último, cambia _buildGridCards()
para obtener la información del producto y, luego, usa esos datos en las tarjetas:
// TODO: Make a collection of cards (102)
// Replace this entire method
List<Card> _buildGridCards(BuildContext context) {
List<Product> products = ProductsRepository.loadProducts(Category.all);
if (products.isEmpty) {
return const <Card>[];
}
final ThemeData theme = Theme.of(context);
final NumberFormat formatter = NumberFormat.simpleCurrency(
locale: Localizations.localeOf(context).toString());
return products.map((product) {
return Card(
clipBehavior: Clip.antiAlias,
// TODO: Adjust card heights (103)
child: Column(
// TODO: Center items on the card (103)
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 18 / 11,
child: Image.asset(
product.assetName,
package: product.assetPackage,
// TODO: Adjust the box size (102)
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
child: Column(
// TODO: Align labels to the bottom and center (103)
crossAxisAlignment: CrossAxisAlignment.start,
// TODO: Change innermost Column (103)
children: <Widget>[
// TODO: Handle overflowing labels (103)
Text(
product.name,
style: theme.textTheme.titleLarge,
maxLines: 1,
),
const SizedBox(height: 8.0),
Text(
formatter.format(product.price),
style: theme.textTheme.titleSmall,
),
],
),
),
),
],
),
);
}).toList();
}
NOTA: Aún no se compilará ni se ejecutará. Queda un cambio más.
Además, cambia la función build()
para pasar BuildContext a _buildGridCards()
antes de intentar compilar el código:
// TODO: Add a grid view (102)
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16.0),
childAspectRatio: 8.0 / 9.0,
children: _buildGridCards(context) // Changed code
),
Reinicia la app en caliente.
Android | iOS |
Es posible que notes que no agregamos ningún espacio vertical entre las tarjetas. Eso se debe a que, de forma predeterminada, tienen 4 puntos de margen en la parte superior e inferior.
Guarda el proyecto.
Se muestran los datos del producto, pero las imágenes tienen espacio adicional. De forma predeterminada, las imágenes se dibujan con un BoxFit de .scaleDown
(en este caso). Cambiemos eso a .fitWidth
para que se acerquen un poco y se quite el espacio en blanco adicional.
Agrega un campo fit:
a la imagen con un valor de BoxFit.fitWidth
:
// TODO: Adjust the box size (102)
fit: BoxFit.fitWidth,
Android | iOS |
Nuestros productos ya se muestran perfectamente en la app.
7. Felicitaciones
Nuestra app tiene un flujo básico que lleva al usuario de la pantalla de acceso a una pantalla principal en la que se pueden ver los productos. Con unas pocas líneas de código, agregamos una barra superior de la app (con un título y tres botones) y tarjetas (para presentar el contenido de la app). Ahora, la pantalla principal es simple y funcional, y tiene una estructura básica y contenido de acción.
Próximos pasos
Con la barra superior de la app, la tarjeta, el campo de texto y el botón, usamos cuatro componentes principales de la biblioteca de Material de Flutter. Para obtener más información, visita el catálogo de widgets de componentes de Material.
Aunque funciona en su totalidad, nuestra app aún no expresa ninguna marca ni punto de vista en particular. En MDC-103: Temas de Material Design con color, forma, elevación y tipo, personalizaremos el estilo de estos componentes para expresar una marca moderna y llamativa.