Initial commit

This commit is contained in:
jahongireshonqulov
2025-10-17 19:42:02 +05:00
commit 9fbdabafb4
1420 changed files with 28021 additions and 0 deletions

View File

@@ -0,0 +1,472 @@
import 'package:dotted_line/dotted_line.dart';
import 'package:flutter/material.dart';
import 'package:grostore/app_lang.dart';
import 'package:grostore/configs/style_config.dart';
import 'package:grostore/configs/theme_config.dart';
import 'package:grostore/custom_ui/BoxDecorations.dart';
import 'package:grostore/custom_ui/Button.dart';
import 'package:grostore/custom_ui/common_appbar.dart';
import 'package:grostore/custom_ui/order_item.dart';
import 'package:grostore/helpers/device_info_helper.dart';
import 'package:grostore/presenters/order_details_presenter.dart';
import 'package:grostore/screens/order/pdf_api.dart';
import 'package:grostore/screens/order/pdf_invoice_api.dart';
import '../../helpers/common_functions.dart';
class OrderDetails extends StatefulWidget {
final code;
const OrderDetails({super.key, this.code});
@override
State<OrderDetails> createState() => _OrderDetailsState();
}
class _OrderDetailsState extends State<OrderDetails> {
OrderDetailsPresenter orderDetailsPresenter = OrderDetailsPresenter();
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
orderDetailsPresenter.initState(widget.code);
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ThemeConfig.xxlightGrey,
appBar: CommonAppbar.show(
title: AppLang.local(context).order_details, context: context),
body: ListenableBuilder(
listenable: orderDetailsPresenter,
builder: (context, child) {
return RefreshIndicator(
onRefresh: () {
return orderDetailsPresenter.onRefresh(widget.code);
},
child: SingleChildScrollView(
child: orderDetailsPresenter.isInitDetails
? Container(
padding: EdgeInsets.symmetric(
horizontal: StyleConfig.padding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 24),
Container(
width: getWidth(context),
decoration: BoxDecorations.shadow(radius: 8),
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Buyurtma raqami: ${orderDetailsPresenter.orderInfo?.code ?? ''}",
style: StyleConfig.fs14fwBold,
),
const SizedBox(
height: 14,
),
Text(
"Buyurtma sanasi: ${orderDetailsPresenter.orderInfo?.date ?? ''}",
style: StyleConfig.fs14fwBold,
),
],
),
),
const SizedBox(
height: 14,
),
Text(
"Billing Address",
style: StyleConfig.fs14fwBold,
),
const SizedBox(
height: 8,
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 8),
decoration: BoxDecorations.shadow(radius: 8),
child: buildBillingAddress(context)),
const SizedBox(
height: 14,
),
Text(
"Shipping Address",
style: StyleConfig.fs14fwBold,
),
const SizedBox(
height: 8,
),
Container(
decoration: BoxDecorations.shadow(radius: 8),
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 8),
child: buildShippingAddress(context)),
const SizedBox(
height: 14,
),
Text(
"Products",
style: StyleConfig.fs14fwBold,
),
const SizedBox(
height: 8,
),
Container(
//decoration: BoxDecorations.shadow(radius: 8),
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 8),
child: Column(
children: [
/*Row(
children: [
Container(
width: getWidth(context)*0.35,
child: Text("Product Name")),
Container(
width: getWidth(context)*0.18,
child: Text("Unit Price")),
Container(
width: getWidth(context)*0.1,
child: Text("QTY")),
Container(
width: getWidth(context)*0.18,
child: Text("Total Price")),
],
),
Divider(color: ThemeConfig.fontColor,),*/
GridView.builder(
//padding: EdgeInsets.only(left: StyleConfig.padding,right: StyleConfig.padding,bottom: 20),
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 8,
childAspectRatio: 0.62),
itemBuilder: (context, index) =>
OrderItem(
context: context,
onReq: (value) {
if (value) {
orderDetailsPresenter
.onRefresh(widget.code);
}
},
item: orderDetailsPresenter
.orderInfo!.items[index],
)
/*Container(
child: Row(
children: [
Container(
width: getWidth(context)*0.35,
child: Text(orderDetailsPresenter.orderInfo?.items[index].product?.name??"")),
Container(
width: getWidth(context)*0.18,
child: Text(showPrice(orderDetailsPresenter.orderInfo?.items[index].unitPrice??""))),
Container(
width: getWidth(context)*0.1,
child: Text("${orderDetailsPresenter.orderInfo?.items[index].qty??''}")),
Container(
width: getWidth(context)*0.18,
child: Text(showPrice(orderDetailsPresenter.orderInfo?.items[index].totalPrice??""))),
],
),
),
,
separatorBuilder: (context, index) => Column(
children: [
SizedBox(
height: 15,
),
Divider(color: ThemeConfig.fontColor,),
],
)*/
,
itemCount: orderDetailsPresenter
.orderInfo?.items.length ??
0),
],
),
),
const SizedBox(
height: 14,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Payment Method",
style: StyleConfig.fs14fwBold,
),
Text(
orderDetailsPresenter
.orderInfo?.payment_method
.toUpperCase() ??
"",
style: StyleConfig.fs14fwNormal,
),
],
),
const SizedBox(
height: 14,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Sub Total",
style: StyleConfig.fs14fwBold,
),
Text(
showPrice(orderDetailsPresenter
.orderInfo?.subTotalAmount ??
""),
style: StyleConfig.fs14fwNormal,
),
],
),
const SizedBox(
height: 14,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Tips",
style: StyleConfig.fs14fwBold,
),
Text(
showPrice(orderDetailsPresenter
.orderInfo?.totalTips ??
""),
style: StyleConfig.fs14fwNormal,
),
],
),
const SizedBox(
height: 14,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Shipping Cost",
style: StyleConfig.fs14fwBold,
),
Text(
showPrice(orderDetailsPresenter
.orderInfo?.totalShippingCost ??
""),
style: StyleConfig.fs14fwNormal,
),
],
),
const SizedBox(
height: 14,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Coupon Discount",
style: StyleConfig.fs14fwBold,
),
Text(
showPrice(orderDetailsPresenter
.orderInfo?.couponDiscountAmount ??
""),
style: StyleConfig.fs14fwNormal,
),
],
),
const SizedBox(
height: 14,
),
const DottedLine(),
const SizedBox(
height: 14,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Total Price",
style: StyleConfig.fs14fwBold,
),
Text(
showPrice(orderDetailsPresenter
.orderInfo?.totalPrice ??
""),
style: StyleConfig.fs14fwBold
.copyWith(color: ThemeConfig.red),
),
],
),
const SizedBox(
height: 24,
),
Button(
minHeight: 40.0,
shape: StyleConfig.buttonRadius(10),
minWidth: MediaQuery.sizeOf(context).width,
color: ThemeConfig.red,
child: Text(
AppLang.local(context).download_invoice,
style: StyleConfig.fs14cWhitefwBold,
),
onPressed: () async {
final pdf = await PdfInvoiceApi.generate(orderDetailsPresenter.orderInfo);
PdfApi.openFile(pdf);
},
),
const SizedBox(
height: 30,
)
],
),
)
: SizedBox(
height: getHeight(context),
child:
const Center(child: CircularProgressIndicator())),
),
);
}),
);
}
Column buildBillingAddress(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
orderDetailsPresenter.orderInfo?.billingAddress?.address ?? "",
style: StyleConfig.fs14fwNormal,
maxLines: 1,
),
Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Row(
children: [
Text(
"${AppLang.local(context).city}: ",
style: StyleConfig.fs14fwBold,
),
Text(
orderDetailsPresenter.orderInfo?.billingAddress?.cityName ?? "",
style: StyleConfig.fs14fwNormal,
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Row(
children: [
Text(
"${AppLang.local(context).state}: ",
style: StyleConfig.fs14fwBold,
),
Text(
orderDetailsPresenter.orderInfo?.billingAddress?.stateName ??
"",
style: StyleConfig.fs14fwNormal,
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Row(
children: [
Text(
"${AppLang.local(context).country}: ",
style: StyleConfig.fs14fwBold,
),
Text(
orderDetailsPresenter.orderInfo?.billingAddress?.countryName ??
"",
style: StyleConfig.fs14fwNormal,
),
],
),
),
],
);
}
Column buildShippingAddress(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
orderDetailsPresenter.orderInfo?.shippingAddress?.address ?? "",
style: StyleConfig.fs14fwNormal,
maxLines: 1,
),
Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Row(
children: [
Text(
"${AppLang.local(context).city}: ",
style: StyleConfig.fs14fwBold,
),
Text(
orderDetailsPresenter.orderInfo?.shippingAddress?.cityName ??
"",
style: StyleConfig.fs14fwNormal,
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Row(
children: [
Text(
"${AppLang.local(context).state}: ",
style: StyleConfig.fs14fwBold,
),
Text(
orderDetailsPresenter.orderInfo?.shippingAddress?.stateName ??
"",
style: StyleConfig.fs14fwNormal,
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Row(
children: [
Text(
"${AppLang.local(context).country}: ",
style: StyleConfig.fs14fwBold,
),
Text(
orderDetailsPresenter.orderInfo?.shippingAddress?.countryName ??
"",
style: StyleConfig.fs14fwNormal,
),
],
),
),
],
);
}
}

View File

@@ -0,0 +1,148 @@
import 'package:flutter/material.dart';
import 'package:grostore/app_lang.dart';
import 'package:grostore/configs/style_config.dart';
import 'package:grostore/configs/theme_config.dart';
import 'package:grostore/custom_ui/Button.dart';
import 'package:grostore/custom_ui/common_appbar.dart';
import 'package:grostore/custom_ui/order_view_model.dart';
import 'package:grostore/custom_ui/shimmers.dart';
import 'package:grostore/helpers/device_info_helper.dart';
import 'package:grostore/helpers/route.dart';
import 'package:grostore/presenters/order_presenter.dart';
import 'package:grostore/screens/main.dart';
import 'package:provider/provider.dart';
class Orders extends StatefulWidget {
bool fromBottomBar;
bool fromCheckOut;
Orders({Key? key, this.fromBottomBar = true,this.fromCheckOut=false}) : super(key: key);
@override
State<Orders> createState() => _OrdersState();
}
class _OrdersState extends State<Orders> {
OrderPresenter order = OrderPresenter();
@override
void initState() {
super.initState();
Provider.of<OrderPresenter>(context, listen: false).setContext(context);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
Provider.of<OrderPresenter>(context, listen: false).initState();
});
}
@override
Widget build(BuildContext context) {
return PopScope(
onPopInvoked: (a) async {
if(widget.fromCheckOut){
MakeRoute.goAndRemoveAll(context, const Main());
return Future(() => true);
}
return Future(() => true);
},
child: Scaffold(
backgroundColor: ThemeConfig.xxlightGrey,
appBar: CommonAppbar.show(
title: AppLang.local(context).orders,
context: context,
gotoMain: widget.fromCheckOut,
showBackButton: !widget.fromBottomBar),
body: Consumer<OrderPresenter>(builder: (context, data, child) {
return RefreshIndicator(
onRefresh: () => data.onRefresh(),
child: SingleChildScrollView(
child: Column(
children: [
const SizedBox(
height: 10,
),
buildTapbar(context, data),
if (!data.isOrdersInit)
SizedBox(
width: getWidth(context),
height: getHeight(context) - 100,
child: Shimmers.list(10, getWidth(context), 80),
)
else if (data.isOrdersInit && data.orders.isNotEmpty)
ListView.separated(
padding: EdgeInsets.only(
left: 10,
right: 10,
top: 10,
bottom: widget.fromBottomBar ? 80 : 10),
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: data.orders.length,
separatorBuilder: (context, index) {
return const SizedBox(
height: 10,
);
},
itemBuilder: (context, index) {
return Button(
//padding: EdgeInsets.symmetric(vertical: 10,horizontal: 20),
minWidth: 100,
onPressed: () {},
child: OrderViewModel(
orderInfo: data.orders[index],
context: context,
));
},
)
else
Container(
height: getHeight(context)-180,
alignment: Alignment.center,
child: Text(AppLang.local(context).data_is_not_available))
],
),
),
);
}),
),
);
}
SizedBox buildTapbar(BuildContext context, OrderPresenter data) {
return SizedBox(
// color: Colors.red,
width: getWidth(context),
height: 40,
child: ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 10),
scrollDirection: Axis.horizontal,
itemCount: data.searchKey.values.length,
separatorBuilder: (context, index) {
return const SizedBox(
width: 10,
);
},
itemBuilder: (context, index) {
return Button(
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 15),
color: data.keySelectedIndex == index
? ThemeConfig.red
: ThemeConfig.white,
minWidth: 80,
minHeight: 40.0,
shape: StyleConfig.buttonRadius(10),
onPressed: () {
data.onChangeIndex(index);
},
child: Text( data.searchKey.values.elementAt(index),
style: data.keySelectedIndex == index
? StyleConfig.fs14cWhitefwBold
: StyleConfig.fs14fwBold,
),
);
},
),
);
}
}

View File

@@ -0,0 +1,27 @@
import 'dart:io';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf/widgets.dart';
class PdfApi {
static Future<File> saveDocument({
required String name,
required Document pdf,
}) async {
final bytes = await pdf.save();
final dir = await getApplicationDocumentsDirectory();
final file = File('${dir.path}/$name');
await file.writeAsBytes(bytes);
return file;
}
static Future openFile(File file) async {
final url = file.path;
await OpenFile.open(url);
}
}

View File

@@ -0,0 +1,152 @@
import 'dart:io';
import 'package:flutter/material.dart' show Colors;
import 'package:flutter/services.dart';
import 'package:grostore/helpers/common_functions.dart';
import 'package:grostore/screens/order/pdf_api.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:pdf/widgets.dart';
import '../../models/order/order_details_response.dart';
class PdfInvoiceApi {
static Future<File> generate(OrderDetailsInfo? detailsInfo) async {
final img = await rootBundle.load(getAssetLogo("logo2x2.png"));
final imageBytes = img.buffer.asUint8List();
final imgQr = await rootBundle.load(getAssetLogo("img.png"));
final qr = imgQr.buffer.asUint8List();
final pdf = Document();
pdf.addPage(MultiPage(
build: (context) => [
buildLogo(imageBytes),
pw.SizedBox(height: 0.3 * PdfPageFormat.cm),
buildDottedDivider(),
pw.SizedBox(height: 0.5 * PdfPageFormat.cm),
buildAddress(
"${detailsInfo?.shippingAddress?.address}, ${detailsInfo?.shippingAddress?.cityName}, ${detailsInfo?.shippingAddress?.stateName}, ${detailsInfo?.shippingAddress?.countryName}"),
pw.SizedBox(height: 0.3 * PdfPageFormat.cm),
buildPhoneNumber("${detailsInfo?.payment_method}"),
pw.SizedBox(height: 0.5 * PdfPageFormat.cm),
buildDottedDivider(),
pw.SizedBox(height: 0.5 * PdfPageFormat.cm),
buildCheckDate("${detailsInfo?.code}", "${detailsInfo?.date}"),
pw.SizedBox(height: 0.3 * PdfPageFormat.cm),
buildDottedDivider(),
pw.SizedBox(height: 0.5 * PdfPageFormat.cm),
buildItemsList(detailsInfo?.items ?? []),
pw.SizedBox(height: 0.7 * PdfPageFormat.cm),
buildDottedDivider(),
pw.SizedBox(height: 0.5 * PdfPageFormat.cm),
buildSummery(detailsInfo),
buildDottedDivider(),
pw.SizedBox(height: 1.2 * PdfPageFormat.cm),
buildQrCode(qr)
],
//footer: (context) => buildFooter(invoice),
));
return PdfApi.saveDocument(name: 'Check.pdf', pdf: pdf);
}
static Widget buildLogo(Uint8List image) {
return Row(mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, children: [
Text("Karvon market",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
)),
pw.Image(pw.MemoryImage(image), height: 40)
]);
}
static Widget buildDottedDivider() {
return Row(
children: List.generate(
800 ~/ 10,
(index) => index % 2 == 0
? Expanded(
child: pw.Container(height: 1, color: PdfColor(0, 0, 0)),
)
: Expanded(
child: Container(height: 2, color: null),
)));
}
static Widget buildAddress(String address) {
return pw.Text(address, style: TextStyle(fontSize: 14));
}
static Widget buildPhoneNumber(String phoneNumber) {
return pw.Text("To'lov turi: $phoneNumber", style: TextStyle(fontSize: 14));
}
static Widget buildCheckDate(String checkNumber, String date) {
return Column(
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
Text("Check raqami: $checkNumber", style: TextStyle(fontSize: 14)),
pw.SizedBox(height: 0.2 * PdfPageFormat.cm),
Text("Chiqarilgan: $date", style: TextStyle(fontSize: 14))
]);
}
static Widget buildItemsList(List<Item> items) {
return Column(
mainAxisSize: pw.MainAxisSize.min,
mainAxisAlignment: pw.MainAxisAlignment.start,
children: List.generate(
items.length,
(index) => Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
Text("${index + 1}.${items[index].product?.name}",
style: TextStyle(
fontSize: 16, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 0.1 * PdfPageFormat.cm),
Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceAround,
children: [
Text("Narxi:",
style: TextStyle(
fontSize: 16, fontWeight: pw.FontWeight.normal)),
Text(items[index].unitPrice,
style: TextStyle(
fontSize: 15, fontWeight: pw.FontWeight.normal)),
Text("${items[index].qty} ta",
style: TextStyle(
fontSize: 15, fontWeight: pw.FontWeight.normal)),
Text(items[index].totalPrice,
style: TextStyle(
fontSize: 17, fontWeight: pw.FontWeight.bold)),
])
]),
));
}
static Widget buildSummery(OrderDetailsInfo? info) {
return Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
mainAxisAlignment: pw.MainAxisAlignment.start,
children: [
Text("Mahsulot narxi: ${info?.subTotalAmount}",
style: TextStyle(fontSize: 15)),
pw.SizedBox(height: 0.2 * PdfPageFormat.cm),
Text("Yuk tashish narxi:${info?.totalShippingCost}",
style: TextStyle(fontSize: 15)),
pw.SizedBox(height: 0.2 * PdfPageFormat.cm),
Text("Chegirmasi: ${info?.couponDiscountAmount}",
style: TextStyle(fontSize: 15)),
pw.SizedBox(height: 0.2 * PdfPageFormat.cm),
Text("Jami narxi: ${info?.totalPrice}",
style: TextStyle(fontSize: 15, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 0.5 * PdfPageFormat.cm),
]);
}
static Widget buildQrCode(Uint8List qrcode) {
return pw.Center(child: pw.Image(pw.MemoryImage(qrcode), height: 200));
}
}