在靠近用户的地方部署容器
本工程教育(EngEd)计划由科支持。
在全球范围内即时部署容器。Section是经济实惠、简单而强大的。
免费入门。
了解用于状态管理的Flutter GetX生态系统
9月21日, 2021
- 主题。
- 语言
状态管理使你能够将数据从一个用户界面传递到另一个界面。当你的应用程序的状态改变时,系统会重建用户界面。
Flutter传统上使用Stateful Widget来管理状态。然而,这在一个复杂的应用程序中是相当难以实现的。
有状态的小部件通过子小部件的构造器传递数据。虽然这个功能很有用,但它会导致数据被传递给不需要它的部件。
另一个缺点是,业务逻辑与用户界面紧密相连。这可能会导致混乱。
目标
本文教你如何使用GetX状态管理包来解决Flutter中的状态管理问题。
在本教程中,我们将建立一个购物移动应用,允许用户查看产品、喜欢的物品、将产品添加到购物车,以及下订单。
下单成功后,用户仍然可以访问其他产品。通过这个应用程序,我们将展示GetX软件包的强大功能。
主要收获
- 如何设置Flutter项目和配置依赖性。
- 如何使用GetX作为一个状态管理工具。
- 使用Obx来最大化反应式编程的力量。
- 探索Getx的导航功能。
- 如何使用GetBuilder子生态系统来管理状态。
- GetX生态系统的可重复使用的组件。
先决条件
要跟上进度,你应该具备以下条件
- Dart和Flutter的一些基本知识。
- 在您的计算机上安装Flutter。
- Android studio或VS Code。
在Android Studio中创建一个Flutter应用程序
在这个项目中,我们将使用Android Studio。
为了开始,启动Android studio并创建一个新的Flutter项目。确保你将类型设置为Flutter application 。
确保你选择了你的Flutter SDK所在的路径,然后点击下一步。

接下来,填写以下项目细节,以完整地设置该项目。
- 由于我们正在建立一个网上商店,我们可以将项目命名为
shopping_getx。 - 选择一个您希望保存项目的目录。
- 添加一个项目描述。
- 选择一个合适的软件包名称。
- 将默认的Android和iOS语言分别保留为Kotlin和Swift。

这将在main.dart 文件中生成默认的Flutter计数器项目。
整合GetX生态系统
什么是GetX?
GetX是一个简单而强大的Flutter包。GetX包的主要支柱是高性能状态管理、智能依赖注入和路由管理。
GetX通过简单愉快的语法帮助开发者实现高水平的生产力,而不牺牲应用程序的性能。
它支持用户界面、表现逻辑、业务逻辑、依赖注入和导航的解耦。这有助于在默认情况下产生干净的代码。

要将GetX集成到应用程序中。请访问GetX文档,复制get: ^4.3.8 ,并将其添加到项目pubspec.yaml 文件中,在dependencies section ,然后运行pub get 命令。
这将把GetX生态系统安装到你的项目中。
将intl: ^0.17.0 添加到dependencies section ,以安装intl package ,然后运行pub get ,以安装该依赖关系。

用以下内容替换main.dart 文件中生成的代码。
import 'package:flutter/material.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';
import 'package:shopping_app/screens/product_overview_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.purple,
accentColor: Colors.deepOrange,
fontFamily: "Lato",
),
home: ProductOverviewPage(),
);
}
}
在上面的代码中。
我们已经创建了main 方法,它是Dart VM 执行程序的入口点。main 方法运行MyApp 类。
MyApp class 扩展了无状态部件,并重写了构建方法。它还返回一个 。GetMaterialApp
GetMaterialApp:构建方法是返回GetMaterialApp而不是通常的MaterialApp。这是因为我们可以使用GetX进行导航。GetMaterialApp 是来自Getx 包的一个类。
在homepage ,我们正在渲染ProductOverviewPage 。
项目结构
我们将对项目进行如下结构。
在lib folder ,我们将创建以下文件夹。
screens(放置我们的用户界面)。controller(业务逻辑)。models(要存储的数据的对象表示)。
让我们从models 开始。
模型
在models 文件夹内,创建一个名为product.dart 的dart文件,如下所示。
产品模型
class Product{
final int id;
final String productTitle;
final String imageUrl;
final String description;
final double price;
bool isFavourite;
Product(
{required this.id,
required this.productTitle,
required this.imageUrl,
required this.description,
required this.price,
this.isFavourite = false});
}
这只是一个包含产品字段的Dart类。
我们添加了一个构造函数来初始化所有字段。isFavourite 变量被设置为false 。
CartItem模型
class CartItem {
final String id;
final String productTitle;
final int productQuantity;
final double productPrice;
CartItem(
{required this.id,
required this.productTitle,
required this.productQuantity,
required this.productPrice});
}
我们创建了一个Dart类来存储CartItem 字段。
然后构造函数初始化了所需的字段。
订单类
import 'package:shopping_app/models/cart_item.dart';
class Order {
final String orderId;
final double amount;
final List<CartItem> products;
final DateTime dateTime;
Order(
{required this.orderId,
required this.amount,
required this.products,
required this.dateTime});
}
这只是一个Dart类,包含了将要下的订单的字段。
控制器
我们所有的业务逻辑都将在控制器上。这将使我们很容易跟踪不同的问题或错误。
在controller folder ,创建一个名为product_controller 的文件,添加以下代码。
产品控制器
import 'package:get/get.dart';
import 'package:shopping_app/models/product.dart';
class ProductController extends GetxController {
List<Product> _items = [
Product(
id: 1,
productTitle: 'Sport Shoe',
description: 'Made for you Check it out!',
price: 7000.00,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Cavalier-Black-1.jpg?v=1589391819',
),
Product(
id: 2,
productTitle: 'Legend',
description:
'Built to last forever, StormKing™ lug rubber outsoles and a flexible elastic goring, this can only be for the Legends and i bet you, you have not seen it anywhere.',
price: 63000.00,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Men-Legend-BlackMatte-3.4_672x672.jpg?v=1600886623'),
Product(
id: 3,
productTitle: 'The Chelsea',
description: 'Functional and Fashionable.',
price: 49.00,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Cavalier-Black-1.jpg?v=1589391819'),
Product(
id: 4,
title: 'Men\'s Sneakers',
productTitle: 'Clean & Comfortable Sneakers made with classic Leathers.',
price: 49.99,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Men-PremierLowTop-Black-3.4.jpg?v=1600270679'),
Product(
id: 5,
productTitle: 'The Chelsea',
description:
'Comfortable as you\'d expect.This can only be found at Resilient collection.',
price: 49.99,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Captain-Natural-3.jpg?v=1584114360'),
Product(
id: 6,
productTitle: 'Men\'s Sneakers',
description: 'Clean & Comfortable Sneakers made with classic Leathers.',
price: 49.99,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Men-PremierLowTop-Black-3.4.jpg?v=1600270679'),
Product(
id: 7,
productTitle: 'The Chelsea',
description:
'Made for the men who understand what classic means, every bit was carefully selected so you can go the extra mile with confidence and alacrity.',
price: 49.99,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Men-Cavalier-Toffee-210402-2.jpg?v=1618424894'),
Product(
id: 8,
productTitle: 'Men\'s Sneakers',
description: 'Clean & Comfortable Sneakers made with classic Leathers.',
price: 49.99,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Men-Cavalier-Toffee-210402-3.jpg?v=1618424894'),
Product(
id: 9,
productTitle: 'The Chelsea',
description: 'Functional and Fashionable.',
price: 49.99,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Cavalier-Black-1.jpg?v=1589391819'),
Product(
id: 10,
productTitle: 'Men\'s Sneakers',
description: 'Clean & Comfortable Sneakers made with classic Leathers.',
price: 49.99,
imageUrl:
'https://cdn.shopify.com/s/files/1/0419/1525/products/1024x1024-Men-PremierLowTop-Black-3.4.jpg?v=1600270679'),
];
List<Product> get items {
return [..._items];
}
List<Product> get favouriteItems {
return _items.where((productItem) => productItem.isFavourite).toList();
}
Product findProductById(int id) {
return _items.firstWhere((element) => element.id == id);
}
void toggleFavouriteStatus(int id) {
items[id].isFavourite = !items[id].isFavourite;
update();
}
}
我们刚刚创建的ProductController 类是对GetxController 类的扩展,它是对DisposableInterface 的一个抽象类。
通过扩展DisposableInterface ,GetX帮助我们减少内存的消耗,在使用它的部件从导航栈中删除后,立即从内存中删除我们的控制器。
我们在控制器中需要的是我们想要绑定到用户界面的元素。在这种情况下,一个列表Products ,因此我们创建了一个字段_items ,其中包含所有产品的列表。
_items 中的下划线使其成为私有。我们是硬编码的,但我们同样可以从后端服务器检索产品。
接下来,我们创建了两个返回所有产品的getters,包括那些被标记为最喜欢的产品。
findProductById 方法接收一个ID 作为参数,并返回具有该特定ID 的产品。
toggleFavouriteStatus 方法接收一个ID ,并将带有该ID 的产品标记为收藏夹。
我们从Getx 中调用更新方法,以便在点击时改变用户界面。update方法监听toggleFavouriteStatus 方法中的变化,并更新相应的用户界面。
如果你熟悉Provider包,update 方法的功能与notifyListeners 。
购物车控制器
CartController 将包含关于如何从购物车中添加和删除物品的业务逻辑。
它还将包含返回购物车中所有物品的方法,以及购物车中所有物品的总金额。
在controllers 文件夹中创建一个名为cart_controller.dart 的dart文件,然后添加以下代码。
import 'dart:core';
import 'package:get/get.dart';
import 'package:shopping_app/models/cart_item.dart';
class CartController extends GetxController {
Map<int, CartItem> _items = {};
Map<int, CartItem> get items {
return {..._items};
}
int get itemCount {
// return _items?.length?? 0;
return _items.length;
}
double get totalAmount {
var total = 0.0;
_items.forEach((key, cartItem) {
total += cartItem.price * cartItem.quantity;
});
return total;
}
void addItem(int productId, double price, String title, int quantity) {
if (_items.containsKey(productId)) {
_items.update(
productId,
(existingCartItem) => CartItem(
id: existingCartItem.id,
title: existingCartItem.title,
quantity: existingCartItem.quantity + 1,
price: existingCartItem.price));
} else {
_items.putIfAbsent(
productId,
() => CartItem(
id: DateTime.now().toString(),
title: title,
price: price,
quantity: 1,
),
);
}
update();
}
void removeitem(int productId) {
_items.remove(productId);
update();
}
void clear() {
_items = {};
update();
}
}
我们已经创建了一个CartContoller 类,它扩展了GetXController 。记得从包中导入GetXController 。
我们还创建了一个map ,用来存放CartItem 对象。
我们包括两个getters,分别返回购物车中的所有物品和物品的数量。
接下来,我们创建了一个totalAmount 方法,计算并返回购物车中所有产品的总金额。
addItem 方法将产品添加到购物车中。首先,我们检查产品是否已经存在于购物车中,如果是,我们更新数量,否则,我们将其添加到购物车中。
removeitem 方法接收一个productId ,并从购物车中删除一个带有该ID 的产品。
一旦成功下了订单,clear 方法将清除购物车。
注意,我们在所有创建的方法中都调用了GetX ,以监听变化,并在需要这些数据的地方更新相应的用户界面。
OrderController
OrderController 将包含下订单的方法。
在controller folder 创建一个名为order_controller.dart 的dart文件,并添加以下代码。
import 'package:get/get.dart';
import 'package:shopping_app/models/cart_item.dart';
import 'package:shopping_app/models/order.dart';
class OrderController extends GetxController {
List<Order> _orders = [];
List<Order> get orders {
return [..._orders];
}
void addOrder(List<CartItem> cartProducts, double total) {
_orders.insert(
0,
Order(
id: DateTime.now().toString(),
products: cartProducts,
amount: total,
dateTime: DateTime.now()));
update();
}
}
我们创建了一个保存所有订单的列表。
接下来,我们创建了一个名为orders 的getter,以返回所有下的订单。
addOrder 方法接收了一个列表CartItem ,这是已经添加到购物车的产品,以及一个类型为double 的total ,这是添加到购物车的所有产品的总和,并下了一个订单。
我们再次调用update 方法来监听变化并更新用户界面。
用户界面
在screens 文件夹中,创建一个dart文件product_overview_screen.dart ,并添加以下代码。
ProductOverviewPage
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shopping_app/controllers/cart_controller.dart';
import 'package:shopping_app/screens/cart_screen.dart';
import 'package:shopping_app/widgets/app_drawer.dart';
import 'package:shopping_app/widgets/badge.dart';
import 'package:shopping_app/widgets/productgrid.dart';
class ProductOverviewPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final cartController = Get.put(CartController());
return Scaffold(
appBar: AppBar(
title: Text("My Shop"),
actions: <Widget>[
GetBuilder<CartController>(
init: CartController(),
builder: (contex) {
return Badge(
child: IconButton(
icon: Icon(
Icons.shopping_cart,
),
onPressed: () {
Get.to(() => CartScreen());
}),
value: cartController.itemCount.toString(),
color: Theme.of(context).accentColor,
);
})
],
),
drawer: AppDrawer(),
body: ProductsGrid(),
);
}
}
依赖性注入允许将一个类的实例注入另一个类中。
为了定义什么是依赖,如果C类使用D类的功能,那么D就是C的依赖。
Getx 允许你只用一行代码就可以进行依赖性注入。
final cartController = Get.put(CartController());
我们已经将cartController 注入我们的用户界面,这样我们就可以访问控制器上的数据。
GetBuilder 在任何一个widget上都包裹着一个 "依赖",使其与控制器的方法和变量进行交互。
无论哪个widget被包装成GetBuilder ,Getx都会对其应用setState 。有了这个,我们就能在CartController 类中调用itemCount 函数。
- 我们使用
Getx navigation manager,即使在按下shopping_cart icon,也能导航到CartScreen页面。
在这个类的主体中,我们调用了ProductsGrid 类来返回一个显示所有产品的网格。
小工具
我们已经分解了我们的用户界面,以保持其简单和可重复使用。
创建一个名为widget 的文件夹。在widget folder ,创建一个名为productgrid.dart 的dart文件。
产品网格类
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shopping_app/controllers/cart_controller.dart';
import 'package:shopping_app/controllers/product_controller.dart';
import 'package:shopping_app/screens/product_details_screen.dart';
class ProductsGrid extends StatelessWidget {
final controller = Get.put(ProductController());
final cartController = Get.put(CartController());
@override
Widget build(BuildContext context) {
return GridView.builder(
padding: const EdgeInsets.all(10),
itemCount: controller.items.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3 / 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10),
itemBuilder: (context, index) {
return GetBuilder(
init: ProductController(),
builder: (value) => ClipRRect(
borderRadius: BorderRadius.circular(10),
child: GridTile(
child: GestureDetector(
onTap: () {
Get.to(
ProductDetailsScreen(
controller.items[index].title,
controller.items[index].price,
controller.items[index].imageUrl,
controller.items[index].description,
),
);
},
child: Image.network(
controller.items[index].imageUrl,
fit: BoxFit.cover,
),
),
footer: GridTileBar(
backgroundColor: Colors.black87,
leading: IconButton(
icon: Icon(
controller.items[index].isFavourite == true
? Icons.favorite
: Icons.favorite_border,
color: Theme.of(context).accentColor,
),
onPressed: () {
controller.toggleFavouriteStatus(index);
},
),
title: Text(
controller.items[index].title,
textAlign: TextAlign.center,
),
trailing: GetBuilder<CartController>(
init: CartController(),
builder: (cont) {
return IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: () {
cartController.addItem(
controller.items[index].id,
controller.items[index].price,
controller.items[index].title,
1);
},
color: Theme.of(context).accentColor,
);
}),
),
),
),
);
},
);
}
}
我们将ProductController 和CartController 注入到ProductGrid 类中,以便能够访问其中定义的功能。
我们用一个GetBuilder 来包装ClipRRect widget,以便在状态改变时更新它。通过注入的ProductController ,我们显示了产品,显示它们的title 和image 。
我们还使用了导航器管理器,当某个产品被点击时,通过简单地调用Get.to() ,并传入产品的title 、price 、image 、description ,来路由到ProductDetailsScreen 。
当最喜欢的图标被点击时,我们调用产品控制器来访问toggleFavouriteStatus 函数,并适当地改变图标的颜色。
shopping_cart 图标已经被包裹在Getbuilder 中,所以每当它被点击时,我们通过调用CartController 中的addItem 函数将产品添加到购物车中。
ProductDetails屏幕
在screens 文件夹中创建一个名为product_details_screen.dart 的dart文件,并添加以下代码。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shopping_app/controllers/cart_controller.dart';
import 'package:shopping_app/controllers/product_controller.dart';
class ProductDetailsScreen extends StatelessWidget {
final String title;
final double price;
final String image;
final String description;
ProductDetailsScreen(this.title, this.price, this.image, this.description);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: SingleChildScrollView(
child: Container(
color: Color(0xffF6F6F6),
child: Column(
children: [
Container(
child: ClipRRect(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(25),
bottomRight: Radius.circular(25)),
child: Image.network(
this.image,
fit: BoxFit.cover,
),
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Chip(
label: Text(
"Price: " + "₦" + this.price.toString(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold
),
),
backgroundColor: Theme.of(context).primaryColor,
),
SizedBox(height: 15),
Text(
"" + this.description,
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xff403B58),
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
)
],
),
),
),
);
}
}
每当人们点击一个产品时,我们使用构造函数从productGrid 类中传入详细信息。
CartScreen
在screens folder ,创建一个名为cart_screen.dart 的dart文件,并编写以下代码。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shopping_app/controllers/cart_controller.dart';
import 'package:shopping_app/controllers/order_controller.dart';
import 'package:shopping_app/widgets/cart_items.dart';
class CartScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
var cartController = Get.put(CartController());
var orderController = Get.put(OrderController());
return Scaffold(
appBar: AppBar(
title: Text("Your cart"),
),
body: GetBuilder<CartController>(
init: CartController(),
builder: (cont) => Column(
children: <Widget>[
Card(
margin: EdgeInsets.all(15),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Total",
style: TextStyle(
fontSize: 20,
),
),
Spacer(),
Chip(
label: Text(
'₦${cartController.totalAmount.toStringAsFixed(2)}',
style: TextStyle(
color: Colors.white,
),
),
backgroundColor: Theme.of(context).primaryColor,
),
GetBuilder<OrderController>(
init: OrderController(),
builder: (context) {
return TextButton(
onPressed: () {
orderController.addOrder(
cartController.items.values.toList(),
cartController.totalAmount);
cartController.clear();
Get.snackbar(
"Orders",
"Orders placed successfully",
backgroundColor: Colors.green,
snackPosition: SnackPosition.BOTTOM
);
},
child: Text('ORDER NOW'));
})
],
),
),
),
SizedBox(
height: 10,
),
Expanded(
child: ListView.builder(
itemCount: cartController.items.length,
itemBuilder: (context, index) => CartItem(
cartController.items.values.toList()[index].id,
cartController.items.values.toList()[index].price,
cartController.items.values.toList()[index].quantity,
cartController.items.values.toList()[index].title,
cartController.items.keys.toList()[index],
)),
),
],
),
),
);
}
}
我们已经把OrderController 和CartController 注入到CartScreen类中,以访问它们的功能。
我们用GetBuilder 来更新那些需要在状态变化时重建的小部件。总金额通过CartController 进行相应的更新。
Listview.builder widget被用来渲染所有添加到购物车的产品列表,也就是Cartitem 类。
如前所述,我们正在显示购物车中产品的title,amount,quantity, 和price 。
我们使用注入的OrderController 的实例来调用addOrder 函数,这样,只要点击TextButton 小部件,就会有一个订单被下。
如果订单成功下达,我们使用GetX的snackbar ,向用户显示订单已成功下达的信息,如下图所示。
Get.snackbar(
"Orders",
"Orders placed successfully",
backgroundColor: Colors.green,
snackPosition: SnackPosition.BOTTOM
);
一旦下单成功,我们就从cartController ,调用clear 方法来清除购物车。
购物车项目
在widget folder 中创建一个cart_items.dart 文件,代码如下。
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shopping_app/controllers/cart_controller.dart';
class CartItem extends StatelessWidget {
final String id;
final int productId;
final double price;
final int quantity;
final String title;
CartItem(this.id, this.price, this.quantity, this.title, this.productId);
@override
Widget build(BuildContext context) {
var cartController = Get.put(CartController());
return Dismissible(
key: ValueKey(id),
background: Container(
color: Theme.of(context).errorColor,
child: Icon(Icons.delete, color: Colors.white,size: 40,
),
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 20),
margin: EdgeInsets.symmetric(horizontal: 15, vertical: 4),
),
direction: DismissDirection.endToStart,
onDismissed: (direction){
cartController.removeitem(productId);
},
child: Card(
margin: EdgeInsets.symmetric(horizontal: 15, vertical: 4),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
leading: Chip(
label: Padding(
padding: const EdgeInsets.all(8.0),
child: Text('₦$price'),
),
backgroundColor: Theme.of(context).primaryColor,
),
title: Text(title),
subtitle: Text('Total: ₦${(price * quantity)}'),
trailing: Text('$quantity X'),
),
),
),
);
}
}
我们已经创建了CartItem 类,它扩展了无状态部件。字段包括id,productId,price,quantity, 和title 。构造函数被用来初始化这些字段。
我们使用注入的CartController 来访问removeitem 方法,从而在Dismissible widget被刷的时候从购物车中删除一个产品。
订单屏幕
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shopping_app/controllers/order_controller.dart';
import 'package:shopping_app/widgets/app_drawer.dart';
import 'package:shopping_app/widgets/order_item.dart';
class OrderScreen extends StatelessWidget {
var orderController = Get.put(OrderController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Yours Orders"),
),
drawer: AppDrawer(),
body: ListView.builder(
itemCount: orderController.orders.length,
itemBuilder: (context, index) =>
OrderItem(orderController.orders[index])),
);
}
}
在上面的文件中。
OrderScreen 类渲染了OrderItem widget。
我们注入了OrderController 来访问orders ,其中包含了所有订单的列表。
我们渲染了AppDrawer ,以显示Orders 和Shops ,这取决于所选的订单。
订单项
import 'package:flutter/material.dart';
import 'package:shopping_app/models/order.dart';
import 'package:intl/intl.dart';
import 'dart:math';
class OrderItem extends StatefulWidget {
final Order order;
OrderItem(this.order);
@override
_OrderItemState createState() => _OrderItemState();
}
class _OrderItemState extends State<OrderItem> {
var _isExpanded = false;
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
ListTile(
title: Text('${widget.order.amount.toStringAsFixed(2)}'),
subtitle: Text(
DateFormat('dd/MM/yyyy hh:mm').format(widget.order.dateTime)),
trailing: IconButton(
icon: Icon(_isExpanded ? Icons.expand_less : Icons.expand_more),
onPressed: () {
setState(() {
_isExpanded = !_isExpanded;
});
},
),
),
if (_isExpanded)
Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 4),
height: min(widget.order.products.length * 20 + 10, 180),
child: ListView(
children: widget.order.products
.map(
(product) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
product.title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
Text(
'${product.quantity}X ₦${product.price}',
style: TextStyle(
fontSize: 18,
color: Colors.grey,
),
),
],
),
)
.toList(),
),
)
],
),
);
}
}
上面的类渲染了所下订单的列表。
AppDrawer
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shopping_app/screens/order_screen.dart';
import 'package:shopping_app/screens/product_overview_screen.dart';
class AppDrawer extends StatelessWidget {
const AppDrawer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: <Widget>[
AppBar(
title: Text("Hello Friend"),
automaticallyImplyLeading: false,
),
Divider(),
ListTile(
leading: Icon(Icons.shop),
title: Text("Shop"),
onTap: () {
Get.to(() => ProductOverviewPage());
},
),
Divider(),
ListTile(
leading: Icon(Icons.payment),
title: Text("Orders"),
onTap: () {
Get.to(() => OrderScreen());
},
),
],
),
);
}
}
这个类返回一个带有Drawer widget的Column,所以用户可以选择导航到显示所有产品的ProductOverviewPage 。
他们也可以导航到order 页面,该页面有已下订单的列表。
OBX
虽然GetBuilder速度快,内存占用少,但它不是反应式的。
Obx是GetX生态系统中的一个反应式状态管理器。GetX将反应式编程范式变成相当简单的东西。
- 没有必要再为每个变量创建StreamControllers和StreamBuilder。
- OBX为你省去了为每个状态创建一个类的压力,以及使用代码生成器。
本教程的重点是GetBuilder,然而,如果我们要使用反应式流(OBX),OrderController 类会是这样的。
import 'package:get/get.dart';
import 'package:shopping_app/models/cart_item.dart';
import 'package:shopping_app/models/order.dart';
class OrderController extends GetxController {
var _orders = [].obs;
List<Order> get orders {
return [..._orders];
}
void addOrder(List<CartItem> cartProducts, double total) {
_orders.insert(
0,
Order(
id: DateTime.now().toString(),
products: cartProducts,
amount: total,
dateTime: DateTime.now()));
}
}
在上面的代码中。
我们声明了一个变量,该变量将保存所有订单的列表。我们通过使用点符号将其改为obs,使其成为可观察的。
每当订单发生变化时,所有使用它的小部件都会自动改变。
在addOrder 方法中,我们不需要手动调用update 方法来更新被绑定的UI。Obx 智能地观察并进行相应的更新。
要使用Obx ,将控制器绑定到视图上,如下图所示,包住widget。
body: Obx(() => ListView.builder(
itemCount: orderController.orders.length,
itemBuilder: (context, index) =>
OrderItem(orderController.orders[index])),
),
总结
在本教程中,你已经学会了如何用Flutter构建一个购物应用程序,以及使用GetX进行状态管理、导航和渲染部件。
源代码可以在这个Github资源库中找到。
同行评审贡献者:。Wanja Mike
类似文章
[

语言
如何创建一个可重复使用的React表单组件
阅读更多

语言, Node.js
用Next.js构建一个薪资系统
阅读更多

架构
在Django中创建和使用装饰器
阅读更多