Skip to content

Commit 57763a5

Browse files
committed
Implement variable product add to cart API integration
1 parent da427d3 commit 57763a5

7 files changed

Lines changed: 189 additions & 19 deletions

File tree

lib/core/pair.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class Pair<T, R> {
2+
final T first;
3+
final R second;
4+
5+
Pair(this.first, this.second);
6+
}

lib/datasource/cart_data_source.dart

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@ import 'dart:convert';
22

33
import 'package:dio/dio.dart';
44
import 'package:wooapp/api/cocart_api_client.dart';
5+
import 'package:wooapp/api/wp_api_client.dart';
6+
import 'package:wooapp/core/pair.dart';
57
import 'package:wooapp/database/database.dart';
68
import 'package:wooapp/locator.dart';
79
import 'package:wooapp/model/cart_response.dart';
10+
import 'package:wooapp/model/variations_response.dart';
811

912
class CartDataSourceImpl extends CartDataSource {
13+
final WpApiClient _wp = locator<WpApiClient>();
1014
final CoCartApiClient _api = locator<CoCartApiClient>();
1115
final AppDb _db = locator<AppDb>();
1216

1317
Future<Dio> _sendUserRequest() => _db.getUser()
1418
.then((user) => _api.withHeaders({
15-
'Authorization': 'Basic ${base64Encode(utf8.encode('${user.login}:${user.password}'))}'
19+
'Authorization': 'Basic ${base64Encode(utf8.encode('${user.login}:${user.password}'))}',
20+
'Content-Type': 'application/json; charset=UTF-8',
1621
}));
1722

1823
@override
@@ -30,8 +35,8 @@ class CartDataSourceImpl extends CartDataSource {
3035
@override
3136
Future<Response> addItem(int id, int count) => _sendUserRequest()
3237
.then((dio) => dio.post('cart/add-item', data: {
33-
'id': id,
34-
'quantity': count,
38+
'id': '$id',
39+
'quantity': '$count',
3540
})).then((response) {
3641
if (response.statusCode == 200) _db.addToCart(id);
3742
return response;
@@ -41,15 +46,22 @@ class CartDataSourceImpl extends CartDataSource {
4146
Future<Response> addVariableItem(
4247
int id,
4348
int count,
44-
Map<String, dynamic> variation,
49+
Map<String, String> map,
4550
) =>
46-
_sendUserRequest().then(
47-
(dio) => dio.post(
51+
_wp.dio.get('wp/v3/variations?id=$id')
52+
.then((response) => (response.data as List).map((v) => Variation.fromJson(v)).toList())
53+
.then((variations) => mapVariations(variations, map))
54+
.then((value) async {
55+
var dio = await _sendUserRequest();
56+
return Pair<Dio, Map<String, String>>(dio, value);
57+
})
58+
.then(
59+
(payload) => payload.first.post(
4860
'cart/add-item',
4961
data: {
50-
'id': id,
51-
'quantity': count,
52-
'variation': variation,
62+
"id": id.toString(),
63+
"quantity": count.toString(),
64+
"variation": payload.second,
5365
},
5466
),
5567
).then((response) {
@@ -91,7 +103,7 @@ abstract class CartDataSource {
91103

92104
Future<Response> addItem(int id, int count);
93105

94-
Future<Response> addVariableItem(int id, int count, Map<String, dynamic> variation);
106+
Future<Response> addVariableItem(int id, int count, Map<String, String> map);
95107

96108
Future<Response> updateQuantity(String itemKey, int count);
97109

@@ -101,3 +113,18 @@ abstract class CartDataSource {
101113

102114
Future<Response> clearCart();
103115
}
116+
117+
Map<String, String> mapVariations(List<Variation> variations, Map<String, String> input) {
118+
Map<String, String> result = {};
119+
print('DBG0 - $input');
120+
for (var key in input.keys) {
121+
Variation? variation = variations.firstWhere((v) => v.name == key, orElse: null);
122+
if (variation == null) continue;
123+
Term? term = variation.terms.firstWhere((t) => t.name == input[key], orElse: null);
124+
if (term == null) continue;
125+
result.addAll({
126+
'attribute_${variation.slug}': '${term.slug}',
127+
});
128+
}
129+
return result;
130+
}

lib/datasource/customer_auth_data_source.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class CustomerAuthDataSourceImpl extends CustomerAuthDataSource {
2323
@override
2424
Future<UserData> login(String login, String password) => _apiCoCart
2525
.withHeaders({
26-
'Authorization': 'Basic ${base64Encode(utf8.encode('$login:$password'))}'
26+
'Authorization': 'Basic ${base64Encode(utf8.encode('$login:$password'))}',
27+
'Content-Type': 'application/json; charset=UTF-8',
2728
})
2829
.post('login')
2930
.then((response) => UserData.fromJson(response.data))

lib/model/variations_response.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class Variation {
2+
final String slug;
3+
final String name;
4+
final List<Term> terms;
5+
6+
Variation.fromJson(Map<String, dynamic> json)
7+
: slug = json['slug'],
8+
name = json['name'],
9+
terms = (json['terms'] as List).map((t) => Term.fromJson(t)).toList();
10+
}
11+
12+
class Term {
13+
final String name;
14+
final String slug;
15+
16+
Term.fromJson(Map<String, dynamic> json)
17+
: name = json['name'],
18+
slug = json['slug'];
19+
}

lib/screens/cart/cart_view.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,14 @@ class CartView extends StatelessWidget {
174174
Navigator.of(context)
175175
.push(MaterialPageRoute(builder: (_) => ProductScreen(item.id )));
176176
},
177-
child: Text(
178-
'${item.name}',
179-
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w400),
177+
child: Container(
178+
width: MediaQuery.of(context).size.width - 180,
179+
child: Text(
180+
'${item.name}',
181+
maxLines: 1,
182+
overflow: TextOverflow.ellipsis,
183+
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w400),
184+
),
180185
),
181186
),
182187
Spacer(),

lib/widget/widget_add_to_cart.dart

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:wooapp/locator.dart';
1010
import 'package:wooapp/model/product.dart';
1111
import 'package:wooapp/screens/orders/create/create_order_screen.dart';
1212
import 'package:wooapp/widget/widget_product_stock.dart';
13+
import 'package:wooapp/widget/widget_product_variations.dart';
1314

1415
import 'widget_diaolg.dart';
1516

@@ -176,6 +177,7 @@ class _AddToCartBottomBarState extends State<AddToCartBottomBar> {
176177

177178
Widget _buildAddButton() => ElevatedButton(
178179
onPressed: () {
180+
// _processSimpleProduct();return;
179181
if (widget.product.isVariable) {
180182
_processVariableProduct();
181183
} else {
@@ -217,22 +219,38 @@ class _AddToCartBottomBarState extends State<AddToCartBottomBar> {
217219
);
218220

219221
void _processSimpleProduct() {
220-
widget._ds.addItem(widget.product.id, getCount());
221-
setState(() {
222-
_inCart = true;
222+
widget._ds.addItem(widget.product.id, getCount()).then((response) {
223+
setState(() {
224+
_inCart = true;
225+
});
223226
});
224227
}
225228

226229
void _processVariableProduct() {
230+
var key = GlobalKey<ProductVariationSelectionWidgetState>();
227231
showDialog(
228232
context: context,
229233
builder: (ctx) => WooDialog(
230234
type: WooDialogType.widget,
231235
title: 'Select product properties',
232-
content: Text('varareara'),
236+
content: ProductVariationSelectionWidget(
237+
key: key,
238+
product: widget.product,
239+
),
233240
buttonPositiveText: 'Add',
234241
onPositiveButton: () {
235-
242+
var variations = key.currentState?.getVariations();
243+
if (variations != null) {
244+
widget._ds.addVariableItem(
245+
widget.product.id,
246+
getCount(),
247+
variations,
248+
).then((response) {
249+
setState(() {
250+
_inCart = true;
251+
});
252+
});
253+
}
236254
},
237255
),
238256
);
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:wooapp/model/attribute.dart';
3+
import 'package:wooapp/model/product.dart';
4+
5+
class ProductVariationSelectionWidget extends StatefulWidget {
6+
final Product product;
7+
8+
ProductVariationSelectionWidget({super.key, required this.product});
9+
10+
@override
11+
State<StatefulWidget> createState() =>
12+
ProductVariationSelectionWidgetState();
13+
}
14+
15+
class ProductVariationSelectionWidgetState
16+
extends State<ProductVariationSelectionWidget> {
17+
18+
List<ProductAttribute> _variableAttrs = [];
19+
Map<String, String> _variations = {};
20+
21+
@override
22+
void initState() {
23+
_genVariations();
24+
super.initState();
25+
}
26+
27+
@override
28+
Widget build(BuildContext context) => Container(
29+
padding: EdgeInsets.symmetric(horizontal: 16),
30+
child: Column(
31+
children: _variableAttrs
32+
.map((attr) => _renderVariation(attr))
33+
.toList(),
34+
),
35+
);
36+
37+
Map<String, String> getVariations() => _variations;
38+
39+
Widget _renderVariation(ProductAttribute attr) => Row(
40+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
41+
children: [
42+
Text(
43+
attr.name,
44+
style: TextStyle(
45+
fontSize: 17,
46+
fontWeight: FontWeight.w400,
47+
),
48+
),
49+
DropdownButtonHideUnderline(
50+
child: DropdownButton<String>(
51+
value: _variations['${attr.name}'],
52+
items: attr.options
53+
.map<DropdownMenuItem<String>>(
54+
(value) => DropdownMenuItem<String>(
55+
value: value,
56+
child: Text(
57+
value,
58+
style: TextStyle(
59+
fontSize: 17,
60+
fontWeight: FontWeight.w500,
61+
),
62+
),
63+
),
64+
)
65+
.toList(),
66+
icon: const Icon(
67+
Icons.keyboard_arrow_down,
68+
size: 24,
69+
// color: AppTheme.green_v2,
70+
),
71+
onChanged: (value) {
72+
setState(() {
73+
_variations['${attr.name}'] = value.toString();
74+
});
75+
},
76+
)
77+
),
78+
],
79+
);
80+
81+
void _genVariations() {
82+
for (var attr in widget.product.attributes) {
83+
if (!attr.variation) continue;
84+
if (!attr.visible) continue;
85+
_variableAttrs.add(attr);
86+
}
87+
for (var attr in _variableAttrs) {
88+
_variations.addAll({
89+
'${attr.name}': attr.options[0],
90+
});
91+
}
92+
setState(() {});
93+
}
94+
}

0 commit comments

Comments
 (0)