Commit c39548b2 authored by Calebe Rocha's avatar Calebe Rocha
Browse files

Merge branch 'feat/us14-tela-detalhes-produto' into develop

parents 6d97f9e5 6bcffd71
Showing with 455 additions and 34 deletions
+455 -34
import 'package:flutter/material.dart';
enum OrderStatus {
IN_CART,
WAITING_FOR_PARTNER,
ACCEPTED,
REFUSED,
PREPARING,
PICKING_UP,
IN_FLIGHT,
DELIVERED
}
extension OrderStatusDescription on OrderStatus {
String get label {
switch (this) {
case OrderStatus.IN_CART:
return 'Carrinho';
case OrderStatus.WAITING_FOR_PARTNER:
return 'Aguardando';
case OrderStatus.ACCEPTED:
return 'Aceito';
case OrderStatus.REFUSED:
return 'Recusado';
case OrderStatus.PREPARING:
return 'Em preparo';
case OrderStatus.PICKING_UP:
return 'Em coleta';
case OrderStatus.IN_FLIGHT:
return 'Em entrega';
case OrderStatus.DELIVERED:
return 'Entregue';
default:
return 'Desconhecido';
}
}
Color get color {
switch (this) {
case OrderStatus.IN_CART:
return Colors.grey;
case OrderStatus.WAITING_FOR_PARTNER:
return Colors.yellow;
case OrderStatus.ACCEPTED:
return Colors.grey;
case OrderStatus.REFUSED:
return Colors.red;
case OrderStatus.PREPARING:
return Colors.orange;
case OrderStatus.PICKING_UP:
return Colors.blue;
case OrderStatus.IN_FLIGHT:
return Colors.blue;
case OrderStatus.DELIVERED:
return Colors.green;
default:
return Colors.grey;
}
}
}
import 'package:apus_drones_mobile/constants/OrderStatus.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:apus_drones_mobile/models/CustomerModel.dart';
import 'package:apus_drones_mobile/models/OrderItemModel.dart';
......@@ -8,7 +9,7 @@ part 'PartnerOrderModel.g.dart';
class PartnerOrderModel {
final int id;
final CustomerModel customer;
String status;
OrderStatus status;
final DateTime createdAt;
final String? deliveryAddress;
final List<OrderItemModel> items;
......
......@@ -10,7 +10,7 @@ PartnerOrderModel _$PartnerOrderModelFromJson(Map<String, dynamic> json) {
return PartnerOrderModel(
id: json['id'] as int,
customer: CustomerModel.fromJson(json['customer'] as Map<String, dynamic>),
status: json['status'] as String,
status: _$enumDecode(_$OrderStatusEnumMap, json['status']),
createdAt: DateTime.parse(json['createdAt'] as String),
deliveryAddress: json['deliveryAddress'] as String?,
items: (json['items'] as List<dynamic>)
......@@ -25,10 +25,47 @@ Map<String, dynamic> _$PartnerOrderModelToJson(PartnerOrderModel instance) =>
<String, dynamic>{
'id': instance.id,
'customer': instance.customer,
'status': instance.status,
'status': _$OrderStatusEnumMap[instance.status],
'createdAt': instance.createdAt.toIso8601String(),
'deliveryAddress': instance.deliveryAddress,
'items': instance.items,
'orderPrice': instance.orderPrice,
'deliveryPrice': instance.deliveryPrice,
};
K _$enumDecode<K, V>(
Map<K, V> enumValues,
Object? source, {
K? unknownValue,
}) {
if (source == null) {
throw ArgumentError(
'A value must be provided. Supported values: '
'${enumValues.values.join(', ')}',
);
}
return enumValues.entries.singleWhere(
(e) => e.value == source,
orElse: () {
if (unknownValue == null) {
throw ArgumentError(
'`$source` is not one of the supported values: '
'${enumValues.values.join(', ')}',
);
}
return MapEntry(unknownValue, enumValues.values.first);
},
).key;
}
const _$OrderStatusEnumMap = {
OrderStatus.IN_CART: 'IN_CART',
OrderStatus.WAITING_FOR_PARTNER: 'WAITING_FOR_PARTNER',
OrderStatus.ACCEPTED: 'ACCEPTED',
OrderStatus.REFUSED: 'REFUSED',
OrderStatus.PREPARING: 'PREPARING',
OrderStatus.PICKING_UP: 'PICKING_UP',
OrderStatus.IN_FLIGHT: 'IN_FLIGHT',
OrderStatus.DELIVERED: 'DELIVERED',
};
......@@ -13,20 +13,23 @@ class ProductModel {
final double weight;
final DateTime? createdAt;
final String imageUrl;
final List<String>? imagesUrls;
final PartnerModel? partner;
final int? quantity;
ProductModel(
{required this.id,
required this.name,
required this.description,
required this.price,
required this.status,
required this.weight,
required this.createdAt,
required this.imageUrl,
this.partner,
this.quantity});
ProductModel({
required this.id,
required this.name,
required this.description,
required this.price,
required this.status,
required this.weight,
required this.createdAt,
required this.imageUrl,
this.imagesUrls,
this.partner,
this.quantity,
});
factory ProductModel.fromJson(Map<String, dynamic> json) =>
_$ProductModelFromJson(json);
......
......@@ -18,6 +18,9 @@ ProductModel _$ProductModelFromJson(Map<String, dynamic> json) {
? null
: DateTime.parse(json['createdAt'] as String),
imageUrl: json['imageUrl'] as String,
imagesUrls: (json['imagesUrls'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
partner: json['partner'] == null
? null
: PartnerModel.fromJson(json['partner'] as Map<String, dynamic>),
......@@ -35,6 +38,7 @@ Map<String, dynamic> _$ProductModelToJson(ProductModel instance) =>
'weight': instance.weight,
'createdAt': instance.createdAt?.toIso8601String(),
'imageUrl': instance.imageUrl,
'imagesUrls': instance.imagesUrls,
'partner': instance.partner,
'quantity': instance.quantity,
};
import 'dart:math';
import 'package:apus_drones_mobile/constants/UserType.dart';
import 'package:apus_drones_mobile/models/ProductModel.dart';
import 'package:apus_drones_mobile/screens/Appbar.dart';
import 'package:apus_drones_mobile/services/ProductService.dart';
import 'package:apus_drones_mobile/utils/money.dart';
import 'package:apus_drones_mobile/widgets/BotNavBar.dart';
import 'package:apus_drones_mobile/widgets/QuantityChangerChip.dart';
import 'package:apus_drones_mobile/widgets/ProductCarousel.dart';
import 'package:flutter/material.dart';
class CustomerProductDetailScreen extends StatefulWidget {
static final String routeName = 'customer/productDetail';
final int productId;
CustomerProductDetailScreen({Key? key, required this.productId})
: super(key: key);
@override
_CustomerProductDetailScreenState createState() =>
_CustomerProductDetailScreenState();
}
class _CustomerProductDetailScreenState
extends State<CustomerProductDetailScreen> {
int _quantity = 1;
late ProductModel product;
ProductService service = ProductService();
void _fetchProduct() {
service.getProduct(widget.productId).then((p) => setState(() {
product = p;
}));
}
@override
void initState() {
super.initState();
_fetchProduct();
}
@override
Widget build(BuildContext context) {
final _product = product;
final maxQuantity = _product.quantity ?? 100;
return Scaffold(
appBar: Appbar(),
bottomNavigationBar: BotNavBar(role: UserType.CUSTOMER),
body: Container(
padding: EdgeInsets.only(top: 35),
child: SingleChildScrollView(
child: Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Column(
children: [
ProductCarousel(
productImages: _product.imagesUrls, height: 250),
SizedBox(height: 20),
Text(
_product.name,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(convertNumToReais(_product.price)),
Text(
'${_product.weight.toString().replaceAll('.', ',')} g')
],
),
SizedBox(height: 20),
Text(_product.description ?? ''),
SizedBox(height: 20),
Container(
alignment: Alignment.center,
width: 365,
child: Row(
children: [
QuantityChangerChip(
quantity: _quantity,
minQuantity: 1,
maxQuantity: maxQuantity,
onTapMinus: () {
setState(() {
_quantity = max(_quantity - 1, 1);
});
},
onTapPlus: () {
setState(() {
_quantity = min(_quantity + 1, maxQuantity);
});
},
),
SizedBox(width: 20),
ElevatedButton.icon(
onPressed: () {},
icon: Icon(Icons.shopping_cart_rounded),
label: Text('Adicionar ao carrinho'),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
),
)
],
),
)
],
),
),
),
),
),
);
}
}
import 'package:apus_drones_mobile/constants/UserType.dart';
import 'package:apus_drones_mobile/screens/PartnerOrdersScreen.dart';
import 'package:apus_drones_mobile/screens/clients/ListOfPartners.dart';
import 'package:apus_drones_mobile/widgets/BotNavBar.dart';
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
......
import 'dart:async';
import 'package:apus_drones_mobile/constants/OrderStatus.dart';
import 'package:apus_drones_mobile/constants/UserType.dart';
import 'package:apus_drones_mobile/models/PartnerOrderModel.dart';
import 'package:apus_drones_mobile/services/LocalNotification.dart';
......@@ -26,7 +27,7 @@ class _PartnerOrdersScreenState extends State<PartnerOrdersScreen> {
PartnerService service = new PartnerService();
_fetchOrders(String filter) {
service.getAllOrders(1, filter).then((orders) => this.setState(() {
service.getAllOrders(3, filter).then((orders) => this.setState(() {
for (int i = 0; i < orders.length; i++) {
orderList.insert(0, orders[i]);
}
......@@ -90,7 +91,7 @@ class _PartnerOrdersScreenState extends State<PartnerOrdersScreen> {
if (_start == 1) {
if (!modalOpen) {
for (int i = 0; i < orderList.length; i++) {
if (orderList[i].status == "WAITING_FOR_PARTNER") {
if (orderList[i].status == OrderStatus.WAITING_FOR_PARTNER) {
order = orderList[i];
show = true;
......
import 'package:apus_drones_mobile/constants/UserType.dart';
import 'package:apus_drones_mobile/models/PartnerModel.dart';
import 'package:apus_drones_mobile/models/ProductModel.dart';
import 'package:apus_drones_mobile/screens/CustomerProductDetailScreen.dart';
import 'package:apus_drones_mobile/services/PartnerService.dart';
import 'package:apus_drones_mobile/utils/money.dart';
import 'package:apus_drones_mobile/widgets/AddToCart.dart';
import 'package:apus_drones_mobile/widgets/BotNavBar.dart';
import 'package:flutter/material.dart';
import '../Appbar.dart';
import '../../widgets/Searchbar.dart';
import 'ListOfPartners.dart';
class ListOfProducts extends StatefulWidget {
final PartnerModel partner;
......@@ -85,9 +86,12 @@ class _ListOfProductsState extends State<ListOfProducts> {
list.add(Card(
elevation: 3,
child: InkWell(
onTap: () {
print("produto $i clicado!");
},
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomerProductDetailScreen(
productId: products[i].id)),
),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
......@@ -113,7 +117,7 @@ class _ListOfProductsState extends State<ListOfProducts> {
Padding(
padding: EdgeInsets.all(10),
child: Text(
'R\$' + products[i].price.toString(),
convertNumToReais(products[i].price),
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16),
),
......
import 'dart:async';
import 'dart:convert';
import 'package:apus_drones_mobile/assets/config.dart';
import 'package:apus_drones_mobile/models/ProductModel.dart';
import 'package:http/http.dart' as http;
class ProductService {
Future<ProductModel> getProduct(int id) async {
http.Response response =
await http.get(Uri.parse('${Config.apiURL}/product/$id'));
var rsBody = json.decode(response.body);
ProductModel product = ProductModel.fromJson(rsBody);
return product;
}
}
final convertReaisToNum = (String value) =>
num.tryParse(
value.replaceAll(RegExp(r'[^\d,]'), '').replaceAll(',', '.')) ??
0;
final convertNumToReais =
(num value) => 'R\$ ${value.toStringAsFixed(2).replaceAll('.', ',')}';
import 'dart:async';
import 'package:apus_drones_mobile/constants/OrderStatus.dart';
import 'package:apus_drones_mobile/models/PartnerOrderModel.dart';
import 'package:apus_drones_mobile/services/LocalNotification.dart';
import 'package:flutter/material.dart';
......@@ -41,7 +42,7 @@ class _PartnerOrderModalState extends State<PartnerOrderModal> {
void pressedButtonRecusar(BuildContext context) {
_timer.cancel();
widget.pom.status = "REFUSED";
widget.pom.status = OrderStatus.REFUSED;
if (!close) {
widget.refresh();
}
......@@ -51,7 +52,7 @@ class _PartnerOrderModalState extends State<PartnerOrderModal> {
void pressedButtonAceitar(BuildContext context) {
_timer.cancel();
widget.pom.status = "ACCEPTED";
widget.pom.status = OrderStatus.ACCEPTED;
widget.refresh();
widget.closed();
Navigator.pop(context);
......
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
class ProductCarousel extends StatefulWidget {
final List<String>? productImages;
final double height;
ProductCarousel({Key? key, required this.productImages, required this.height})
: super(key: key);
@override
_ProductCarouselState createState() => _ProductCarouselState();
}
class _ProductCarouselState extends State<ProductCarousel> {
int _current = 0;
final CarouselController _controller = CarouselController();
@override
Widget build(BuildContext context) {
final productImages = widget.productImages;
return Container(
child: productImages != null
? Column(
children: [
CarouselSlider.builder(
carouselController: _controller,
itemCount: productImages.length,
itemBuilder: (BuildContext context, int itemIndex,
int pageViewIndex) =>
Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.contain,
alignment: FractionalOffset.center,
image: NetworkImage(
productImages[itemIndex],
),
),
),
),
options: CarouselOptions(
height: widget.height,
autoPlay: true,
autoPlayInterval: Duration(seconds: 5),
autoPlayAnimationDuration: Duration(milliseconds: 500),
scrollDirection: Axis.horizontal,
onPageChanged: (index, reason) {
setState(() {
_current = index;
});
}),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: productImages.asMap().entries.map((entry) {
return GestureDetector(
onTap: () => _controller.animateToPage(entry.key),
child: Container(
width: 8.0,
height: 8.0,
margin: EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 4.0,
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color:
(Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black)
.withOpacity(
_current == entry.key ? 0.9 : 0.4)),
),
);
}).toList(),
),
],
)
: SizedBox(height: widget.height),
);
}
}
import 'package:flutter/material.dart';
class QuantityChangerChip extends StatelessWidget {
final int quantity;
final int minQuantity;
final int maxQuantity;
final VoidCallback onTapMinus;
final VoidCallback onTapPlus;
const QuantityChangerChip(
{Key? key,
required this.quantity,
this.minQuantity = 1,
required this.maxQuantity,
required this.onTapMinus,
required this.onTapPlus})
: super(key: key);
@override
Widget build(BuildContext context) {
final plusButtonEnabled = quantity < maxQuantity;
final minusButtonEnabled = quantity > minQuantity;
return Container(
width: 150,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.all(
Radius.circular(50),
),
),
padding: EdgeInsets.symmetric(vertical: 6, horizontal: 20),
child: Row(
children: [
InkWell(
onTap: minusButtonEnabled ? onTapMinus : null,
child: Container(
width: 20,
alignment: Alignment.center,
child: Text(
'-',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color:
minusButtonEnabled ? Colors.black : Colors.grey.shade400,
),
),
),
),
SizedBox(width: 16),
Expanded(
child: Container(
alignment: Alignment.center,
child: Text(
quantity.toString(),
style: TextStyle(
fontSize: 20,
// fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(width: 16),
InkWell(
onTap: plusButtonEnabled ? onTapPlus : null,
child: Container(
width: 20,
alignment: Alignment.center,
child: Text(
'+',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color:
plusButtonEnabled ? Colors.black : Colors.grey.shade400,
),
),
),
)
],
),
);
}
}
import 'package:apus_drones_mobile/constants/OrderStatus.dart';
import 'package:flutter/material.dart';
class StatusButton extends StatelessWidget {
final String status;
final OrderStatus status;
const StatusButton({Key? key, required this.status}) : super(key: key);
static const statusColors = {
'Entregue': Colors.green,
'Em preparo': Colors.yellow,
'Em entrega': Colors.blue,
};
@override
Widget build(BuildContext context) {
return Container(
width: 95,
width: 100,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: (statusColors[this.status] ?? Colors.grey)),
border: Border.all(color: status.color),
borderRadius: BorderRadius.circular(20),
color: Colors.grey.shade100),
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 10),
child: Text(
this.status,
status.label,
textAlign: TextAlign.center,
),
);
......
......@@ -92,6 +92,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "8.1.2"
carousel_slider:
dependency: "direct main"
description:
name: carousel_slider
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
characters:
dependency: transitive
description:
......
......@@ -33,6 +33,7 @@ dependencies:
modal_bottom_sheet: ^2.0.0
flutter_local_notifications: ^8.2.0
rxdart: ^0.27.2
carousel_slider: ^4.0.0
dev_dependencies:
build_runner: ^2.1.2
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment