Creación de Listados Dinámicos en Aplicaciones Flutter
Clase 25 de 30 • Curso de Flutter con Firebase
Resumen
La arquitectura de bloques en Flutter es una técnica poderosa para organizar y estructurar componentes reutilizables en tus aplicaciones. En esta segunda parte del desarrollo de nuestro block builder, exploraremos cómo implementar un listado categorizado según el tipo de datos, complementando la funcionalidad de gráficas que ya habíamos construido anteriormente.
¿Cómo implementar un block builder para mostrar listados categorizados?
Continuando con nuestro proyecto, ahora nos enfocaremos en la sección de "category list". Anteriormente teníamos un list builder simple, pero lo transformaremos en un block builder más robusto que pueda manejar diferentes estados y presentar la información de manera más organizada.
Para comenzar, necesitamos eliminar el list builder existente y reemplazarlo con nuestro block builder. Este componente necesitará acceso a las mismas instancias que hemos estado utilizando:
// Implementación del block builder para category list
builder(context, state) {
// Verificamos si los datos están cargando
if (state is TransactionLoading) {
return const CircularProgressIndicator();
}
// Verificamos si los datos ya están cargados
else if (state is TransactionLoaded) {
// Separamos los datos por categoría
final categoryData = _calculateCategoryTotals(state.transactions);
return ListView.builder(
itemCount: categoryData.length,
itemBuilder: (context, index) {
final category = categoryData.keys.elementAt(index);
final total = categoryData[category];
return Card(
color: Theme.of(context).primaryColorDark,
margin: const EdgeInsets.symmetric(vertical: 8),
child: ListTile(
title: Text(
category == 'income' ? 'Income' : 'Expense',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.foreground,
),
),
trailing: Text(
'\$${total.toString()}',
style: TextStyle(
fontSize: 16,
color: Theme.of(context).colorScheme.foreground,
),
),
),
);
},
);
}
// Verificamos si hay algún error
else if (state is TransactionError) {
return Text(state.message);
}
// Si no hay transacciones
else {
return Center(
child: Text('No transactions'),
);
}
}
¿Cómo calcular los totales por categoría?
Una parte fundamental de nuestro block builder es la función que calcula los totales por categoría. Esta función privada recibe todas las transacciones y las separa según su tipo (ingreso o gasto):
Map<String, double> _calculateCategoryTotals(List<IncomeExpenseModel> transactions) {
// Inicializamos el mapa con valores por defecto
Map<String, double> categoryTotals = {
'income': 0,
'expense': 0,
};
// Recorremos cada transacción y sumamos según su tipo
for (var transaction in transactions) {
final category = transaction.type;
categoryTotals[category] = (categoryTotals[category] ?? 0) + transaction.amount;
}
return categoryTotals;
}
Esta función es crucial porque:
- Inicializa categorías con valor cero para evitar errores si no hay datos.
- Itera sobre cada transacción para clasificarla según su tipo.
- Suma los montos a la categoría correspondiente.
- Retorna un mapa con los totales acumulados por categoría.
¿Cómo mostrar la información de manera atractiva?
Para presentar los datos de manera visualmente atractiva, utilizamos un ListView.builder
que crea tarjetas para cada categoría:
ListView.builder(
itemCount: categoryData.length,
itemBuilder: (context, index) {
final category = categoryData.keys.elementAt(index);
final total = categoryData[category];
return Card(
color: Theme.of(context).primaryColorDark,
margin: const EdgeInsets.symmetric(vertical: 8),
child: ListTile(
title: Text(
category == 'income' ? 'Income' : 'Expense',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.foreground,
),
),
trailing: Text(
'\$${total.toString()}',
style: TextStyle(
fontSize: 16,
color: Theme.of(context).colorScheme.foreground,
),
),
),
);
},
)
Cada tarjeta contiene:
- Un título que muestra si es un ingreso o un gasto.
- Un valor que muestra el monto total de esa categoría.
- Estilos personalizados que siguen el tema de la aplicación.
¿Cómo manejar diferentes estados en nuestra interfaz?
Un aspecto importante de nuestra implementación es el manejo de diferentes estados. Esto proporciona una mejor experiencia de usuario al mostrar:
- Indicador de carga mientras se obtienen los datos.
- Listado categorizado cuando los datos están disponibles.
- Mensaje de error si algo sale mal.
- Mensaje informativo si no hay transacciones.
Este enfoque basado en estados es una práctica recomendada en Flutter, ya que permite que la interfaz responda adecuadamente a diferentes situaciones sin necesidad de lógica compleja en el widget principal.
La implementación completa de nuestro block builder nos permite tener una pantalla de "spending" funcional que muestra claramente los ingresos y gastos categorizados. El siguiente paso sería ajustar la barra de progreso para reflejar estos valores, completando así la funcionalidad de esta pantalla.
La arquitectura de bloques en Flutter no solo mejora la organización del código, sino que también facilita la reutilización de componentes y el manejo de estados complejos. Estas técnicas son fundamentales para desarrollar aplicaciones robustas y mantenibles a largo plazo.
¿Has implementado esta funcionalidad en tu aplicación? Comparte una captura de pantalla en los comentarios para ver tu progreso y discutir posibles mejoras o alternativas.