import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:shopping_app/api/api_service.dart'; import 'package:shopping_app/models/cart_item.dart'; import 'package:shopping_app/models/product.dart';

class ShoppingCartPage extends StatefulWidget { const ShoppingCartPage({Key? key}) : super(key: key);

@override _ShoppingCartPageState createState() => _ShoppingCartPageState(); }

class _ShoppingCartPageState extends State { late List<List> cartList; late double totalPrice;

@override void initState() { super.initState(); _getShoppingCart(); }

Future _getShoppingCart() async { try { List jsonData = await ApiService.getShoppingCart(); setState(() { cartList = jsonData[0] .map((cartJson) => CartItem.fromJson(cartJson, Product.fromJson(cartJson[1]))) .toList() .fold<List<List>>([], (prev, cur) { if (prev.isNotEmpty && prev.last.first.cartId == cur.cartId) { prev.last.add(cur); } else { prev.add([cur]); } return prev; }); totalPrice = jsonData[1]; }); } catch (e) { Fluttertoast.showToast(msg: '获取购物车失败,请检查网络连接'); } }

Future _updateCartItem(CartItem item, int quantity) async { try { dynamic jsonData = await ApiService.updateCart(item.product.productId, quantity); setState(() { totalPrice = jsonData[0]; item.quantity = jsonData[1]['quantity']; item.price = jsonData[1]['price']; }); } catch (e) { Fluttertoast.showToast(msg: '更新购物车失败,请检查网络连接'); } }

Future _deleteCartItem(CartItem item) async { try { dynamic jsonData = await ApiService.deleteCart(item.product.productId); setState(() { totalPrice = jsonData; cartList.forEach((group) => group.remove(item)); cartList.removeWhere((group) => group.isEmpty); }); } catch (e) { Fluttertoast.showToast(msg: '删除购物车商品失败,请检查网络连接'); } }

Widget _buildCartItem(CartItem item) { return GestureDetector( onTap: () async { int? result = await showDialog( context: context, builder: (context) => _buildQuantityDialog(item), ); if (result != null) { await _updateCartItem(item, result); } }, child: Container( padding: EdgeInsets.symmetric(vertical: 10, horizontal: 15), decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey.shade300)), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 80, height: 100, margin: EdgeInsets.only(right: 10), decoration: BoxDecoration( image: DecorationImage( image: NetworkImage(item.product.imageUrl), fit: BoxFit.cover, ), ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(item.product.name, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis), SizedBox(height: 5), Row( children: [ Text('单价:'), Text( '¥${item.product.price}', style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold), ), ], ), SizedBox(height: 5), Row( children: [ Text('数量:'), Text(item.quantity.toString(), style: TextStyle(fontWeight: FontWeight.bold)), ], ), ], ), ), GestureDetector( onTap: () async { bool? confirm = await showDialog(context: context, builder: (context) { return AlertDialog( title: Text('确认删除?'), content: Text('您确定要从购物车中删除此商品吗?'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text('取消'), ), TextButton( onPressed: () => Navigator.of(context).pop(true), child: Text('删除'), ), ], ); }); if (confirm == true) { await _deleteCartItem(item); } }, child: Icon( Icons.delete_outline, size: 20, color: Colors.grey.shade500, ), ), ], ), ), ); }

Widget _buildQuantityDialog(CartItem item) { return AlertDialog( title: Text('修改数量'), content: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('当前数量:${item.quantity}'), Text('单价:${item.product.price}元'), ], ), SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('数量:'), SizedBox( width: 120, child: TextField( keyboardType: TextInputType.number, controller: TextEditingController( text: item.quantity.toString()), decoration: InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), ), ], ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(null), child: Text('取消'), ), TextButton( onPressed: () { String text = ( context.findWidgetThatIsScrollable()!.controller as TextEditingController) .text; int quantity = int.tryParse(text) ?? 0; if (quantity <= 0) { Fluttertoast.showToast(msg: '数量必须为正整数'); } else { Navigator.of(context).pop(quantity); } }, child: Text('确定'), ), ], ); }

Widget _buildCheckoutButton() { return ElevatedButton( onPressed: () async { List addressList = await ApiService.getAddresses(); bool? result = await showDialog( context: context, builder: (context) => _buildPaymentDialog(addressList), ); if (result == true) { // 结算 try { await ApiService.submitOrder(1013, '支付宝'); Fluttertoast.showToast(msg: '结算成功!'); _getShoppingCart(); } catch (e) { Fluttertoast.showToast(msg: '结算失败,请检查网络连接'); } } }, child: Text('结算'), ); }

Widget _buildPaymentDialog(List addressList) { return AlertDialog( title: Text('结算'), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text('选择收货地址:'), SizedBox(height: 10), ...addressList.map((address) { return RadioListTile( title: Text('${address['name']} ${address['phone']}'), subtitle: Text('${address['country']} ${address['provience']} ${address['city']} ${address['district']} ${address['addressLine']}'), value: address, groupValue: null, onChanged: null, ); }).toList(), SizedBox(height: 20), Text('选择支付方式:'), SizedBox(height: 10), Row( children: [ Radio( value: '支付宝', groupValue: null, onChanged: null, ), Text('支付宝'), ], ), Row( children: [ Radio( value: '微信', groupValue: null, onChanged: null, ), Text('微信'), ], ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text('取消'), ), TextButton( onPressed: () => Navigator.of(context).pop(true), child: Text('结算'), ), ], ); }

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('购物车'), ), body: cartList == null ? Center(child: CircularProgressIndicator()) : cartList.isEmpty ? Center(child: Text('购物车为空')) : Column( children: [ Expanded( child: ListView.builder( itemCount: cartList.length, itemBuilder: (context, index) { return Column( children: [ Container( padding: EdgeInsets.symmetric(vertical: 10, horizontal: 15), color: Colors.grey.shade100, child: Row( children: [ Icon(Icons.location_on, size: 16), SizedBox(width: 5), Text( '收货地址', style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(width: 10), Text( '东北石油大学', ), ], ), ), ...cartList[index].map(_buildCartItem), ], ); }, ), ), Container( padding: EdgeInsets.symmetric(vertical: 10, horizontal: 15), decoration: BoxDecoration( border: Border(top: BorderSide(color: Colors.grey.shade300)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('合计:¥$totalPrice'), _buildCheckoutButton(), ], ), ), ], ), ); } }

class CartItem { late int cartId; late int quantity; late double price; late Product product;

CartItem.fromJson(Map<String, dynamic> json, Product product) : cartId = json['cartid'], quantity = json['quantity'], price = json['price'], product = product; }

// API 服务代码 import 'dart:convert'; import 'package:dio/dio.dart'; import '../models/product.dart';

class ApiService { static final String baseUrl = 'http://book.musecloud.tech'; static Dio _dio = Dio(); // 全局Dio实例 static Dio dioWithCookie = Dio(); //登录

// 登录 static Future login(String email, String password) async { print(email); print(password); //拦截302跳转并获取cookie Response response = await _dio.get( '$baseUrl/user/login', queryParameters: { 'name': email, 'password': password, }, options: Options( followRedirects: false, validateStatus: (status) { print(status); return status! < 500; }, ), ); //如果response.data包含特色书籍字符串,则登录成功 if (response.statusCode == 302) { //保存cookie String? cookie = response.headers['set-cookie']?[0]; print(response.headers['set-cookie']); //JSESSIONID=255559E896E3DDA5480CE00C6D01683D; Path=/; HttpOnly //只要JESSIONID=255559E896E3DDA5480CE00C6D01683D cookie = cookie!.substring(0, cookie.indexOf(';')); //将cookie保存到全局Dio实例中 dioWithCookie.options.headers['cookie'] = cookie; return true; } else { return false; } }

// 获取产品详情 static Future getProductDetail(int productId) async { try { Response response = await _dio.get( '$baseUrl/getproductdetail', queryParameters: { 'productId': productId, }, ); dynamic jsonData = jsonDecode(response.data); Product product = Product.fromJson(jsonData); return product; } catch (e) { throw e; } }

// 获取产品列表 static Future<List> 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 jsonData = jsonDecode(response.data); List products = jsonData.map((json) => Product.fromJson(json)).toList(); return products; } catch (e) { throw e; } }

// 添加到购物车 static Future addToCart(int productId, int quantity) async { try { Response response = await dioWithCookie.get( '$baseUrl/addcart', queryParameters: { 'productId': productId, 'quantity': quantity, }, ); String result = response.data; return result; } catch (e) { throw e; } }

// 获取购物车 static Future<List> getShoppingCart() async { try { Response response = await dioWithCookie.get('$baseUrl/getshoppingcart'); List jsonData = jsonDecode(response.data); return jsonData; } catch (e) { throw e; } }

// 更新购物车 static Future<List> updateCart(int productId, int quantity) async { try { Response response = await dioWithCookie.get( '$baseUrl/updatecart', queryParameters: { 'productId': productId, 'quantity': quantity, }, ); List jsonData = jsonDecode(response.data); return jsonData; } catch (e) { throw e; } }

// 删除购物车商品 static Future deleteCart(int productId) async { try { Response response = await dioWithCookie.get( '$baseUrl/deletecart', queryParameters: { 'productId': productId, }, ); double jsonData = double.parse(response.data); return jsonData; } catch (e) { throw e; } }

// 获取收货地址 static Future<List> getAddresses() async { try { Response response = await dioWithCookie.get('$baseUrl/getaddresses'); List jsonData = jsonDecode(response.data); return jsonData; } catch (e) { throw e; } }

// 提交订单 static Future submitOrder(int addressId, String paymentMethod) async { try { Response response = await dioWithCookie.get( '$baseUrl/submitorder', queryParameters: { 'addressId': addressId, 'paymentMethod': paymentMethod, }, ); } catch (e) { throw e; } } }

Flutter 购物车页面开发实战:UI 设计、数据解析、API 调用

原文地址: http://www.cveoy.top/t/topic/oi1t 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录