Diferencia entre revisiones de «Flutter StatefulWidget. Conceptos»
Ir a la navegación
Ir a la búsqueda
| Línea 15: | Línea 15: | ||
:* 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... | :* 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 camnbiando 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. | ||
<br /><br /> | <br /><br /> | ||
Revisión del 17:17 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 camnbiando 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 { int _contador = 0; // Debería ser final, ya que al igual que en StatelessWidget, esta clase es inmutable. Por tanto sólo se debería definir constantes. @override _BotonStatefulState createState() => _BotonStatefulState(); } class _BotonStatefulState extends State<_BotonStateful> { @override Widget build(BuildContext context) { return Center(child: TextButton(child:Text('Botón Stateful ${widget._contador}'), style: TextButton.styleFrom(backgroundColor: Colors.amber), onPressed: () { setState(() { widget._contador++; // No debería hacerse ya que en el StatefullWidget deberían declararse sólo constantes (final) }); }, ) ); } }
- 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++; }, ) ); } }
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.
Ejercicio propuesto:
- 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).