Flutter 状态管理
管理应用程序中的状态是应用程序生命周期中最重要和必要的过程之一。
让我们考虑一个简单的购物车应用程序。
- 用户将使用他们的凭据登录到应用程序。
- 用户登录后,应用程序应在所有屏幕中保留登录的用户详细信息。
- 同样,当用户选择产品并保存到购物车时,购物车信息应该在页面之间保留,直到用户签出购物车。
- 任何实例中的用户及其购物车信息称为该实例中应用程序的状态。
根据特定状态在应用程序中持续的持续时间,可以将状态管理分为两类。
- Ephemeral - 持续几秒钟,例如动画的当前状态或单个页面,例如产品的当前评级。 Flutter 通过 StatefulWidget 支持它。
- app state - 最后整个应用程序,如登录的用户详细信息、购物车信息等,Flutter 通过 scoped_model 支持它。
导航与路由
在任何应用程序中,从一个页面/屏幕导航到另一个页面/屏幕定义了应用程序的工作流程。 处理应用程序导航的方式称为路由。 Flutter 提供了一个基本的路由类——MaterialPageRoute 和两个方法——Navigator.push 和 Navigator.pop,来定义应用程序的工作流程。
MaterialPageRoute
MaterialPageRoute
是一个小部件,用于通过将整个屏幕替换为特定于平台的动画来呈现其 UI。
MaterialPageRoute(builder: (context) => Widget())
在这里,builder 将接受一个函数来通过提供应用程序的当前上下文来构建其内容。
Navigation.push
Navigation.push
用于使用 MaterialPageRoute 小部件导航到新屏幕。
Navigator.push( context, MaterialPageRoute(builder: (context) => Widget()), );
Navigation.pop
Navigation.pop 用于导航到上一个屏幕。
Navigator.pop(context);
让我们创建一个新应用程序来更好地理解导航概念。
在 Android Studio 中新建一个 Flutter 应用,product_nav_app
- 将 assets 文件夹从 product_nav_app 复制到 product_state_app 并在 pubspec.yaml 文件中添加 assets。
flutter:
assets:
- assets/appimages/floppy.png
- assets/appimages/iphone.png
- assets/appimages/laptop.png
- assets/appimages/pendrive.png
- assets/appimages/pixel.png
- assets/appimages/tablet.png
用我们的启动代码替换默认启动代码(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',)
),
);
}
}
让我们创建一个 Product 类来组织产品信息。
class Product {
final String name;
final String description;
final int price;
final String image;
Product(this.name, this.description, this.price, this.image);
}
让我们在 Product 类中编写一个方法 getProducts
来生成我们的虚拟产品记录。
static List<Product> getProducts() {
List<Product> items = <Product>[];
items.add(
Product(
"Pixel",
"Pixel is the most feature-full phone ever", 800,
"pixel.png"
)
);
items.add(
Product(
"Laptop",
"Laptop is most productive development tool",
2000, "
laptop.png"
)
);
items.add(
Product(
"Tablet",
"Tablet is the most useful device ever for meeting",
1500,
"tablet.png"
)
);
items.add(
Product(
"Pendrive",
"Pendrive is useful storage medium",
100,
"pendrive.png"
)
);
items.add(
Product(
"Floppy Drive",
"Floppy drive is useful rescue storage medium",
20,
"floppy.png"
)
);
return items;
}
import product.dart in main.dart
import 'Product.dart';
让我们包括我们的新小部件 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,
),
),
],
);
}
}
让我们修改我们的 ProductBox 小部件以使用我们的新 Product 类。
class ProductBox extends StatelessWidget {
ProductBox({Key key, this.item}) : super(key: key);
final Product item;
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/" + this.item.image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(this.item.name,
style: TextStyle(fontWeight: FontWeight.bold)),
Text(this.item.description),
Text("Price: " + this.item.price.toString()),
RatingBox(),
],
)
)
)
]
),
)
);
}
}
让我们重写 MyHomePage 小部件以使用 Product 模型并使用 ListView 列出所有产品。
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
final items = Product.getProducts();
@override
Widget build(BuildContext context) {
return Scaffold( appBar: AppBar(title: Text("Product Navigation")),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return GestureDetector(
child: ProductBox(item: items[index]),
onTap: () {
Navigator.push(
context, MaterialPageRoute(
builder: (context) => ProductPage(item: items[index]),
),
);
},
);
},
));
}
}
在这里,我们使用 MaterialPageRoute 导航到产品详细信息页面。
现在,让我们添加 ProductPage 以显示产品详细信息。
class ProductPage extends StatelessWidget {
ProductPage({Key key, this.item}) : super(key: key);
final Product item;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.item.name),
),
body: Center(
child: Container(
padding: EdgeInsets.all(0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image.asset("assets/appimages/" + this.item.image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
this.item.name, style: TextStyle(
fontWeight: FontWeight.bold
)
),
Text(this.item.description),
Text("Price: " + this.item.price.toString()),
RatingBox(),
],
)
)
)
]
),
),
),
);
}
}
应用程序的完整代码如下
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class Product {
final String name;
final String description;
final int price;
final String image;
Product(this.name, this.description, this.price, this.image);
static List<Product> getProducts() {
List<Product> items = <Product>[];
items.add(
Product(
"Pixel",
"Pixel is the most featureful phone ever",
800,
"pixel.png"
)
);
items.add(
Product(
"Laptop",
"Laptop is most productive development tool",
2000,
"laptop.png"
)
);
items.add(
Product(
"Tablet",
"Tablet is the most useful device ever for meeting",
1500,
"tablet.png"
)
);
items.add(
Product(
"Pendrive",
"iPhone is the stylist phone ever",
100,
"pendrive.png"
)
);
items.add(
Product(
"Floppy Drive",
"iPhone is the stylist phone ever",
20,
"floppy.png"
)
);
items.add(
Product(
"iPhone",
"iPhone is the stylist phone ever",
1000,
"iphone.png"
)
);
return items;
}
}
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 Navigation demo home page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
final items = Product.getProducts();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Product Navigation")),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return GestureDetector(
child: ProductBox(item: items[index]),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductPage(item: items[index]),
),
);
},
);
},
)
);
}
}
class ProductPage extends StatelessWidget {
ProductPage({Key key, this.item}) : super(key: key);
final Product item;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.item.name),
),
body: Center(
child: Container(
padding: EdgeInsets.all(0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image.asset("assets/appimages/" + this.item.image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(this.item.name, style: TextStyle(fontWeight: FontWeight.bold)),
Text(this.item.description),
Text("Price: " + this.item.price.toString()),
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,
),
),
],
);
}
}
class ProductBox extends StatelessWidget {
ProductBox({Key key, this.item}) : super(key: key);
final Product item;
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/" + this.item.image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(this.item.name, style: TextStyle(fontWeight: FontWeight.bold)), Text(this.item.description),
Text("Price: " + this.item.price.toString()),
RatingBox(),
],
)
)
)
]
),
)
);
}
}
运行应用程序并单击任一产品项。 它将显示相关的详细信息页面。 我们可以通过点击后退按钮移动到主页。 应用的产品列表页面和产品详情页面如下图所示