Flutter ListView y ScrollPhysics: Una vista detallada
Explorando el Widget ListView y sus propiedades en profundidad.
--
Articulo original escrito por Deven Joshi traducido por Rurick Maqueo
Nota: esta es una traducción integra del articulo original, es posible que algunos links enlacen a paginas en ingles y los enunciados que hablen en primera persona hacen referencia a Deven no a mi.
Hace un tiempo escribí un articulo con el uso básico del ListView y GridView (traducción pendiente). Este articulo está enfocado en dar una exploración mas detallada de la Clase ListView y ScrollPhysics junto con modificaciones y optimización del widget en general.
En Flutter una ListView es una lista lineal deslizable de elementos. Podemos usarla para hacer una lista de elementos desplazable o una lista de elementos repetidos.
Explorando los tipos de ListView
Empezaremos con una pequeña mirada a los tipos de ListView y mas adelante a las demás propiedades y fantásticas modificaciones para estas.
Vamos a ver los tipos de ListView que hay:
- ListView
- ListView.builder
- ListView.separated
- ListView.custom
Vamos a explorar uno por uno:
ListView
Este es el constructor por defecto de la clase ListView. Una ListView simplemente toma una lista de hijos y la hace desplazable.
El formato general del código es:
ListView(
children: <Widget>[
ElementoUno(),
ElementoDos(),
ElementoTres(),
],
),
Usualmente esto debe ser usado con un pequeño numero de hijos ya que el ListView también construye los elementos visibles de la lista y una cantidad grande de elementos podria renderizar de manera ineficiente.
ListView.builder()
El constructor builder() construye una lista de elementos repetidos. El constructor toma dos parámetros: un itemCount para el numero de elementos in la lista y un itemBuilder para construir cada elemento de la lista.
El formato general para el código es:
ListView.builder(
itemCount: numeroElementos,
itemBuilder: (context, posicion) {
return elementoDeLaLista();
},
),
La lista de elementos es construida perezosamente, significa que solo un numero especifico de elementos es construido y cuando el usuario se desplaza los creados anteriormente son destruidos.
Super Truco: Ya que los elementos son cargados perezosamente y solo el numero necesario de elementos es cargado, realmente no necesitamos un itemCount como un parámetro obligatorio, sin éste la lista se hará infinita.
ListView.builder(
itemBuilder: (context, posicion) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(position.toString(), style: TextStyle(fontSize: 22.0),),
),
);
},
),
ListView.separated()
En el constructor separated() vamos a generar una lista y especificaremos el separador que estará entre cada elemento.
En esencia construimos dos listas intercaladas: una es la lista principal y la otra es la lista de separadores.
Nótese que el numero infinito de elementos visto en el constructor anterior no puede ser usado aquí, este constructor obliga a tener un itemCount.
El código para este tipo de lista va así:
ListView.separated(
itemBuilder: (context, posicion) {
return elementoDeLaLista();
},
separatorBuilder: (context, posicion) {
return elementoSeparador();
},
itemCount: numeroElementos,
),
Este tipo de listas te permite definir separadores de manera dinámica, tener diferentes tipos de separadores para diferentes tipos de elementos, agregar o remover separadores cuando sea necesario, etc…
Esta implementación puede ser usada para insertar otro tipo de elementos (publicidad por ejemplo) de manera sencilla y sin ninguna modificación a la lista principal.
Nota: El tamaño de la lista de separadores es menor en 1 que el tamaño de la lista de elementos ya que el separador no existe después del ultimo elemento.
ListView.custom()
El constructor custom(), como su nombre sugiere, te permite construir un ListView con una funcionalidad personalizada en como serán construidos los hijos de la lista. El parámetro principal requiere para esto un SliverChildDelegate que construye los elementos.
Los tipos de SliverChildDelegate son:
- SliverChildListDelegate
- SliverChildBuilderDelegate
SliverChildListDelegate acepta directamente una lista de hijos mientras que SliverChildBuilderDelegate acepta un IndexedWidgetBuilder (la función constructor que usamos).
Puedes usar o subclasear (heredar de la clase) estos para construir tus propios delegados.
ListView.builder es esencialmente un ListView.custom con un SliverChildBuilderDelegate.
El constructor ListView.default se comporta como un ListView.custom con un SliverChildListDelegate.
Ahora que acabamos con los tipo de ListViews vamos a ver las ScrollPhysics.
Explorando ScrollPhysics
Para controlar la manera de hacer scroll configuramos el parámetro physics en el constructor del ListView. Los diferentes tipos son:
NeverScrollablePhysics
NeverScrollablePhysics renderiza la lista de manera no deslizable. Usa esto para des habilitar completamente el desplazamiento del ListView.
BouncingScrollPhysics
BouncingScrollPhysics hace rebotar la lista cuando termina. Es una efecto similar al usado en iOS.
ClampingScrollPhysics
Esta es el ScrollingPhysics usado en android. La lista se detiene al final y muestra un efecto que lo indica.
FixedExtentScrollPhysics
Esta física es un poco diferente a las demás vistas en el sentido de que solo funciona con FixedExtendedScrollControllers y listas que los usen. Por ejemplo, tomaremos una ListWheelScrollView que crea una lista en forma de rueda.
FixedExtentScrollPhysics solo desliza hacia los elementos en lugar de los espacios entre ellos.
El código para este ejemplo es extremadamente simple:
FixedExtentScrollController fixedExtentScrollController =
new FixedExtentScrollController();ListWheelScrollView(
controller: fixedExtentScrollController,
physics: FixedExtentScrollPhysics(),
children: mesesDelAño.map((mes) {
return Card(
child: Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
mes,
style: TextStyle(fontSize: 18.0),
),
)),
],
));
}).toList(),
itemExtent: 60.0,
),
Unas pocas cosas mas por saber
¿Cómo mantener vivos los elementos que son destruidos en una lista?
Flutter nos da un widget llamado KeepAlive() el cual mantiene con vida lo que en otra circunstancia seria destruido. En una lista, los elementos son encapsulados por defecto en un AutomaticKeepAlive widget.
AutomaticKeepAlives puede ser des habilitado configurando el parámetro addAutomaticKeepAlives a false. Esto es útil en casos donde los elementos no necesitan mantenerse con vida o para una implementación personalizada de KeepAlive.
¿Por qué mi ListView tiene un espacio entre la lista y los widgets exteriores
Por defecto, una ListView tiene un padding entre esta y los widgets exteriores, para removerlo solo configura el padding a EdgeInsets.all(0.0).
¡Eso es todo por este articulo! Espero lo hayan disfrutado y que dejen un par de aplausos si es así. Siganme para mas artículos de Flutter y comenten cualquier duda o sugerencia sobre el articulo.