Flutter - 短暂状态管理

由于 Flutter 应用程序由 widget 组成,因此状态管理也由 widget 完成。状态管理的入口点是 Statefulwidget。Widget 可以从 Statefulwidget 继承,以维护其状态及其子状态。Statefulwidget 为 widget 提供了一个选项,当 widget 首次通过 createState 方法创建时,可以创建状态 State(其中 T 是继承的 widget),然后可以使用 setState 方法在需要时更改状态。状态更改将通过手势完成。例如,可以通过点击评级 widget 中的星星来更改产品的评级。

让我们创建一个具有状态维护的 widget,RatingBox。该 widget 的目的是显示特定产品的当前评级。创建具有状态维护的 RatingBox 小部件的分步过程如下 −

  • 通过继承 StatefulWidget 创建小部件 RatingBox。

class RatingBox extends StatefulWidget { }
  • 通过继承 State<T> 为 RatingBox 创建状态 _RatingBoxState

class _RatingBoxState extends State<RatingBox> { }
  • 重写 StatefulWidget 的 createState 方法以创建状态 _RatingBoxState。

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

在 _RatingBoxState 的 build 方法中创建 RatingBox 小部件的用户界面。通常,用户界面将在 RatingBox 小部件本身的 build 方法中完成。但是,当需要状态维护时,我们需要在 _RatingBoxState 小部件中构建用户界面。这可确保每当小部件的状态发生变化时重新呈现用户界面。

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,
            ),
         ),
      ],
   );
}

在这里,我们使用了三颗星,使用 IconButton 小部件创建,并使用 Row 小部件将其排列在一行中。这个想法是通过红色星星的序列显示评级。例如,如果评级是两颗星,那么前两颗星将是红色的,最后一颗星是白色的。

  • 在 _RatingBoxState 中编写方法来更改/设置小部件的状态。

void _setRatingAsOne() { 
   setState( () { 
      _rating = 1; 
   }); 
}
void _setRatingAsTwo() {
   setState( () {
      _rating = 2;
   });
}
void _setRatingAsThree() { 
   setState( () { 
      _rating = 3; 
   }); 
}
  • 此处,每个方法都通过 setState 设置小部件的当前评级。

  • 将用户手势(点击星星)连接到适当的状态更改方法。

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,
            ),
         ),
      ],
   );
}

此处,onPressed 事件调用相关函数来更改状态,随后更改用户界面。例如,如果用户点击第三颗星,则将调用 _setRatingAsThree 并将 _rating 更改为 3。由于状态已更改,因此将再次调用 build 方法并再次构建和渲染用户界面。

  • 小部件 RatingBox 的完整代码如下 −

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, 
               ), 
            ), 
         ], 
      ); 
   } 
}

让我们创建一个新的应用程序,并使用我们新创建的 RatingBox 小部件来显示产品的评级。

  • 在 Android Studio 中创建一个新的 Flutter 应用程序 product_state_app

用以下代码替换 main.dart 代码−

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

class MyApp extends StatelessWidget { 
   // 此小部件是您的应用程序的根。
   @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', )
         ),
      );
   }
}
  • 这里,

  • 我们通过扩展 StatelessWidget 而不是默认的 StatefulWidget 创建了 MyHomePage 小部件,然后删除了相关代码。

  • 包括我们新创建的 RatingBox 小部件。

  • 创建一个 ProductBox 小部件以列出产品以及评级,如下所示 −

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(), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}
  • 更新 MyHomePage 小部件以包含下面指定的 ProductBox 小部件 −

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"
               ),
            ], 
         )
      ); 
   }
}
  • 该应用程序的完整代码如下 −

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

class MyApp extends StatelessWidget { 
   // 此小部件是您的应用程序的根。
   @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(), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      ); 
   }
}
    最后运行应用,查看状态管理-商品列表页面结果如下图所示 −
状态管理

点击评分星星会更新商品的评分,例如给 iPhone 设置 2 星评分,评分会显示如下图所示 −

评分星星

flutter_state_management.html