Diferencia entre revisiones de «Flutter StatefulWidget. Usos»

De MediaWiki
Ir a la navegación Ir a la búsqueda
Línea 1: Línea 1:
−
* Scroll infinito aplicado a las ListView
+
==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.


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)


Flutter dart scrollinifinito 1.JPG


  • 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>


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>




Paso de datos entre Widgets