Flutter商城商品详情页添加购物车按钮和数量选择功能
Flutter商城商品详情页添加购物车按钮和数量选择功能
本文将介绍如何在Flutter商城商品详情页添加购物车按钮和数量选择功能,包括悬浮购物车按钮,长按动画展开购物车商品列表,添加结算按钮,以及网络请求和数据解析。
需求分析
- 商品详情页缺少一个添加到购物车按钮,而且没有数量选择功能。
- 购物车按钮想要做成悬浮的,长按可以动画展开显示所有购物车商品,然后添加一个结算按钮,可以直接跳转到结算页面。
- 所有网络请求都要写在
ApiService类中,并且写好对应的model和api_service。
接口说明
- 添加到购物车接口:
http://book.musecloud.tech/addcart?productId=-1896935422&quantity=1,如果添加成功则返回success。 - 购物车接口:
http://book.musecloud.tech/getshoppingcart,返回的数据是JSON数组,第一个是购物车所有商品信息,第二个是每个商品的信息,第三个是总价格。
代码实现
1. Model类
import 'dart:convert';
class CartItem {
final int productId;
final String productName;
final double price;
final int quantity;
final String imageUrl;
CartItem({
required this.productId,
required this.productName,
required this.price,
required this.quantity,
required this.imageUrl,
});
factory CartItem.fromJson(Map<String, dynamic> json) {
return CartItem(
productId: json['productId'],
productName: json['productName'],
price: json['price'].toDouble(),
quantity: json['quantity'],
imageUrl: json['imageUrl'],
);
}
}
class CartData {
final List<CartItem> cartItems;
final double totalPrice;
CartData({
required this.cartItems,
required this.totalPrice,
});
factory CartData.fromJson(List<dynamic> json) {
return CartData(
cartItems: List<CartItem>.from(json[1].map((item) => CartItem.fromJson(item))),
totalPrice: json[2].toDouble(),
);
}
}
2. ApiService类
import 'dart:convert';
import 'package:dio/dio.dart';
import '../models/product.dart';
import '../models/cart.dart';
class ApiService {
static final String baseUrl = 'http://book.musecloud.tech';
static Dio _dio = Dio();
// 获取产品列表
static Future<List<Product>> getProductList({
required int page,
required int limit,
}) async {
try {
Response response = await _dio.get(
'$baseUrl/getproductlist',
queryParameters: {
'page': page,
'limit': limit,
'category': '',
'keyword': '',
'minprice': '',
'maxprice': '',
'origin': '',
'brand': '',
'supplier': '',
'isActive': '',
'isDeleted': '',
},
);
List<dynamic> jsonData = jsonDecode(response.data);
List<Product> products =
jsonData.map((json) => Product.fromJson(json)).toList();
return products;
} catch (e) {
throw e;
}
}
// 获取产品详情
static Future<Product> getProductDetail(int productId) async {
try {
Response response = await _dio.get('$baseUrl/getproductdetail?id=$productId');
Map<String, dynamic> jsonData = jsonDecode(response.data);
return Product.fromJson(jsonData);
} catch (e) {
throw e;
}
}
// 添加到购物车
static Future<String> addToCart(int productId, int quantity) async {
try {
Response response = await _dio.get('$baseUrl/addcart', queryParameters: {
'productId': productId,
'quantity': quantity,
});
return response.data;
} catch (e) {
throw e;
}
}
// 获取购物车数据
static Future<CartData> getShoppingCart() async {
try {
Response response = await _dio.get('$baseUrl/getshoppingcart');
List<dynamic> jsonData = jsonDecode(response.data);
return CartData.fromJson(jsonData);
} catch (e) {
throw e;
}
}
}
3. 商品详情页代码
import 'package:cached_network_image/cached_network_image.dart';
import 'package:ecommerce_muse/models/product.dart';
import 'package:ecommerce_muse/services/api_service.dart';
import 'package:flutter/material.dart';
class ProductDetailPage extends StatefulWidget {
final int productId;
ProductDetailPage({required this.productId});
@override
_ProductDetailPageState createState() =>
_ProductDetailPageState(productId: productId);
}
class _ProductDetailPageState extends State<ProductDetailPage> {
final int productId;
late Future<Product> _productFuture;
int _quantity = 1;
bool _showShoppingCart = false;
_ProductDetailPageState({required this.productId});
@override
void initState() {
super.initState();
_productFuture = ApiService.getProductDetail(productId);
}
void _addToCart() async {
try {
String result = await ApiService.addToCart(productId, _quantity);
if (result == 'success') {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('添加到购物车成功'),
duration: Duration(seconds: 2),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('添加到购物车失败,请重试'),
duration: Duration(seconds: 2),
),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('添加到购物车失败,请检查网络连接'),
duration: Duration(seconds: 2),
),
);
}
}
Widget _buildFloatingCartButton() {
return FloatingActionButton(
onPressed: () {
setState(() {
_showShoppingCart = !_showShoppingCart;
});
},
child: Stack(
children: [
Icon(Icons.shopping_cart),
Positioned(
top: 0,
right: 0,
child: Container(
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
child: Text(
'1', // 替换成购物车中商品数量的变量
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
),
],
),
);
}
Widget _buildQuantitySelector() {
return Row(
children: [
IconButton(
onPressed: () {
setState(() {
if (_quantity > 1) {
_quantity--;
}
});
},
icon: Icon(Icons.remove),
),
Text(
_quantity.toString(),
style: TextStyle(fontSize: 18),
),
IconButton(
onPressed: () {
setState(() {
_quantity++;
});
},
icon: Icon(Icons.add),
),
],
);
}
Widget _buildShoppingCart() {
return Container(
padding: EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.0),
boxShadow: [BoxShadow(color: Colors.grey, blurRadius: 4.0)],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('购物车商品', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
// 添加购物车商品列表
// ...
ElevatedButton(
onPressed: () {
// 跳转到结算页面
},
child: Text('结算'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('商品详情'),
),
body: Stack(
children: [
FutureBuilder<Product>(
future: _productFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
return _buildProductDetail(snapshot.data!);
} else if (snapshot.hasError) {
return Center(
child: Text('加载商品详情失败'),
);
}
return Center(
child: CircularProgressIndicator(),
);
},
),
if (_showShoppingCart) _buildShoppingCart(),
],
),
floatingActionButton: _buildFloatingCartButton(),
);
}
Widget _buildProductDetail(Product product) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CachedNetworkImage(
imageUrl: product.imageUrl,
fit: BoxFit.cover,
errorWidget: (context, url, error) => Icon(Icons.error),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
'价格: ¥${product.price.toStringAsFixed(2)}',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 8),
Text(
'品牌: ${product.brand}',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 8),
Text(
'描述: ${product.description}',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 8),
Text(
'数量: ',
style: TextStyle(fontSize: 18),
),
_buildQuantitySelector(),
SizedBox(height: 8),
ElevatedButton(
onPressed: _addToCart,
child: Text('添加到购物车'),
),
],
),
),
],
),
);
}
}
解释说明
CartItem类用于解析每个购物车商品信息,CartData类用于解析整个购物车数据。ApiService类中添加了addToCart和getShoppingCart方法,分别用于添加商品到购物车和获取购物车数据。ProductDetailPage类中添加了以下功能:- 使用
_quantity变量保存商品数量,初始值为1。 - 使用
_showShoppingCart变量控制购物车列表是否显示,初始值为false。 - 使用
_buildFloatingCartButton方法创建悬浮的购物车按钮,点击按钮可以控制购物车列表的显示隐藏。 - 使用
_buildQuantitySelector方法创建商品数量选择器。 - 使用
_buildShoppingCart方法创建购物车列表,包括商品列表和结算按钮。 - 在
_buildProductDetail方法中添加商品数量选择器和添加到购物车按钮。
- 使用
- 购物车按钮添加动画效果,可以参考Flutter官方文档中的动画相关内容。
总结
本文介绍了如何在Flutter商城商品详情页添加购物车按钮和数量选择功能,并提供了相应的代码实现。用户可以根据自己的需求进行调整和完善。
注意:
- 需要确保已配置好Flutter开发环境,并安装了Dio、cached_network_image等依赖库。
- 需要修改
ApiService类中的baseUrl为实际的API地址。 - 需要将
_buildShoppingCart方法中的// 添加购物车商品列表部分替换成实际的购物车商品列表。 - 需要将
_buildFloatingCartButton方法中的1替换成购物车中商品数量的变量。 - 需要将
_buildShoppingCart方法中的// 跳转到结算页面部分替换成实际的跳转逻辑。
希望本文对大家有所帮助。
原文地址: https://www.cveoy.top/t/topic/oieF 著作权归作者所有。请勿转载和采集!