Diferencia entre revisiones de «Flutter StatefulWidget. Conceptos»
Ir a la navegación
Ir a la búsqueda
| Línea 125: | Línea 125: | ||
class _BotonStateful extends StatefulWidget { | class _BotonStateful extends StatefulWidget { | ||
| − | + | final colorAzul = Colors.blue; | |
@override | @override | ||
_BotonStatefulState createState() => _BotonStatefulState(); | _BotonStatefulState createState() => _BotonStatefulState(); | ||
| Línea 131: | Línea 131: | ||
class _BotonStatefulState extends State<_BotonStateful> { | class _BotonStatefulState extends State<_BotonStateful> { | ||
| + | int _contador = 0; | ||
@override | @override | ||
Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||
return Center(child: TextButton(child:Text('Botón Stateful ${widget._contador}'), | return Center(child: TextButton(child:Text('Botón Stateful ${widget._contador}'), | ||
| − | style: TextButton.styleFrom(backgroundColor: | + | style: TextButton.styleFrom(backgroundColor: widget.colorAzul), onPressed: () { setState(() { |
| − | + | _contador++; | |
}); }, | }); }, | ||
) | ) | ||
| Línea 224: | Línea 225: | ||
<br /> | <br /> | ||
| − | ==Ejemplo contador== | + | ==Ejemplos de uso== |
| + | |||
| + | * Voy a explicar a continuación varios ejemplos que irán completando la explicación anterior... | ||
| + | |||
| + | * En todos los ejemplos, salvo que se indique lo contrario, debéis hacer lo mismo: | ||
| + | : Creamos una nueva página en la [https://wiki.cifprodolfoucha.es/index.php?title=Flutter_Widget#Estructura_de_p.C3.A1ginas carpeta pages/stateful/aprender] de nombre '''aprenderNUM_page.dart''' (crear los directorios dentro de lib si no están creados previamente). NUM es el número de ejemplo. | ||
| + | : 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 (AprenderNUMPage) que derive de '''StatefulWidget'''. Se creará (si lo hacéis con un Snippet) una clase asociada que deriva de State. NUM es el número de ejemplo. | ||
| + | :* Modificamos la clase State y en su métod build hacemos que devuelva un Scaffold con una Appbar (hacerlo con un Snippet) | ||
| + | |||
| + | |||
| + | <br /> | ||
| + | ===Ejemplo 1=== | ||
| + | |||
| + | * En este ejemplo, partimos que todo nuestro Widget Scaffold es un StatefulWidget. | ||
| + | : Dentro del mismo, todos los Widget que definamos 'localmente', van poder llamar al método setState para redibujar el StatefulWidget y por tanto redibujará toda la pantalla. | ||
| + | |||
| + | * '''Objetivo:''' Modificar el tamaño (ancho) de un container el pulsar sobre el botón. | ||
| + | |||
| + | [[Imagen:Flutter_dart_exemplo_1_stateful.JPG|250px|center]] | ||
| + | |||
| + | |||
| + | : '''Solución:''' Para lograr este objetivo, necesitamos emplear una variable (ya que su valor va a aumentar al pulsar el botón) y asociar dicha variable al ancho del contenedor. | ||
| + | : Por lo tanto, al pulsar sobre el botón, es necesario modificar el valor de la variable y redibujar el contenedor (en este momento no sabemos hacer eso, sabemos redibujar el StatefulWidget que es la pantalla entera, y por tanto se va a redibujar el botón, el contenedor, la AppBar,es decir, toda la pantalla. | ||
| + | |||
| + | |||
| + | :<syntaxhighlight lang="java" enclose="div" highlight="15, 29,37-39" > | ||
| + | import 'package:flutter/material.dart'; | ||
| + | /** | ||
| + | * Neste exemplo aprendemos a diferenza entre Stateless e Stateful | ||
| + | * Temos que entender como funcionan os Widgets en Flutter. | ||
| + | */ | ||
| + | |||
| + | class Aprender1Page extends StatefulWidget { | ||
| + | const Aprender1Page({Key? key}) : super(key: key); | ||
| + | |||
| + | @override | ||
| + | _Aprender1PageState createState() => _Aprender1PageState(); | ||
| + | } | ||
| + | |||
| + | class _Aprender1PageState extends State<Aprender1Page> { | ||
| + | double _ancho = 50; | ||
| + | |||
| + | @override | ||
| + | Widget build(BuildContext context) { | ||
| + | return Scaffold( | ||
| + | appBar: AppBar( | ||
| + | title: Text('Aprender1 Statefulwidget'), | ||
| + | ), | ||
| + | body: Center( | ||
| + | child: Column( | ||
| + | mainAxisAlignment: MainAxisAlignment.center, | ||
| + | children: [ | ||
| + | Container( | ||
| + | padding: EdgeInsets.all(10), | ||
| + | width: _ancho, | ||
| + | height: 50, | ||
| + | color: Colors.indigo, | ||
| + | ), | ||
| + | TextButton( | ||
| + | child: Text('PRESIONAME'), | ||
| + | style: TextButton.styleFrom(backgroundColor: Colors.black), | ||
| + | onPressed: () { | ||
| + | setState(() { | ||
| + | _ancho+=20; | ||
| + | }); | ||
| + | }, | ||
| + | ) | ||
| + | ], | ||
| + | ), | ||
| + | ) | ||
| + | ); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | </syntaxhighlight> | ||
| + | |||
| + | |||
| + | |||
| + | <br /> | ||
| + | ===Ejemplo 2=== | ||
| + | |||
| + | * En este ejemplo, partimos que todo nuestro Widget Scaffold es un StatefulWidget. | ||
| + | : Dentro del mismo, todos los Widget que definamos 'localmente', van poder llamar al método setState para redibujar el StatefulWidget y por tanto redibujará toda la pantalla. | ||
| + | |||
| + | * En este ejemplo, al pulsar sobre un elemento de la lista, este queda seleccionado visualmente y una etiqueta muestra el valor seleccionado. | ||
| + | |||
| + | :<syntaxhighlight lang="java" enclose="div" highlight="31-60,21-23" > | ||
| + | |||
| + | </syntaxhighlight> | ||
| + | |||
| + | |||
| + | |||
| + | <br /> | ||
| + | ===Ejemplo contador=== | ||
* Podemos analizar ahora el ejemplo 'Hola Mundo' que aparece cuando se instala Flutter y que podemos visualizar en la [https://dartpad.dev/?null_safety=true&id=1a28bdd9203250d3226cc25d512579ec página dartpad.dev] | * Podemos analizar ahora el ejemplo 'Hola Mundo' que aparece cuando se instala Flutter y que podemos visualizar en la [https://dartpad.dev/?null_safety=true&id=1a28bdd9203250d3226cc25d512579ec página dartpad.dev] | ||
| Línea 231: | Línea 327: | ||
<br /> | <br /> | ||
| − | ==Ejemplo de varios StatefulWidget== | + | ===Ejemplo de varios StatefulWidget=== |
* 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 '''varios_stateful.dart''' | * 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 '''varios_stateful.dart''' | ||
Revisión del 17:46 4 nov 2021
Sumario
Introducción
- Más información en:
- Hasta ahora hemos usado los StatelessWidget para todo.
- Este tipo de Widget:
- No permite guardar ningún tipo de dato en forma de variable y cuando definimos algo a nivel de clase siempre debe estar asociado al modificador final o static const.
- Si no se pone dicho modificador, no impide compilar la aplicación y aparecerá un Warning en la definición de la Clase (el nombre de la clase aparecerá subrayada).
- Un ejemplo de uso sería cuando tenemos un formulario y queremos guardar lo escrito por el usuario en una variable. Esto no lo podríamos realizar con este tipo de Widget.
- No se pueden 'redibujar' para hacer que reflejen un cambio que pueda darse por programación. Imagináos el caso de que busquéis datos por Internet y se carguen nuevos datos en la lista en la que se basa un ListView para visualizar el contenido...
- En Flutter, no funciona igual que la programación tradicional con entornos gráficos, en los que se tiene una pantalla con componentes (cajas de texto, listas,...) y cambiando sus valores automáticamente se reflejan dichos cambios en el componente visual.
- En Flutter, los componentes visuales (Widget´s) si visualizan en base a los datos definidos en el Widget. Si dichos datos son cambiados, es necesario redibujar el Widget para que se vuelva a visualizar con los datos modificados.
- Así, si por ejemplo, tengo que una etiqueta (Widget Text) visualiza el contenido de una variable, y cambio el valor de dicha variable, es necesario redibujar el Widget etiqueta para que vuelva a visualizar el contenido cambiado.
Minuto 10: Indico que se debe devolver un EjemploStateful cuando debería ser un _EjemploStatefulState
Código explicativo del vídeo.
import 'package:flutter/material.dart'; class EjemploStateLess extends StatelessWidget { int _valor = 0; // Debería ser final ya que un StatelessWidget está definido como inmutable @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('StateLessWidget'), ), body: _boton() ); } Widget _boton(){ return Center(child: TextButton(child:Text('Presiona $_valor'), onPressed: (){ print('pulsado $_valor'); _valor = _valor + 1 ; // Aunque su valor se modifica nunca podríamos 'visualizar' su contenido actualizado. }, style: TextButton.styleFrom(backgroundColor: Colors.amber), ) ); } } class EjemploStateful extends StatefulWidget { @override _EjemploStatefulState createState() => _EjemploStatefulState(); } class _EjemploStatefulState extends State<EjemploStateful> { int _valor = 10; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('appbarTitle'), ), body: _boton() ); } Widget _boton(){ return Center(child: TextButton(child:Text('Presiona valores $_valor'), onPressed: (){ print('pulsado $_valor'); setState(() { _valor++; }); }, style: TextButton.styleFrom(backgroundColor: Colors.amber), ) ); } }
Accediendo a las propiedes del StatefulWidget desde el State
- Cuando definimos una propiedad local dentro del StatefulWidget, podremos acceder a dicha variable dentro de la clase State empleado la palabra widget.propiedad.
- Veamos un ejemplo en el que definimos la variable contador dentro del StatefulWidget botón.
import 'package:flutter/material.dart'; class EjemploStateLess extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('StateLessWidget'), ), body: Column( children: [ _BotonStateful() // Recordar que esto es equivalente a new _BotonStateless ], ) ); } } class _BotonStateful extends StatefulWidget { final colorAzul = Colors.blue; @override _BotonStatefulState createState() => _BotonStatefulState(); } class _BotonStatefulState extends State<_BotonStateful> { int _contador = 0; @override Widget build(BuildContext context) { return Center(child: TextButton(child:Text('Botón Stateful ${widget._contador}'), style: TextButton.styleFrom(backgroundColor: widget.colorAzul), onPressed: () { setState(() { _contador++; }); }, ) ); } }
- Nota: Lo 'normal' es que las constantes estén definidas en el Stateful (partes invariantes) y las variables en el State asociado.
Combinando Widgets
- Indicar que dentro de una clase podemos tener diferentes Widget de tipo Stateless o Stateful.
- En el siguiente ejemplo, dentro de un StatelessWidget creo dos Widgets botones, uno de tipo StatelessWidget y otro de tipo StatefulWidget.
- Ya veremos posteriormente como podemos pasar datos entre Widgets y entre pantallas diferentes.
- En el ejemplo creo una clase (Datos) que guarda el valor que quiero enviar a los dos botones.
- Al pulsar sobre el StatelesWidget, puedo incrementar el valor de dicha variable, pero no puedo redibujar el Widget, por lo que el contador aumentará pero no se visualizará hasta que pulse sobre el StatefulWidget el cual se redibujará con el nuevo valor.
import 'package:flutter/material.dart'; class _Datos{ int valor = 0; } final dato = new _Datos(); class EjemploStateLess extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('StateLessWidget'), ), body: Column( children: [ _BotonStateless(), // Recordar que esto es equivalente a new _BotonStateless _BotonStateful() ], ) ); } } class _BotonStateful extends StatefulWidget { @override _BotonStatefulState createState() => _BotonStatefulState(); } class _BotonStatefulState extends State<_BotonStateful> { @override Widget build(BuildContext context) { return Center(child: TextButton(child:Text('Botón Stateful ${dato.valor}'), style: TextButton.styleFrom(backgroundColor: Colors.amber), onPressed: () { setState(() { dato.valor++; }); }, ) ); } } class _BotonStateless extends StatelessWidget { @override Widget build(BuildContext context) { return Center(child: TextButton(child:Text('Botón Stateless ${dato.valor}'), style: TextButton.styleFrom(backgroundColor: Colors.amber), onPressed: () { dato.valor++; }, ) ); } }
Ejemplos de uso
- Voy a explicar a continuación varios ejemplos que irán completando la explicación anterior...
- En todos los ejemplos, salvo que se indique lo contrario, debéis hacer lo mismo:
- Creamos una nueva página en la carpeta pages/stateful/aprender de nombre aprenderNUM_page.dart (crear los directorios dentro de lib si no están creados previamente). NUM es el número de ejemplo.
- 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 (AprenderNUMPage) que derive de StatefulWidget. Se creará (si lo hacéis con un Snippet) una clase asociada que deriva de State. NUM es el número de ejemplo.
- Modificamos la clase State y en su métod build hacemos que devuelva un Scaffold con una Appbar (hacerlo con un Snippet)
Ejemplo 1
- En este ejemplo, partimos que todo nuestro Widget Scaffold es un StatefulWidget.
- Dentro del mismo, todos los Widget que definamos 'localmente', van poder llamar al método setState para redibujar el StatefulWidget y por tanto redibujará toda la pantalla.
- Objetivo: Modificar el tamaño (ancho) de un container el pulsar sobre el botón.
- Solución: Para lograr este objetivo, necesitamos emplear una variable (ya que su valor va a aumentar al pulsar el botón) y asociar dicha variable al ancho del contenedor.
- Por lo tanto, al pulsar sobre el botón, es necesario modificar el valor de la variable y redibujar el contenedor (en este momento no sabemos hacer eso, sabemos redibujar el StatefulWidget que es la pantalla entera, y por tanto se va a redibujar el botón, el contenedor, la AppBar,es decir, toda la pantalla.
import 'package:flutter/material.dart'; /** * Neste exemplo aprendemos a diferenza entre Stateless e Stateful * Temos que entender como funcionan os Widgets en Flutter. */ class Aprender1Page extends StatefulWidget { const Aprender1Page({Key? key}) : super(key: key); @override _Aprender1PageState createState() => _Aprender1PageState(); } class _Aprender1PageState extends State<Aprender1Page> { double _ancho = 50; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Aprender1 Statefulwidget'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: EdgeInsets.all(10), width: _ancho, height: 50, color: Colors.indigo, ), TextButton( child: Text('PRESIONAME'), style: TextButton.styleFrom(backgroundColor: Colors.black), onPressed: () { setState(() { _ancho+=20; }); }, ) ], ), ) ); } }
Ejemplo 2
- En este ejemplo, partimos que todo nuestro Widget Scaffold es un StatefulWidget.
- Dentro del mismo, todos los Widget que definamos 'localmente', van poder llamar al método setState para redibujar el StatefulWidget y por tanto redibujará toda la pantalla.
- En este ejemplo, al pulsar sobre un elemento de la lista, este queda seleccionado visualmente y una etiqueta muestra el valor seleccionado.
Ejemplo contador
- Podemos analizar ahora el ejemplo 'Hola Mundo' que aparece cuando se instala Flutter y que podemos visualizar en la página dartpad.dev
Ejemplo de varios StatefulWidget
- Creamos una nueva página en la carpeta pages/stateful/ de nombre varios_stateful.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 que derive de StatelessWidget (al hacerlo será necesario implementar el método build, ya os da un aviso el Android Studio).
- Dentro del método build, retornamos un widget Scaffold (recordar que existe un snippet que lo hace automático).
- En este ejemplo podemos ver como disponemos de 3 Containers, y al pulsarlos, la variable local a dichos contenedores se incrementa.
- Dentro del child del Scaffold retornamos un widget Row, donde situaremos los 3 contenedores.
- Para controlar el evento de pulsar sobre un contenedor hago uso de la clase InkWell que permite tener un efecto al pulsar sobre el contenedor.
import 'package:flutter/material.dart'; class VariosStateFull extends StatelessWidget { const VariosStateFull({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Varios StatefulWidget'), ), body: Center( child: _variosContenedores(), )); } Widget _variosContenedores(){ return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Contenedor(color: Colors.green), Contenedor(color: Colors.red), Contenedor(color: Colors.yellow), ], ); } } class Contenedor extends StatefulWidget { final Color color; const Contenedor({Key? key, required this.color}) : super(key: key); @override _ContenedorState createState() => _ContenedorState(); } class _ContenedorState extends State<Contenedor> { int _contador=0; _ContenedorState(){ } @override Widget build(BuildContext context) { return InkWell( child: Container( child: Text('$_contador'), color: widget.color, width: 50, height: 50, margin: EdgeInsets.all(12.0), ), onTap: (){ setState(() { _contador++; });}, ); } }
- Nota: Veremos más adelante en esta Wiki como pasar datos entre los diferentes Stateful Widget´s.
Ejercicios propuestos
- Crea una variable global que guarde en un lista los valores de los tres contenedores.
- Haz que cada contenedor esté identificado por un id (0,1,2) que se corresponda con la posición de la lista a la que tendrá que acceder a modificar su valor.
- En base al id (0,1,2) haz que al presionar el contenedor, aumente el valor del elemento de la lista y que se visualice en el texto que hay en el interior.
- ¿ Cómo harías para que en el caso de que se actualizara en contador de todos los contenedores al pulsar en cualquiera de ellos ?
Solución ejercicio propuesto:
import 'package:flutter/material.dart'; var datos = <int>[1,2,3]; class VariosStateFull extends StatelessWidget { const VariosStateFull({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Varios StatefulWidget'), ), body: Center( child: _variosContenedores(), )); } Widget _variosContenedores(){ return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Contenedor(color: Colors.green,id: 0), Contenedor(color: Colors.red, id: 1), Contenedor(color: Colors.yellow,id: 2), ], ); } } class Contenedor extends StatefulWidget { final Color color; final int id; const Contenedor({Key? key, required this.color,required this.id}) : super(key: key); @override _ContenedorState createState() => _ContenedorState(); } class _ContenedorState extends State<Contenedor> { int _contador=0; _ContenedorState(){ } @override Widget build(BuildContext context) { return InkWell( child: Container( child: Text('${datos[widget.id]}'), color: widget.color, width: 50, height: 50, margin: EdgeInsets.all(12.0), ), onTap: (){ setState(() { datos[widget.id]++; });}, ); } }
- Pregunta: ¿ Cómo harías para que en el caso de que se actualizara en contador de todos los contenedores al pulsar en cualquiera de ellos ?
- Respuesta: Habría que crear un único StatefulWidget que 'envuelva' a los 3 contenedores.
Enlace a la página principal de la UD4
Enlace a la página principal del curso
-- Ángel D. Fernández González -- (2021).