Add: getting products from API
This commit is contained in:
80
lib/interfaces/items.dart
Normal file
80
lib/interfaces/items.dart
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
class GymItem {
|
||||||
|
final String id;
|
||||||
|
final String externalId;
|
||||||
|
final String title;
|
||||||
|
final String description;
|
||||||
|
final int count;
|
||||||
|
final double price;
|
||||||
|
final String categoryId;
|
||||||
|
final List<GymImage> images;
|
||||||
|
int localCount = 0;
|
||||||
|
|
||||||
|
GymItem({
|
||||||
|
required this.id,
|
||||||
|
required this.externalId,
|
||||||
|
required this.title,
|
||||||
|
required this.description,
|
||||||
|
required this.count,
|
||||||
|
required this.price,
|
||||||
|
required this.categoryId,
|
||||||
|
required this.images,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory GymItem.fromRawJson(String str) => GymItem.fromJson(json.decode(str));
|
||||||
|
|
||||||
|
String toRawJson() => json.encode(toJson());
|
||||||
|
|
||||||
|
factory GymItem.fromJson(Map<String, dynamic> json) => GymItem(
|
||||||
|
id: json["id"],
|
||||||
|
externalId: json["ExternalId"],
|
||||||
|
title: json["title"],
|
||||||
|
description: json["description"],
|
||||||
|
count: json["count"],
|
||||||
|
price: json["price"] / 100,
|
||||||
|
categoryId: json["categoryId"],
|
||||||
|
images: List<GymImage>.from(
|
||||||
|
json["images"].map((x) => GymImage.fromJson(x))),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"id": id,
|
||||||
|
"ExternalId": externalId,
|
||||||
|
"title": title,
|
||||||
|
"description": description,
|
||||||
|
"count": count,
|
||||||
|
"price": price * 100,
|
||||||
|
"categoryId": categoryId,
|
||||||
|
"images": List<dynamic>.from(images.map((x) => x.toJson())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class GymImage {
|
||||||
|
final String id;
|
||||||
|
final dynamic deletedAt;
|
||||||
|
final String url;
|
||||||
|
|
||||||
|
GymImage({
|
||||||
|
required this.id,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.url,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory GymImage.fromRawJson(String str) =>
|
||||||
|
GymImage.fromJson(json.decode(str));
|
||||||
|
|
||||||
|
String toRawJson() => json.encode(toJson());
|
||||||
|
|
||||||
|
factory GymImage.fromJson(Map<String, dynamic> json) => GymImage(
|
||||||
|
id: json["id"],
|
||||||
|
deletedAt: json["deletedAt"],
|
||||||
|
url: json["url"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"id": id,
|
||||||
|
"deletedAt": deletedAt,
|
||||||
|
"url": url,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,9 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:gymlink_module_web/components/app_bar.dart';
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
import 'package:gymlink_module_web/components/basket_item_card.dart';
|
import 'package:gymlink_module_web/components/basket_item_card.dart';
|
||||||
import 'package:gymlink_module_web/components/heading.dart';
|
import 'package:gymlink_module_web/components/heading.dart';
|
||||||
|
import 'package:gymlink_module_web/interfaces/items.dart';
|
||||||
import 'package:gymlink_module_web/pages/order_confirmation.dart';
|
import 'package:gymlink_module_web/pages/order_confirmation.dart';
|
||||||
import 'package:gymlink_module_web/providers/cart.dart';
|
import 'package:gymlink_module_web/providers/cart.dart';
|
||||||
import 'package:gymlink_module_web/providers/main.dart';
|
import 'package:gymlink_module_web/tools/items.dart';
|
||||||
import 'package:gymlink_module_web/tools/prefs.dart';
|
import 'package:gymlink_module_web/tools/prefs.dart';
|
||||||
import 'package:gymlink_module_web/tools/routes.dart';
|
import 'package:gymlink_module_web/tools/routes.dart';
|
||||||
import 'package:lazy_load_scrollview/lazy_load_scrollview.dart';
|
import 'package:lazy_load_scrollview/lazy_load_scrollview.dart';
|
||||||
@@ -56,46 +57,47 @@ class BasketPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _BasketPageState extends State<BasketPage> {
|
class _BasketPageState extends State<BasketPage> {
|
||||||
List<Map<String, dynamic>> cartItems = [];
|
List<GymItem> cartItems = [];
|
||||||
int totalPrice = 0;
|
double totalPrice = 0;
|
||||||
|
List<GymItem> gymCart = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
getCart().then((value) {
|
Future.microtask(() => getCart().then((value) async {
|
||||||
setState(() {
|
final items = await getItems(context);
|
||||||
cartItems = value.map((element) {
|
setState(() {
|
||||||
final item = cart.firstWhere((e) => e['id'] == element['id']);
|
gymCart = items;
|
||||||
return {...item, 'count': element['count'] as int};
|
cartItems = value.map((element) {
|
||||||
}).toList();
|
final item = gymCart.firstWhere((e) => e.id == element['id']);
|
||||||
totalPrice = cartItems.fold(
|
item.localCount = element['count'] as int;
|
||||||
0,
|
return item;
|
||||||
(sum, item) =>
|
}).toList();
|
||||||
sum + int.parse(item['price']) * item['count'] as int);
|
totalPrice = cartItems.fold(
|
||||||
});
|
0, (sum, item) => sum + item.price * item.localCount);
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateCart() {
|
void _updateCart() {
|
||||||
Provider.of<CartProvider>(context, listen: false).updateCartLength();
|
Provider.of<CartProvider>(context, listen: false).updateCartLength();
|
||||||
Provider.of<GymLinkProvider>(context, listen: false).onTokenReceived('123');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeItem(String id) async {
|
void removeItem(String id) async {
|
||||||
final item = cartItems.firstWhere((element) => element['id'] == id);
|
final item = cartItems.firstWhere((element) => element.id == id);
|
||||||
bool toDelete = false;
|
bool toDelete = false;
|
||||||
setState(() {
|
setState(() {
|
||||||
if (item['count'] > 1) {
|
if (item.localCount > 1) {
|
||||||
item['count']--;
|
item.localCount--;
|
||||||
cartItems[cartItems.indexOf(item)]['count'] = item['count'];
|
cartItems[cartItems.indexOf(item)].localCount = item.localCount;
|
||||||
} else {
|
} else {
|
||||||
toDelete = true;
|
toDelete = true;
|
||||||
}
|
}
|
||||||
totalPrice = cartItems.fold(0,
|
totalPrice =
|
||||||
(sum, item) => sum + int.parse(item['price']) * item['count'] as int);
|
cartItems.fold(0, (sum, item) => sum + item.price * item.localCount);
|
||||||
});
|
});
|
||||||
if (toDelete) {
|
if (toDelete) {
|
||||||
await _deleteItemAlert(id, item['name']);
|
await _deleteItemAlert(id, item.title);
|
||||||
} else {
|
} else {
|
||||||
await removeItemFromCart(id);
|
await removeItemFromCart(id);
|
||||||
}
|
}
|
||||||
@@ -103,15 +105,16 @@ class _BasketPageState extends State<BasketPage> {
|
|||||||
|
|
||||||
void addItem(String id) async {
|
void addItem(String id) async {
|
||||||
setState(() {
|
setState(() {
|
||||||
final item = cartItems.firstWhere((element) => element['id'] == id,
|
final item =
|
||||||
orElse: () => {
|
cartItems.firstWhere((element) => element.id == id, orElse: () {
|
||||||
...cart.firstWhere((element) => element['id'] == id),
|
final cartItem = gymCart.firstWhere((element) => element.id == id);
|
||||||
'count': 0
|
cartItem.localCount = 0;
|
||||||
});
|
return cartItem;
|
||||||
item['count']++;
|
});
|
||||||
cartItems[cartItems.indexOf(item)]['count'] = item['count'];
|
item.localCount++;
|
||||||
totalPrice = cartItems.fold(0,
|
cartItems[cartItems.indexOf(item)].localCount = item.localCount;
|
||||||
(sum, item) => sum + int.parse(item['price']) * item['count'] as int);
|
totalPrice =
|
||||||
|
cartItems.fold(0, (sum, item) => sum + item.price * item.localCount);
|
||||||
});
|
});
|
||||||
await addItemToCart(id);
|
await addItemToCart(id);
|
||||||
}
|
}
|
||||||
@@ -142,7 +145,7 @@ class _BasketPageState extends State<BasketPage> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
removeItemFromCart(id);
|
removeItemFromCart(id);
|
||||||
setState(() {
|
setState(() {
|
||||||
cartItems.removeWhere((element) => element['id'] == id);
|
cartItems.removeWhere((element) => element.id == id);
|
||||||
});
|
});
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_updateCart();
|
_updateCart();
|
||||||
@@ -224,76 +227,20 @@ class _BasketPageState extends State<BasketPage> {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
const GymLinkHeader(title: "Корзина"),
|
const GymLinkHeader(title: "Корзина"),
|
||||||
cartItems.isEmpty
|
gymCart.isEmpty
|
||||||
? Expanded(
|
? const Expanded(
|
||||||
child: Center(
|
child: Center(child: CircularProgressIndicator()))
|
||||||
child: Column(
|
: cartItems.isEmpty
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
? Expanded(
|
||||||
children: [
|
child: Center(
|
||||||
Text('Корзина пуста',
|
|
||||||
style: Theme.of(context).textTheme.bodyLarge),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => Navigator.pop(context, true),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
|
||||||
shape: const RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.all(Radius.circular(50)),
|
|
||||||
),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
),
|
|
||||||
child: const Text('Вернуться назад'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Expanded(
|
|
||||||
child: _buildRowOrCol(
|
|
||||||
context: context,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: LazyLoadScrollView(
|
|
||||||
onEndOfPage: _onLoad,
|
|
||||||
child: ListView.builder(
|
|
||||||
itemCount: cartItems.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final item = cartItems[index];
|
|
||||||
return BasketItemCard(
|
|
||||||
name: item['name'],
|
|
||||||
price: item['price'],
|
|
||||||
id: item['id'],
|
|
||||||
image: Image(
|
|
||||||
image: AssetImage('assets/${item['image']}'),
|
|
||||||
width: 50,
|
|
||||||
),
|
|
||||||
onTapPlus: () => addItem(item['id'].toString()),
|
|
||||||
onTapMinus: () {
|
|
||||||
removeItem(item['id'].toString());
|
|
||||||
},
|
|
||||||
quantity: item['count'].toString(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_buildSpacer(),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsetsDirectional.symmetric(
|
|
||||||
horizontal: 10, vertical: 10),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text('Корзина пуста',
|
||||||
'Итого: $totalPrice',
|
style: Theme.of(context).textTheme.bodyLarge),
|
||||||
),
|
const SizedBox(height: 10),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => Navigator.of(context).push(
|
onPressed: () => Navigator.pop(context, true),
|
||||||
CustomPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
const OrderConfirmationPage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@@ -302,27 +249,89 @@ class _BasketPageState extends State<BasketPage> {
|
|||||||
),
|
),
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
),
|
),
|
||||||
child: const Text('Оформить заказ'),
|
child: const Text('Вернуться назад'),
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: _clearCartAlert,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
|
||||||
shape: const RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.all(Radius.circular(50))),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
),
|
|
||||||
child: const Text('Очистить корзину'),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 50),
|
)
|
||||||
],
|
: Expanded(
|
||||||
),
|
child: _buildRowOrCol(
|
||||||
),
|
context: context,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: LazyLoadScrollView(
|
||||||
|
onEndOfPage: _onLoad,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: cartItems.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = cartItems[index];
|
||||||
|
return BasketItemCard(
|
||||||
|
name: item.title,
|
||||||
|
price: item.price.toString(),
|
||||||
|
id: item.id,
|
||||||
|
image: Image(
|
||||||
|
image: NetworkImage(item.images[0].url),
|
||||||
|
width: 50,
|
||||||
|
),
|
||||||
|
onTapPlus: () =>
|
||||||
|
addItem(item.id.toString()),
|
||||||
|
onTapMinus: () {
|
||||||
|
removeItem(item.id.toString());
|
||||||
|
},
|
||||||
|
quantity: item.localCount.toString(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildSpacer(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
|
horizontal: 10, vertical: 10),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Итого: $totalPrice',
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => Navigator.of(context).push(
|
||||||
|
CustomPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
const OrderConfirmationPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).primaryColor,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(50)),
|
||||||
|
),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: const Text('Оформить заказ'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _clearCartAlert,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).primaryColor,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(50))),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: const Text('Очистить корзину'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 50),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gymlink_module_web/components/app_bar.dart';
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
import 'package:gymlink_module_web/components/heading.dart';
|
import 'package:gymlink_module_web/components/heading.dart';
|
||||||
|
import 'package:gymlink_module_web/pages/basket.dart';
|
||||||
import 'package:gymlink_module_web/providers/cart.dart';
|
import 'package:gymlink_module_web/providers/cart.dart';
|
||||||
import 'package:gymlink_module_web/tools/prefs.dart';
|
import 'package:gymlink_module_web/tools/prefs.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/routes.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
//TODO: Сделать получение инфы через объект
|
//TODO: Сделать получение инфы через объект
|
||||||
@@ -83,38 +85,59 @@ class _DetailPageState extends State<DetailPage> {
|
|||||||
child: const Text('Добавить в корзину'),
|
child: const Text('Добавить в корзину'),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Row(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
Row(
|
||||||
icon: const Icon(Icons.remove),
|
mainAxisSize: MainAxisSize.min,
|
||||||
onPressed: () async {
|
children: [
|
||||||
await removeItemFromCart(widget.id);
|
IconButton(
|
||||||
setState(() {
|
icon: const Icon(Icons.remove),
|
||||||
if (quantity > 1) {
|
onPressed: () async {
|
||||||
quantity--;
|
await removeItemFromCart(widget.id);
|
||||||
} else {
|
setState(() {
|
||||||
isInCart = false;
|
if (quantity > 1) {
|
||||||
quantity = 0;
|
quantity--;
|
||||||
}
|
} else {
|
||||||
});
|
isInCart = false;
|
||||||
if (mounted) {
|
quantity = 0;
|
||||||
context.read<CartProvider>().updateCartLength();
|
}
|
||||||
}
|
});
|
||||||
},
|
if (mounted) {
|
||||||
),
|
context.read<CartProvider>().updateCartLength();
|
||||||
const SizedBox(width: 10),
|
}
|
||||||
Text('$quantity'),
|
},
|
||||||
const SizedBox(width: 10),
|
),
|
||||||
IconButton(
|
const SizedBox(width: 10),
|
||||||
icon: const Icon(Icons.add),
|
Text('$quantity'),
|
||||||
onPressed: () async {
|
const SizedBox(width: 10),
|
||||||
await addItemToCart(widget.id);
|
IconButton(
|
||||||
setState(() {
|
icon: const Icon(Icons.add),
|
||||||
quantity++;
|
onPressed: () async {
|
||||||
});
|
await addItemToCart(widget.id);
|
||||||
},
|
setState(() {
|
||||||
|
quantity++;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 10),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pushReplacement(context,
|
||||||
|
CustomPageRoute(builder: (context) => const BasketPage()));
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(50)),
|
||||||
|
),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: const Text('Открыть корзину'),
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -139,37 +162,43 @@ class _DetailPageState extends State<DetailPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
widget.image,
|
widget.image,
|
||||||
Padding(
|
widget.description != ''
|
||||||
padding: const EdgeInsetsDirectional.all(30),
|
? Padding(
|
||||||
child: ConstrainedBox(
|
padding: const EdgeInsetsDirectional.all(30),
|
||||||
constraints: const BoxConstraints(
|
child: ConstrainedBox(
|
||||||
minWidth: 340,
|
constraints: const BoxConstraints(
|
||||||
maxWidth: 340,
|
minWidth: 340,
|
||||||
maxHeight: 600,
|
maxWidth: 340,
|
||||||
),
|
maxHeight: 600,
|
||||||
child: Card(
|
),
|
||||||
elevation: 4,
|
child: Card(
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
elevation: 4,
|
||||||
shape: RoundedRectangleBorder(
|
color:
|
||||||
borderRadius: BorderRadius.circular(16),
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
),
|
shape: RoundedRectangleBorder(
|
||||||
child: SingleChildScrollView(
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsetsDirectional.all(15),
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
minHeight: 100,
|
|
||||||
),
|
),
|
||||||
child: Text(
|
child: SingleChildScrollView(
|
||||||
widget.description,
|
child: Padding(
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
padding:
|
||||||
|
const EdgeInsetsDirectional.all(15),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minHeight: 100,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
widget.description,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
: const SizedBox.shrink(),
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
Align(
|
||||||
alignment: const AlignmentDirectional(0, -1),
|
alignment: const AlignmentDirectional(0, -1),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -179,7 +208,7 @@ class _DetailPageState extends State<DetailPage> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Стоимость ${widget.price}',
|
'Стоимость ${widget.price}руб.',
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
),
|
),
|
||||||
_buildButton()
|
_buildButton()
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import 'dart:math';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gymlink_module_web/components/app_bar.dart';
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
import 'package:gymlink_module_web/components/item_card.dart';
|
import 'package:gymlink_module_web/components/item_card.dart';
|
||||||
|
import 'package:gymlink_module_web/interfaces/items.dart';
|
||||||
import 'package:gymlink_module_web/pages/basket.dart';
|
import 'package:gymlink_module_web/pages/basket.dart';
|
||||||
import 'package:gymlink_module_web/pages/detail.dart';
|
import 'package:gymlink_module_web/pages/detail.dart';
|
||||||
import 'package:gymlink_module_web/pages/order_history.dart';
|
import 'package:gymlink_module_web/pages/order_history.dart';
|
||||||
import 'package:gymlink_module_web/providers/cart.dart';
|
import 'package:gymlink_module_web/providers/cart.dart';
|
||||||
import 'package:gymlink_module_web/providers/main.dart';
|
import 'package:gymlink_module_web/tools/items.dart';
|
||||||
import 'package:gymlink_module_web/tools/prefs.dart';
|
import 'package:gymlink_module_web/tools/prefs.dart';
|
||||||
import 'package:gymlink_module_web/tools/relative.dart';
|
import 'package:gymlink_module_web/tools/relative.dart';
|
||||||
import 'package:gymlink_module_web/tools/routes.dart';
|
import 'package:gymlink_module_web/tools/routes.dart';
|
||||||
@@ -65,22 +66,22 @@ class MainPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _MainPageState extends State<MainPage> {
|
class _MainPageState extends State<MainPage> {
|
||||||
String searchText = '';
|
String searchText = '';
|
||||||
List<Map<String, String>> filteredData = [];
|
List<GymItem> filteredData = [];
|
||||||
|
List<GymItem> items = [];
|
||||||
int cartLength = 0;
|
int cartLength = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
filteredData = testData;
|
|
||||||
getCart().then((value) {
|
getCart().then((value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
cartLength = value.length;
|
cartLength = value.length;
|
||||||
});
|
});
|
||||||
}).whenComplete(() {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
getItems(context).then((value) => setState(() {
|
||||||
|
filteredData = value;
|
||||||
|
items = value;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _goToPage() async {
|
Future<void> _goToPage() async {
|
||||||
@@ -97,8 +98,8 @@ class _MainPageState extends State<MainPage> {
|
|||||||
|
|
||||||
void _onSearch() {
|
void _onSearch() {
|
||||||
setState(() {
|
setState(() {
|
||||||
filteredData = testData
|
filteredData = items
|
||||||
.where((element) => (element['name']!).contains(searchText))
|
.where((element) => (element.title).contains(searchText))
|
||||||
.toList();
|
.toList();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -106,7 +107,6 @@ class _MainPageState extends State<MainPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cartL = context.watch<CartProvider>().cartLength;
|
final cartL = context.watch<CartProvider>().cartLength;
|
||||||
final onError = context.read<GymLinkProvider>().onError;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: const GymLinkAppBar(),
|
appBar: const GymLinkAppBar(),
|
||||||
body: Column(
|
body: Column(
|
||||||
@@ -160,7 +160,6 @@ class _MainPageState extends State<MainPage> {
|
|||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
onError();
|
|
||||||
Navigator.of(context).push(CustomPageRoute(
|
Navigator.of(context).push(CustomPageRoute(
|
||||||
builder: (context) => const HistoryPage(),
|
builder: (context) => const HistoryPage(),
|
||||||
));
|
));
|
||||||
@@ -193,40 +192,48 @@ class _MainPageState extends State<MainPage> {
|
|||||||
onEndOfPage: _onLoad,
|
onEndOfPage: _onLoad,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
GridView.builder(
|
items.isEmpty
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
? const Center(child: CircularProgressIndicator())
|
||||||
crossAxisCount: min(
|
: filteredData.isEmpty
|
||||||
(MediaQuery.sizeOf(context).width ~/ 200).toInt(), 8),
|
? const Center(child: Text('Ничего не найдено'))
|
||||||
),
|
: GridView.builder(
|
||||||
itemCount: filteredData.length,
|
gridDelegate:
|
||||||
itemBuilder: (context, index) {
|
SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
final product = filteredData[index];
|
crossAxisCount: min(
|
||||||
return ProductCard(
|
(MediaQuery.sizeOf(context).width ~/ 200)
|
||||||
imagePath: Image(
|
.toInt(),
|
||||||
image: Image.network(
|
8),
|
||||||
'https://rus-sport.net/upload/iblock/311/topb85ez18pq0aavohpa5zipk2sbfxll.jpg')
|
),
|
||||||
.image,
|
itemCount: filteredData.length,
|
||||||
width: 50,
|
itemBuilder: (context, index) {
|
||||||
),
|
final product = filteredData[index];
|
||||||
name: product['name']!,
|
return ProductCard(
|
||||||
price: product['price']!,
|
imagePath: Image(
|
||||||
onTap: () => Navigator.of(context).push(
|
image: Image.network(product.images[0].url)
|
||||||
CustomPageRoute(
|
.image,
|
||||||
builder: (context) => DetailPage(
|
width: 50,
|
||||||
name: product['name']!,
|
),
|
||||||
description: product['details']!,
|
name: product.title,
|
||||||
price: product['price']!,
|
price: product.price.toString(),
|
||||||
id: product['id']!,
|
onTap: () => Navigator.of(context).push(
|
||||||
image: Image(
|
CustomPageRoute(
|
||||||
image:
|
builder: (context) => DetailPage(
|
||||||
AssetImage('assets/${product['image']!}'),
|
name: product.title,
|
||||||
width: 300),
|
description: product.description,
|
||||||
|
price: product.price.toString(),
|
||||||
|
id: product.id,
|
||||||
|
image: Image(
|
||||||
|
image: Image.network(
|
||||||
|
product.images[0].url)
|
||||||
|
.image,
|
||||||
|
width: 300,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import 'package:flutter_markdown/flutter_markdown.dart';
|
|||||||
import 'package:gymlink_module_web/components/app_bar.dart';
|
import 'package:gymlink_module_web/components/app_bar.dart';
|
||||||
import 'package:gymlink_module_web/components/heading.dart';
|
import 'package:gymlink_module_web/components/heading.dart';
|
||||||
import 'package:gymlink_module_web/components/order_confirm_item_card.dart';
|
import 'package:gymlink_module_web/components/order_confirm_item_card.dart';
|
||||||
|
import 'package:gymlink_module_web/interfaces/items.dart';
|
||||||
import 'package:gymlink_module_web/pages/order_history.dart';
|
import 'package:gymlink_module_web/pages/order_history.dart';
|
||||||
import 'package:gymlink_module_web/providers/cart.dart';
|
import 'package:gymlink_module_web/providers/cart.dart';
|
||||||
|
import 'package:gymlink_module_web/tools/items.dart';
|
||||||
import 'package:gymlink_module_web/tools/prefs.dart';
|
import 'package:gymlink_module_web/tools/prefs.dart';
|
||||||
import 'package:gymlink_module_web/tools/routes.dart';
|
import 'package:gymlink_module_web/tools/routes.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@@ -56,24 +58,26 @@ class OrderConfirmationPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _OrderConfirmationPageState extends State<OrderConfirmationPage> {
|
class _OrderConfirmationPageState extends State<OrderConfirmationPage> {
|
||||||
List<Map<String, dynamic>> cartItems = [];
|
List<GymItem> cartItems = [];
|
||||||
int totalPrice = 0;
|
double totalPrice = 0;
|
||||||
|
List<GymItem> gymCart = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
getCart().then((value) {
|
Future.microtask(() => getCart().then((value) async {
|
||||||
setState(() {
|
final items = await getItems(context);
|
||||||
cartItems = value.map((element) {
|
setState(() {
|
||||||
final item = cart.firstWhere((e) => e['id'] == element['id']);
|
gymCart = items;
|
||||||
return {...item, 'count': element['count'] as int};
|
cartItems = value.map((element) {
|
||||||
}).toList();
|
final item = gymCart.firstWhere((e) => e.id == element['id']);
|
||||||
totalPrice = cartItems.fold(
|
item.localCount = element['count'] as int;
|
||||||
0,
|
return item;
|
||||||
(sum, item) =>
|
}).toList();
|
||||||
sum + int.parse(item['price']) * item['count'] as int);
|
totalPrice = cartItems.fold(
|
||||||
});
|
0, (sum, item) => sum + item.price * item.localCount);
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _goToPage() async {
|
Future<void> _goToPage() async {
|
||||||
@@ -101,13 +105,13 @@ class _OrderConfirmationPageState extends State<OrderConfirmationPage> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = cartItems[index];
|
final item = cartItems[index];
|
||||||
return OrderConfirmItemCard(
|
return OrderConfirmItemCard(
|
||||||
name: item['name'],
|
name: item.title,
|
||||||
image: Image(
|
image: Image(
|
||||||
image: AssetImage('assets/${item['image']}'),
|
image: NetworkImage(item.images[0].url),
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 50),
|
height: 50),
|
||||||
price: double.parse(item['price']),
|
price: item.price,
|
||||||
count: item['count'],
|
count: item.localCount,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
33
lib/tools/items.dart
Normal file
33
lib/tools/items.dart
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gymlink_module_web/interfaces/items.dart';
|
||||||
|
import 'package:gymlink_module_web/providers/main.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
Future<List<GymItem>> getItems(BuildContext context) async {
|
||||||
|
final token = context.read<GymLinkProvider>().token;
|
||||||
|
if (token != '') {
|
||||||
|
final Uri url =
|
||||||
|
Uri.http('gymlink.freemyip.com:8080', 'api/product/get-list');
|
||||||
|
try {
|
||||||
|
final response = await http.get(url, headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
});
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data =
|
||||||
|
jsonDecode(utf8.decode(response.bodyBytes)) as List<dynamic>;
|
||||||
|
final items = data.map((e) => GymItem.fromJson(e)).toList();
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
throw Error();
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('error: $e');
|
||||||
|
return await Future.delayed(
|
||||||
|
const Duration(seconds: 5), () => getItems(context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.read<GymLinkProvider>().onError();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user