Flutter - Gestión de estado efímero

Dado que la aplicación Flutter se compone de widgets, la gestión del estado también se realiza mediante widgets. El punto de entrada de la gestión estatal es Statefulwidget. El widget se puede heredar de Statefulwidget para mantener su estado y su estado secundario. Statefulwidget proporciona una opción para que un widget cree un estado, State(donde T es el widget heredado) cuando el widget se crea por primera vez a través del método createState y luego un método, setState para cambiar el estado cuando sea necesario. El cambio de estado se hará mediante gestos. Por ejemplo, la calificación de un producto se puede cambiar tocando una estrella en el widget de calificación.

Creemos un widget, RatingBox con mantenimiento de estado. El propósito del widget es mostrar la calificación actual de un producto específico. El proceso paso a paso para crear un widget RatingBox con mantenimiento de estado es el siguiente:

  • Cree el widget, RatingBox heredando StatefulWidget.

class RatingBox extends StatefulWidget { }
  • Cree un estado para RatingBox, _RatingBoxState heredando State <T>

class _RatingBoxState extends State<RatingBox> { }
  • Anule el método createState de StatefulWidget para crear el estado, _RatingBoxState.

class RatingBox extends StatefulWidget { 
   @override 
   _RatingBoxState createState() => _RatingBoxState(); 
}

Cree la interfaz de usuario del widget RatingBox en el método de compilación de _RatingBoxState. Por lo general, la interfaz de usuario se realizará en el método de construcción del propio widget RatingBox. Pero, cuando se necesita el mantenimiento del estado, necesitamos construir la interfaz de usuario en el widget _RatingBoxState. Esto asegura la reproducción de la interfaz de usuario cada vez que se cambia el estado del widget.

Widget build(BuildContext context) { 
   double _size = 20; 
   print(_rating); 

   return Row(
      mainAxisAlignment: MainAxisAlignment.end,
      crossAxisAlignment: CrossAxisAlignment.end,
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
         Container(
            padding: EdgeInsets.all(0), 
            child: IconButton( 
               icon: (_rating >= 1 ? Icon(Icons.star, size: _size,) : 
               Icon(Icons.star_border, size: _size,)), 
               color: Colors.red[500], 
               iconSize: _size, 
            ), 
         ), Container(
            padding: EdgeInsets.all(0),
            child: IconButton(
               icon: (_rating >= 2 ? Icon(Icons.star, size: _size,) :
               Icon(Icons.star_border, size: _size,)),
               color: Colors.red[500],
               iconSize: _size,
            ),
         ), Container(
            padding: EdgeInsets.all(0),
            child: IconButton(
               icon: (_rating >= 3 ? Icon(Icons.star, size: _size,) :
               Icon(Icons.star_border, size: _size,)),
               color: Colors.red[500],
               iconSize: _size,
            ),
         ),
      ],
   );
}

Aquí, hemos usado tres estrellas, creado usando el widget IconButton y organizado usando el widget Row en una sola fila. La idea es mostrar la calificación a través de la secuencia de estrellas rojas. Por ejemplo, si la calificación es de dos estrellas, las primeras dos estrellas serán rojas y la última será blanca.

  • Escriba métodos en _RatingBoxState para cambiar / establecer el estado del widget.

void _setRatingAsOne() { 
   setState( () { 
      _rating = 1; 
   }); 
}
void _setRatingAsTwo() {
   setState( () {
      _rating = 2;
   });
}
void _setRatingAsThree() { 
   setState( () { 
      _rating = 3; 
   }); 
}
  • Aquí, cada método establece la calificación actual del widget a través de setState.

  • Conecte el gesto del usuario (tocando la estrella) al método de cambio de estado adecuado.

Widget build(BuildContext context) { 
   double _size = 20; 
   print(_rating); 
   
   return Row(
      mainAxisAlignment: MainAxisAlignment.end, 
      crossAxisAlignment: CrossAxisAlignment.end,
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
         Container(
            padding: EdgeInsets.all(0),
            child: IconButton(
               icon: (_rating >= 1 ? Icon(Icons.star, size: _size,) :
               Icon(Icons.star_border, size: _size,)),
               color: Colors.red[500],
               onPressed: _setRatingAsOne,
               iconSize: _size,
            ),
         ),
         Container(
            padding: EdgeInsets.all(0),
            child: IconButton(
               icon: (_rating >= 2 ? Icon(Icons.star, size: _size,) :
               Icon(Icons.star_border, size: _size,)),
               color: Colors.red[500],
               onPressed: _setRatingAsTwo,
               iconSize: _size,
            ),
         ),
         Container(
            padding: EdgeInsets.all(0),
            child: IconButton(
               icon: (_rating >= 3 ? Icon(Icons.star, size: _size,) :
               Icon(Icons.star_border, size: _size,)),
               color: Colors.red[500],
               onPressed: _setRatingAsThree,
               iconSize: _size,
            ),
         ),
      ],
   );
}

Aquí, el evento onPressed llama a la función correspondiente para cambiar el estado y, posteriormente, cambiar la interfaz de usuario. Por ejemplo, si un usuario hace clic en la tercera estrella, se llamará a _setRatingAsThree y cambiará la clasificación a 3. Como se cambia el estado, se volverá a llamar al método de compilación y se compilará y renderizará la interfaz de usuario nuevamente.

  • El código completo del widget, RatingBox es el siguiente:

class RatingBox extends StatefulWidget { 
   @override 
   _RatingBoxState createState() => _RatingBoxState(); 
}
class _RatingBoxState extends State<RatingBox> { 
   int _rating = 0; 
   void _setRatingAsOne() {
      setState( () {
         _rating = 1; 
      }); 
   } 
   void _setRatingAsTwo() {
      setState( () {
         _rating = 2;
      });
   }
   void _setRatingAsThree() {
      setState( () {
         _rating = 3;
      });
   }
   Widget build(BuildContext context) {
      double _size = 20; 
      print(_rating); 
      return Row(
         mainAxisAlignment: MainAxisAlignment.end, 
         crossAxisAlignment: CrossAxisAlignment.end, 
         mainAxisSize: MainAxisSize.max, 
         children: <Widget>[ 
            Container(
               padding: EdgeInsets.all(0),
               child: IconButton(
                  icon: (_rating >= 1 ? Icon(Icons.star, size: _size,) :
                  Icon(Icons.star_border, size: _size,)),
                  color: Colors.red[500],
                  onPressed: _setRatingAsOne,
                  iconSize: _size,
               ),
            ),
            Container(
               padding: EdgeInsets.all(0),
               child: IconButton(
                  icon: (_rating >= 2 ? Icon(Icons.star, size: _size,) :
                  Icon(Icons.star_border, size: _size,)), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsTwo, 
                  iconSize: _size, 
               ), 
            ), 
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton(
                  icon: (_rating >= 3 ? Icon(Icons.star, size: _size,) : 
                  Icon(Icons.star_border, size: _size,)), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsThree, 
                  iconSize: _size, 
               ), 
            ), 
         ], 
      ); 
   } 
}

Creemos una nueva aplicación y usemos nuestro widget RatingBox recién creado para mostrar la calificación del producto.

  • Cree una nueva aplicación Flutter en Android Studio, product_state_app .

Reemplace el código main.dart con el siguiente código:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp());

class MyApp extends StatelessWidget { 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) { 
      return MaterialApp(
         title: 'Flutter Demo',
         theme: ThemeData(
            primarySwatch: Colors.blue,
         ), home: MyHomePage(title: 'Product state demo home page'),
      );
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key);
   
   final String title;
   @override
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(
            title: Text(this.title),
         ),
         body: Center(
            child: Text( 'Hello World', )
         ),
      );
   }
}
  • Here,

  • Hemos creado el widget MyHomePage extendiendo StatelessWidget en lugar del StatefulWidget predeterminado y luego eliminamos el código relevante.

  • Incluya nuestro widget RatingBox recién creado .

  • Cree un widget ProductBox para enumerar el producto junto con la calificación como se especifica a continuación:

class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image})
   : super(key: key); 
   
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Widget build(BuildContext context) { 
      return Container(
         padding: EdgeInsets.all(2), 
         height: 120, 
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly,
               children: <Widget>[
                  Image.asset("assets/appimages/" + image),
                  Expanded(
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column( 
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[
                              Text(this.name, style: TextStyle(
                              fontWeight: FontWeight.bold)), 
                              Text(this.description), 
                              Text("Price: " + this.price.toString()), 
                              RatingBox(), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}
  • Actualice el widget MyHomePage para incluir el widget ProductBox como se especifica a continuación:

class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 

   @override 
   Widget build(BuildContext context) {
      return Scaffold( 
         appBar: AppBar(title: Text("Product Listing")), 
         body: ListView( 
            shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[
               ProductBox( 
                  name: "iPhone", 
                  description: "iPhone is the stylist phone ever", 
                  price: 1000, 
                  image: "iphone.png"
               ), 
               ProductBox( 
                  name: "Pixel", 
                  description: "Pixel is the most feature phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool",
                  price: 2000, 
                  image: "laptop.png"
               ),
               ProductBox( 
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ),
               ProductBox( 
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ),
               ProductBox( 
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ),
            ], 
         )
      ); 
   }
}
  • El código completo de la aplicación es el siguiente:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget { 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) { 
      return MaterialApp( 
         title: 'Flutter Demo', 
         theme: ThemeData( 
            primarySwatch: Colors.blue, 
         ), 
         home: MyHomePage( title: 'Product layout demo home page'), 
      ); 
   }
}
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 

   final String title; 
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text("Product Listing")),
         body: ListView(
            shrinkWrap: true,
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0),
            children: <Widget>[
               ProductBox(
                  name: "iPhone",
                  description: "iPhone is the stylist phone ever",
                  price: 1000,
                  image: "iphone.png"
               ),
               ProductBox(
                  name: "Pixel",
                  description: "Pixel is the most featureful phone ever",
                  price: 800,
                  image: "pixel.png"
               ),
               ProductBox(
                  name: "Laptop",
                  description: "Laptop is most productive development tool",
                  price: 2000,
                  image: "laptop.png"
               ),
               ProductBox(
                  name: "Tablet",
                  description: "Tablet is the most useful device ever for meeting",
                  price: 1500,
                  image: "tablet.png"
               ),
               ProductBox(
                  name: "Pendrive",
                  description: "iPhone is the stylist phone ever",
                  price: 100,
                  image: "pendrive.png"
               ),
               ProductBox(
                  name: "Floppy Drive",
                  description: "iPhone is the stylist phone ever",
                  price: 20,
                  image: "floppy.png"
               ),
               ProductBox(
                  name: "iPhone",
                  description: "iPhone is the stylist phone ever",
                  price: 1000,
                  image: "iphone.png"
               ),
               ProductBox(
                  name: "iPhone",
                  description: "iPhone is the stylist phone ever",
                  price: 1000,
                  image: "iphone.png"
               ),
            ],
         )
      );
   }
}
class RatingBox extends StatefulWidget {
   @override 
   _RatingBoxState createState() =>       
   _RatingBoxState(); 
}
class _RatingBoxState extends State<RatingBox> {
   int _rating = 0; 
   void _setRatingAsOne() {
      setState( () {
         _rating = 1;
      }); 
   } 
   void _setRatingAsTwo() { 
      setState( () { 
         _rating = 2; 
      }); 
   } 
   void _setRatingAsThree() { 
      setState( () { 
         _rating = 3; 
      }); 
   } 
   Widget build(BuildContext context) { 
      double _size = 20; 
      print(_rating); 
      return Row( 
         mainAxisAlignment: MainAxisAlignment.end, 
         crossAxisAlignment: CrossAxisAlignment.end, 
         mainAxisSize: MainAxisSize.max, 
         children: <Widget>[
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton( 
                  icon: (_rating >= 1 ? Icon(Icons.star, size: _size,) : 
                  Icon(Icons.star_border, size: _size,)), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsOne, 
                  iconSize: _size, 
               ), 
            ), 
            Container( 
               padding: EdgeInsets.all(0), 
               child: IconButton( 
                  icon: (_rating >= 2 ? Icon(Icons.star, size: _size,) : 
                  Icon(Icons.star_border, size: _size,)), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsTwo, 
                  iconSize: _size, 
               ), 
            ), 
            Container(
               padding: EdgeInsets.all(0),
               child: IconButton(
                  icon: (_rating >= 3 ? Icon(Icons.star, size: _size,) : 
                  Icon(Icons.star_border, size: _size,)),
                  Colors.red[500], 
                  onPressed: _setRatingAsThree, 
                  iconSize: _size, 
               ), 
            ), 
         ], 
      ); 
   }
}
class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image}) : 
   super(key: key); 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2),
         height: 140,
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly,
               children: <Widget>[
                  Image.asset("assets/appimages/" + image),
                  Expanded(
                     child: Container(
                        padding: EdgeInsets.all(5),
                        child: Column(
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[
                              Text(this.name, style: TextStyle(fontWeight: FontWeight.bold)), 
                              Text(this.description), 
                              Text("Price: " + this.price.toString()), 
                              RatingBox(), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      ); 
   }
}
    Finalmente, ejecute la aplicación y vea la administración de estado - Resultados de la página de lista de productos como se muestra aquí -

Al hacer clic en la estrella de calificación, se actualizará la calificación del producto. Por ejemplo, al configurar una calificación de 2 estrellas para iPhone, se mostrará la calificación de la siguiente manera: