latest

Programación basada en widgets. En profundidad: ListView.

En esta lección de nuestro curso de Flutter vamos a entrar un poco más en detalle en cómo construir una ListView. Trataremos de separar la parte del modelo de datos de la lista, de la vista o apariencia de cada uno de sus widgets ListTile. Cada ListTile, como vimos en la lección anterior, representa una celda / fila de la lista.

Vamos a realizar un ejemplo clásico, similar al que podéis seguir en la documentación de Flutter. Se trata de una ListView que contiene un conjunto finito de elementos, en este caso contactos. Estos contactos, se modelan como una List de objetos que hemos denominado ModeloContacto y que constan de dos atributos (_nombre,_email). Para albergar el ListView, se ha construido un nuevo widget sin estado (de tipo StatelessWidget) que hemos denominado ListaContactos. Este widget es cargado en la propiedad body de un Scaffold en main.dart.

El aspecto final de la lista es el siguiente:

Vamos a ir mostrando las distintas partes de código que constituyen este ejemplo. Primero, partimos de la clase ModeloContacto, de la que hemos hablado con anterioridad y contiene el modelo de datos de cada celda / fila  de la List<ModeloContacto> con la que se compondrá el ListView.

A continuación, se muestra el contenido de lib/modelo/modelocontacto.dart. Observad que se ha creado el paquete modelo para albergar el archivo.

class ModeloContacto{
  String _nombre;
  String _email;

  ModeloContacto({String nombre, String email})
  {
      this._nombre = nombre;
      this._email = email;
  }

  String get nombre => _nombre;
  String get email => _email;
}

Por otro lado,  el contenido de lib/listacontactos.dart, donde se construye el widget sin estado ListaContactos y en cuyo método build() se instancia el widget ListView es el siguiente:

import "package:flutter/material.dart";
import 'package:milista_widget/modelo/modelocontacto.dart';
import 'package:milista_widget/vista/itemcontacto.dart';

class ListaContactos extends StatelessWidget {
  
  List<ModeloContacto> _construirContactos(){
    return <ModeloContacto>[
      ModeloContacto(
        nombre: "David Gilmour", 
        email: "david_gilmour@pinkfloyd.com"),
      ModeloContacto(
        nombre: "Roger Waters", 
        email: "roger_waters@pinkfloyd.com"),
      ModeloContacto(
        nombre: "Richard Wright", 
        email: "richard_wright@pinkfloyd.com"), 
      ModeloContacto(
        nombre: "Nick Mason", 
        email: "nick_mason@pinkfloyd.com"),
      ModeloContacto(
        nombre: "John Lennon", 
        email: "john_lennon@beatles.com"),
      ModeloContacto(
        nombre: "Paul McCartney", 
        email: "paul_mccartney@beatles.com"),
      ModeloContacto(
        nombre: "George Harrison", 
        email: "paul_mccartney@beatles.com"),
      ModeloContacto(
        nombre: "Ringo Starr", 
        email: "ringo_starr@beatles.com"),
      ModeloContacto(
        nombre: "Mick Jagger", 
        email: "mick_jagger@rollingstones.com"),
      ModeloContacto(
        nombre: "Keith Richards", 
        email: "keith_richards@rollingstones.com"),
      ModeloContacto(
        nombre: "Charlie Watts", 
        email: "charlie_watts@rollingstones.com"),
      ModeloContacto(
        nombre: "Ron Wood", 
        email: "ron_wood@rollingstones.com"),
    ];
  }
 
  List<ItemContacto> _construirLista(){
    return _construirContactos().map(
       (contacto) => new ItemContacto(contacto)
    ).toList();
  }
  
  @override
  Widget build(BuildContext context) {
    return new ListView(
      children: _construirLista(),
    );
  }
}

El método _construirContactos() devuelve una lista constituida por objetos de clase ModeloContacto. Por su parte, el método _construirLista() itera sobre cada ModeloContacto de la lista anterior, gracias al método Iterable.map<T>( (ModeloContacto) -> T f) -> Iterable<T>, construyendo un widget ItemContacto por cada ModeloContacto recorrido. Finalmente, retorna un Iterable<ItemContacto> que se convierte en el List<ItemContacto> que recibirá el widget ListView gracias al método toList().

ItemContacto hereda de un StatelessWidget y representa el aspecto visual (Vista) de cada celda / fila de la ListView. Su código, en lib/vista/itemcontacto.dart es el siguiente:

import "package:flutter/material.dart";
import 'package:milista_widget/modelo/modelocontacto.dart';

class ItemContacto extends StatelessWidget {
  
  final ModeloContacto _contacto;

  ItemContacto(this._contacto);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: new CircleAvatar(
        child: new Text(_contacto.nombre[0]),
        // child: new Text(_contacto.nombre.substring(0,1)),
        foregroundColor: Colors.white,
        backgroundColor: Colors.orangeAccent,
        // backgroundImage: ,
      ),
      title: new Text(_contacto.nombre),
      subtitle: new Text(_contacto.email),
    );
  }
}

Básicamente, el código anterior construye un widget ListTile y lo retorna. Se aprovechan alguna de las propiedades de este widget para crear el aspecto visual de cada fila / celda que tendrá la ListView. A grandes rasgos, en la propiedad leading se añade un icono circular CircleAvatar con un texto que representa la primera letra del nombre del contacto, también se añade un título (title), que representa el nombre completo del contacto y un subtítulo (subtitle) para contener su e-mail.

Por último, para usar el widget ListaContactos en lib/main.dart lo añadimos a la propiedad body de un Scafold:

import 'package:flutter/material.dart';
import 'package:milista_widget/listacontactos.dart';

void main() {
  runApp(
    new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text("Contactos"),
          backgroundColor: Colors.orangeAccent,
        ),
        body: new ListaContactos(),
      ),
    )
  );
}

En la siguiente sesión de nuestro curso de Flutter abordaremos otra forma diferente de construir una ListView, además, esta lista obtendrá datos bajo demanda de un servidor remoto (concretamente imágenes) que irá renderizando de forma paulatina a medida que vamos haciendo scroll sobre ella. Es decir, inicialmente la lista sólo contará con unos pocos items, pero a medida que se haga scroll, se irán haciendo peticiones al servidor e irá creciendo.

Author image
Iván González is postdoctoral researcher at the Castilla-La Mancha University.
Ciudad Real (Spain)