Diferencia entre revisiones de «Flutter StatefulWidget. Usos»
Ir a la navegación
Ir a la búsqueda
| Línea 1: | Línea 1: | ||
| − | + | ==Scroll infinito aplicado a ListView== | |
| − | * Paso de datos entre Widgets | + | |
| + | * En este punto vamos a aprender como podemos cargar de forma dinámica un número indeterminado de elementos que se van a visualizar en una lista. | ||
| + | : Para ello partimos del ejemplo realizado [https://wiki.cifprodolfoucha.es/index.php?title=Flutter_Widget_Componentes#ListView.separated en la UD3 - Componentes => ListView.separated]. | ||
| + | : En dicho ejemplo disponíamos de un StatelessWidget que visualizaba con un ListView.separated, un conjunto de datos en una lista. | ||
| + | |||
| + | * Lo que voy a hacer en este ejemplo es hacer crecer la lista de forma dinámica, añadiendo nuevos elementos a la misma, al llegar al final de los elementos cargados actualmente. | ||
| + | : Para ello voy a hacer alguna modificación al código. | ||
| + | :* Convertiré el StatelessWidget en un StatefulWidget, ya que al añadir nuevos elementos a la lista, voy a necesitar redibujar el Widget. | ||
| + | |||
| + | |||
| + | * Creamos una nueva página en la [https://wiki.cifprodolfoucha.es/index.php?title=Flutter_Widget#Estructura_de_p.C3.A1ginas carpeta pages/stateful] de nombre '''listasinfinite_page.dart''' | ||
| + | : En todas las páginas haremos lo mismo: | ||
| + | :* Importamos la librería de 'material' (recordar que existe un [https://github.com/marius-h/flutter_enhancement_suite/blob/main/SNIPPETS.md#flutter-widget-snippets snippet] que lo hace automático). | ||
| + | :* Creamos una clase (ListasInfinitePage) que derive de '''StatefulWidget'''. Se creará (si lo hacéis con un Snippet) una clase asociada que deriva de State. | ||
| + | :* Modificamos la clase State y en su métod build hacemos que devuelva un Scaffold con una Appbar (hacerlo con un Snippet) | ||
| + | |||
| + | |||
| + | |||
| + | [[Imagen:Flutter_dart_scrollinifinito_1.JPG|300px|center]] | ||
| + | |||
| + | |||
| + | * Por lo tanto partimos del siguiente código (el atributo _datos no tiene ningún dato, ya que los iremos cargando dinámicamente): | ||
| + | :<syntaxhighlight lang="java" enclose="div" highlight=""> | ||
| + | import 'package:flutter/material.dart'; | ||
| + | |||
| + | class ListasInfinitePage extends StatefulWidget { | ||
| + | const ListasInfinitePage({Key? key}) : super(key: key); | ||
| + | |||
| + | @override | ||
| + | _ListasInfinitePageState createState() => _ListasInfinitePageState(); | ||
| + | } | ||
| + | |||
| + | class _ListasInfinitePageState extends State<ListasInfinitePage> { | ||
| + | final _datos = <String>[]; | ||
| + | |||
| + | @override | ||
| + | Widget build(BuildContext context) { | ||
| + | return Scaffold( | ||
| + | appBar: AppBar( | ||
| + | title: Text('Ejemplo de listas con ListView.separated'), | ||
| + | ), | ||
| + | body: _obtenerLista() | ||
| + | ); | ||
| + | } | ||
| + | |||
| + | Widget _obtenerLista(){ | ||
| + | |||
| + | return ListView.separated( | ||
| + | itemBuilder: (buildContext, index) => _elementoLista(index), | ||
| + | separatorBuilder: (buildContext, index) => Divider(thickness: 10,color: Colors.green,), | ||
| + | itemCount: _datos.length); | ||
| + | } | ||
| + | |||
| + | Widget _elementoLista(pos) { | ||
| + | var elemLista = _datos.elementAt(pos); | ||
| + | |||
| + | return ListTile( | ||
| + | title: Text(elemLista), | ||
| + | subtitle: Text('Texto aclaratorio de $elemLista'), | ||
| + | leading: Icon(Icons.accessibility), | ||
| + | trailing: Icon(Icons.more_vert), | ||
| + | onTap: () {}, | ||
| + | ); | ||
| + | } | ||
| + | |||
| + | |||
| + | } | ||
| + | <syntaxhighlight> | ||
| + | |||
| + | |||
| + | |||
| + | * Primero voy a crear un método que permita añadir un número de elementos determinado a la lista. Lógicamente, al añadirse, se tendrá que llamar al método setState para que redibuje el StatefulWidget: | ||
| + | |||
| + | :<syntaxhighlight lang="java" enclose="div" highlight=""> | ||
| + | /** | ||
| + | * Añade el número de elementos indicados a la lista | ||
| + | */ | ||
| + | void _addElementosLista(int num_elementos){ | ||
| + | int ultimoElemento = _datos.length; | ||
| + | for(int cont=ultimoElemento; cont < ultimoElemento+ num_elementos; cont++){ | ||
| + | _datos.add('Elem $cont'); | ||
| + | } | ||
| + | |||
| + | setState(() { | ||
| + | |||
| + | }); | ||
| + | } | ||
| + | <syntaxhighlight> | ||
| + | |||
| + | |||
| + | * Al iniciarse el StatefulWidget, voy a sobreescribir el [https://api.flutter.dev/flutter/widgets/State/initState.html método initState()] para que cargue datos la lista. | ||
| + | : Recordar que ya comenté en la [https://wiki.cifprodolfoucha.es/index.php?title=Flutter_StatefulWidget._Eventos sección de Eventos] algo acerca de ciclo de vida de los Widget´s | ||
| + | :<syntaxhighlight lang="java" enclose="div" highlight="3,5-10"> | ||
| + | class _ListasInfinitePageState extends State<ListasInfinitePage> { | ||
| + | final _datos = <String>[]; | ||
| + | final _numElementosListaAdd = 10; | ||
| + | |||
| + | @override | ||
| + | void initState() { | ||
| + | // TODO: implement initState | ||
| + | super.initState(); | ||
| + | _addElementosLista(_numElementosListaAdd); | ||
| + | } | ||
| + | ............. | ||
| + | <syntaxhighlight> | ||
| + | |||
| + | |||
| + | * Ahora lo que tengo que hacer es 'controlar' cuando llego al final de la lista, para que en ese caso, cargar otro conjunto de elementos llamando al método. | ||
| + | : Para ello hacemos uso del [https://api.flutter.dev/flutter/widgets/ScrollController-class.html ScrollController]. | ||
| + | |||
| + | : Los pasos son parecidos a los que ya hicimos con [https://wiki.cifprodolfoucha.es/index.php?title=Flutter_StatefulWidget._Formularios#Modificando_contenido_de_un_TextField._TextEditingController con el TextField, empleando el TextEditingController]. | ||
| + | |||
| + | :* Creamos un objeto de la clase ScrollController. | ||
| + | :* Asociamos el controlador a la lista, con la propiedad del constructor '''controller'''. | ||
| + | : En el método initState, 'nos subscribimos' a los cambios de scroll que haya en la lista. | ||
| + | |||
| + | :<syntaxhighlight lang="java" enclose="div" highlight="5-10,13-15,23"> | ||
| + | class _ListasInfinitePageState extends State<ListasInfinitePage> { | ||
| + | final _datos = <String>[]; | ||
| + | final _numElementosListaAdd = 10; | ||
| + | |||
| + | ScrollController _scrollController = ScrollController(); | ||
| + | |||
| + | @override | ||
| + | void initState() { | ||
| + | // TODO: implement initState | ||
| + | super.initState(); | ||
| + | |||
| + | _addElementosLista(_numElementosListaAdd); | ||
| + | _scrollController.addListener(() { | ||
| + | print('SCROLL'); | ||
| + | }); | ||
| + | |||
| + | } | ||
| + | ................. | ||
| + | |||
| + | Widget _obtenerLista(){ | ||
| + | |||
| + | return ListView.separated( | ||
| + | controller: _scrollController, | ||
| + | itemBuilder: (buildContext, index) => _elementoLista(index), | ||
| + | separatorBuilder: (buildContext, index) => Divider(thickness: 10,color: Colors.green,), | ||
| + | itemCount: _datos.length); | ||
| + | } | ||
| + | |||
| + | ................... | ||
| + | <syntaxhighlight> | ||
| + | |||
| + | |||
| + | |||
| + | :* Ahora podemos acceder a los [https://api.flutter.dev/flutter/widgets/ScrollPosition-class.html métodos de la clase ScrollPosition] accediendo con scrollController.position. | ||
| + | :: En esa clase están definidas muchas propiedades. Una de ellas es [https://api.flutter.dev/flutter/widgets/ScrollPosition/pixels.html pixels] que nos devuelve la posición actual en pixeles, y [https://api.flutter.dev/flutter/widgets/ScrollPosition/maxScrollExtent.html maxScrollExtent] que nos devuelve la posición máxima en pixeles. Por tanto, cuando ambos valores sean iguales significa que hemos llegado al final del scroll de la lista y podremos añadir un nuevo grupo de elementos. | ||
| + | |||
| + | |||
| + | |||
| + | '''Código completo listasinfinite_page.dart:''' | ||
| + | :<syntaxhighlight lang="java" enclose="div" highlight=""> | ||
| + | import 'package:flutter/material.dart'; | ||
| + | |||
| + | class ListasInfinitePage extends StatefulWidget { | ||
| + | const ListasInfinitePage({Key? key}) : super(key: key); | ||
| + | |||
| + | @override | ||
| + | _ListasInfinitePageState createState() => _ListasInfinitePageState(); | ||
| + | } | ||
| + | |||
| + | class _ListasInfinitePageState extends State<ListasInfinitePage> { | ||
| + | final _datos = <String>[]; | ||
| + | final _numElementosListaAdd = 10; | ||
| + | |||
| + | ScrollController _scrollController = ScrollController(); | ||
| + | |||
| + | @override | ||
| + | void initState() { | ||
| + | // TODO: implement initState | ||
| + | super.initState(); | ||
| + | |||
| + | _addElementosLista(_numElementosListaAdd); | ||
| + | _scrollController.addListener(() { | ||
| + | if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { | ||
| + | _addElementosLista(_numElementosListaAdd); | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | } | ||
| + | |||
| + | @override | ||
| + | Widget build(BuildContext context) { | ||
| + | return Scaffold( | ||
| + | appBar: AppBar( | ||
| + | title: Text('Ejemplo de listas con ListView.separated'), | ||
| + | ), | ||
| + | body: _obtenerLista() | ||
| + | ); | ||
| + | } | ||
| + | |||
| + | Widget _obtenerLista(){ | ||
| + | |||
| + | return ListView.separated( | ||
| + | controller: _scrollController, | ||
| + | itemBuilder: (buildContext, index) => _elementoLista(index), | ||
| + | separatorBuilder: (buildContext, index) => Divider(thickness: 10,color: Colors.green,), | ||
| + | itemCount: _datos.length); | ||
| + | } | ||
| + | |||
| + | Widget _elementoLista(pos) { | ||
| + | var elemLista = _datos.elementAt(pos); | ||
| + | |||
| + | return ListTile( | ||
| + | title: Text(elemLista), | ||
| + | subtitle: Text('Texto aclaratorio de $elemLista'), | ||
| + | leading: Icon(Icons.accessibility), | ||
| + | trailing: Icon(Icons.more_vert), | ||
| + | onTap: () {}, | ||
| + | ); | ||
| + | } | ||
| + | |||
| + | /** | ||
| + | * Añade el número de elementos indicados a la lista | ||
| + | */ | ||
| + | void _addElementosLista(int num_elementos){ | ||
| + | int ultimoElemento = _datos.length; | ||
| + | for(int cont=ultimoElemento; cont < ultimoElemento+ num_elementos; cont++){ | ||
| + | _datos.add('Elem $cont'); | ||
| + | } | ||
| + | |||
| + | setState(() { | ||
| + | |||
| + | }); | ||
| + | } | ||
| + | |||
| + | } | ||
| + | <syntaxhighlight> | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | <br /> | ||
| + | ==Paso de datos entre Widgets== | ||
:* https://medium.com/flutter-community/simple-ways-to-pass-to-and-share-data-with-widgets-pages-f8988534bd5b | :* https://medium.com/flutter-community/simple-ways-to-pass-to-and-share-data-with-widgets-pages-f8988534bd5b | ||
:* https://stackoverflow.com/questions/49491860/flutter-how-to-correctly-use-an-inherited-widget/49492495#49492495 | :* https://stackoverflow.com/questions/49491860/flutter-how-to-correctly-use-an-inherited-widget/49492495#49492495 | ||
Revisión del 16:50 23 sep 2021
Scroll infinito aplicado a ListView
- En este punto vamos a aprender como podemos cargar de forma dinámica un número indeterminado de elementos que se van a visualizar en una lista.
- Para ello partimos del ejemplo realizado en la UD3 - Componentes => ListView.separated.
- En dicho ejemplo disponíamos de un StatelessWidget que visualizaba con un ListView.separated, un conjunto de datos en una lista.
- Lo que voy a hacer en este ejemplo es hacer crecer la lista de forma dinámica, añadiendo nuevos elementos a la misma, al llegar al final de los elementos cargados actualmente.
- Para ello voy a hacer alguna modificación al código.
- Convertiré el StatelessWidget en un StatefulWidget, ya que al añadir nuevos elementos a la lista, voy a necesitar redibujar el Widget.
- Creamos una nueva página en la carpeta pages/stateful de nombre listasinfinite_page.dart
- En todas las páginas haremos lo mismo:
- Importamos la librería de 'material' (recordar que existe un snippet que lo hace automático).
- Creamos una clase (ListasInfinitePage) que derive de StatefulWidget. Se creará (si lo hacéis con un Snippet) una clase asociada que deriva de State.
- Modificamos la clase State y en su métod build hacemos que devuelva un Scaffold con una Appbar (hacerlo con un Snippet)
- Por lo tanto partimos del siguiente código (el atributo _datos no tiene ningún dato, ya que los iremos cargando dinámicamente):
- <syntaxhighlight lang="java" enclose="div" highlight="">
import 'package:flutter/material.dart';
class ListasInfinitePage extends StatefulWidget {
const ListasInfinitePage({Key? key}) : super(key: key);
@override _ListasInfinitePageState createState() => _ListasInfinitePageState();
}
class _ListasInfinitePageState extends State<ListasInfinitePage> {
final _datos = <String>[];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Ejemplo de listas con ListView.separated'),
),
body: _obtenerLista()
);
}
Widget _obtenerLista(){
return ListView.separated(
itemBuilder: (buildContext, index) => _elementoLista(index),
separatorBuilder: (buildContext, index) => Divider(thickness: 10,color: Colors.green,),
itemCount: _datos.length);
}
Widget _elementoLista(pos) {
var elemLista = _datos.elementAt(pos);
return ListTile(
title: Text(elemLista),
subtitle: Text('Texto aclaratorio de $elemLista'),
leading: Icon(Icons.accessibility),
trailing: Icon(Icons.more_vert),
onTap: () {},
);
}
}
<syntaxhighlight>
- Primero voy a crear un método que permita añadir un número de elementos determinado a la lista. Lógicamente, al añadirse, se tendrá que llamar al método setState para que redibuje el StatefulWidget:
- <syntaxhighlight lang="java" enclose="div" highlight="">
/**
* Añade el número de elementos indicados a la lista
*/
void _addElementosLista(int num_elementos){
int ultimoElemento = _datos.length;
for(int cont=ultimoElemento; cont < ultimoElemento+ num_elementos; cont++){
_datos.add('Elem $cont');
}
setState(() {
}); }
<syntaxhighlight>
- Al iniciarse el StatefulWidget, voy a sobreescribir el método initState() para que cargue datos la lista.
- Recordar que ya comenté en la sección de Eventos algo acerca de ciclo de vida de los Widget´s
- <syntaxhighlight lang="java" enclose="div" highlight="3,5-10">
class _ListasInfinitePageState extends State<ListasInfinitePage> {
final _datos = <String>[]; final _numElementosListaAdd = 10;
@override
void initState() {
// TODO: implement initState
super.initState();
_addElementosLista(_numElementosListaAdd);
}
.............
<syntaxhighlight>
- Ahora lo que tengo que hacer es 'controlar' cuando llego al final de la lista, para que en ese caso, cargar otro conjunto de elementos llamando al método.
- Para ello hacemos uso del ScrollController.
- Los pasos son parecidos a los que ya hicimos con con el TextField, empleando el TextEditingController.
- Creamos un objeto de la clase ScrollController.
- Asociamos el controlador a la lista, con la propiedad del constructor controller.
- En el método initState, 'nos subscribimos' a los cambios de scroll que haya en la lista.
- <syntaxhighlight lang="java" enclose="div" highlight="5-10,13-15,23">
class _ListasInfinitePageState extends State<ListasInfinitePage> {
final _datos = <String>[]; final _numElementosListaAdd = 10;
ScrollController _scrollController = ScrollController();
@override
void initState() {
// TODO: implement initState
super.initState();
_addElementosLista(_numElementosListaAdd);
_scrollController.addListener(() {
print('SCROLL');
});
} .................
Widget _obtenerLista(){
return ListView.separated(
controller: _scrollController,
itemBuilder: (buildContext, index) => _elementoLista(index),
separatorBuilder: (buildContext, index) => Divider(thickness: 10,color: Colors.green,),
itemCount: _datos.length);
}
...................
<syntaxhighlight>
- Ahora podemos acceder a los métodos de la clase ScrollPosition accediendo con scrollController.position.
- En esa clase están definidas muchas propiedades. Una de ellas es pixels que nos devuelve la posición actual en pixeles, y maxScrollExtent que nos devuelve la posición máxima en pixeles. Por tanto, cuando ambos valores sean iguales significa que hemos llegado al final del scroll de la lista y podremos añadir un nuevo grupo de elementos.
Código completo listasinfinite_page.dart:
- <syntaxhighlight lang="java" enclose="div" highlight="">
import 'package:flutter/material.dart';
class ListasInfinitePage extends StatefulWidget {
const ListasInfinitePage({Key? key}) : super(key: key);
@override _ListasInfinitePageState createState() => _ListasInfinitePageState();
}
class _ListasInfinitePageState extends State<ListasInfinitePage> {
final _datos = <String>[]; final _numElementosListaAdd = 10;
ScrollController _scrollController = ScrollController();
@override
void initState() {
// TODO: implement initState
super.initState();
_addElementosLista(_numElementosListaAdd);
_scrollController.addListener(() {
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
_addElementosLista(_numElementosListaAdd);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Ejemplo de listas con ListView.separated'),
),
body: _obtenerLista()
);
}
Widget _obtenerLista(){
return ListView.separated(
controller: _scrollController,
itemBuilder: (buildContext, index) => _elementoLista(index),
separatorBuilder: (buildContext, index) => Divider(thickness: 10,color: Colors.green,),
itemCount: _datos.length);
}
Widget _elementoLista(pos) {
var elemLista = _datos.elementAt(pos);
return ListTile(
title: Text(elemLista),
subtitle: Text('Texto aclaratorio de $elemLista'),
leading: Icon(Icons.accessibility),
trailing: Icon(Icons.more_vert),
onTap: () {},
);
}
/**
* Añade el número de elementos indicados a la lista
*/
void _addElementosLista(int num_elementos){
int ultimoElemento = _datos.length;
for(int cont=ultimoElemento; cont < ultimoElemento+ num_elementos; cont++){
_datos.add('Elem $cont');
}
setState(() {
}); }
} <syntaxhighlight>