sc-android-app/lib/screens/Add_products_screen.dart
2024-08-16 17:54:32 +05:30

368 lines
17 KiB
Dart

import 'package:cheminova/widgets/common_app_bar.dart';
import 'package:cheminova/widgets/common_background.dart';
import 'package:cheminova/widgets/common_drawer.dart';
import 'package:cheminova/widgets/common_elevated_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../provider/products_provider.dart';
class AddProductsScreen extends StatefulWidget {
final String distributorType;
final String tradeName;
final String pdRdId;
const AddProductsScreen({
super.key,
required this.distributorType,
required this.tradeName,
required this.pdRdId
});
@override
State<AddProductsScreen> createState() => _AddProductsScreenState();
}
class _AddProductsScreenState extends State<AddProductsScreen> {
final searchController = TextEditingController();
late ProductProvider productProvider;
@override
void initState() {
productProvider = Provider.of<ProductProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
productProvider.getProducts();
});
super.initState();
}
@override
void dispose() {
if (mounted) {
productProvider.resetProducts();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return CommonBackground(
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: CommonAppBar(
actions: [
IconButton(
onPressed: () => Navigator.pop(context),
icon: Image.asset('assets/Back_attendance.png'),
padding: const EdgeInsets.only(right: 20)
)
],
title: Text(
'${widget.distributorType}\n${widget.tradeName}',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek'
)
),
backgroundColor: Colors.transparent,
elevation: 0
),
drawer: const CommonDrawer(),
bottomNavigationBar: Consumer<ProductProvider>(
builder: (context, value, child) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: value.selectedProducts.isEmpty
? Alignment.center
: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton.extended(
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.9
),
context: context,
builder: (BuildContext context) {
return Consumer<ProductProvider>(
builder: (context, value, child) => StatefulBuilder(
builder: (context, setState) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(18.0),
child: TextField(
controller: searchController,
decoration: const InputDecoration(
labelText: 'Search by name or SKU',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search)
),
onChanged: (val) {
value.filterProducts(val);
setState(() {});
}
)
),
Expanded(
child: ListView.builder(
itemCount: searchController.text.isEmpty
? value.productList.length
: value.searchList.length,
itemBuilder: (context, index) {
bool isAlreadySelected = value.selectedProducts.any(
(selectedProduct) => selectedProduct.sku == value.productList[index].sku
);
final data = searchController.text.isEmpty
? value.productList[index]
: value.searchList[index];
return Card(
child: ListTile(
title: Text(
data.productName,
style: TextStyle(color: isAlreadySelected ? Colors.grey : Colors.black)
),
subtitle: Text(
data.sku,
style: TextStyle(color: isAlreadySelected ? Colors.grey : Colors.black)
),
onTap: isAlreadySelected
? null
: () {
setState(() => value.selectedProducts.add(data));
Navigator.pop(context);
}
)
);
}
)
)
]
);
}
)
);
},
).whenComplete(() => setState(() {}));
},
backgroundColor: Colors.white,
icon: const Icon(Icons.add, color: Colors.black),
label: const Text('Add Products', style: TextStyle(color: Colors.black))
),
if (value.selectedProducts.isNotEmpty) ...[
const SizedBox(height: 16.0),
Consumer<ProductProvider>(
builder: (context, value, child) => CommonElevatedButton(
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: 'SUBMIT',
backgroundColor: const Color(0xff004791),
onPressed: () {
if (value.selectedProducts.isNotEmpty &&
value.selectedProducts.every((product) =>
product.sku.isNotEmpty &&
product.productName.isNotEmpty &&
product.sale != null &&
product.inventory != null)) {
value.submitProducts(
distributorType: widget.distributorType,
pdRdId: widget.pdRdId
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please fill out all product details, including sale and inventory.')
),
);
}
}
)
)
]
]
)
)
),
],
),
),
body: Consumer<ProductProvider>(
builder: (context, value, child) {
return Stack(
children: [
Column(
children: [
if (value.selectedProducts.isNotEmpty)
Expanded(
child: ListView.builder(
itemCount: value.selectedProducts.length,
itemBuilder: (context, index) {
return ProductBlock(
onUpdate: (updatedProduct) {
debugPrint('selected productt le ${value.selectedProducts.length}');
setState(() {
value.selectedProducts[index] = updatedProduct;
});
},
onRemove: () {
setState(() {
value.selectedProducts.removeAt(index);
});
},
product: value.selectedProducts[index]
);
}
)
)
]
),
(value.isLoading)
? Container(
color: Colors.black12,
child: const Center(child: CircularProgressIndicator())
)
: const SizedBox()
]
);
}
)
)
);
}
}
class ProductBlock extends StatefulWidget {
final ProductModel product;
final ValueChanged<ProductModel> onUpdate;
final VoidCallback onRemove;
const ProductBlock({
super.key,
required this.product,
required this.onUpdate,
required this.onRemove,
});
@override
State<ProductBlock> createState() => _ProductBlockState();
}
class _ProductBlockState extends State<ProductBlock> {
final saleController = TextEditingController();
final inventoryController = TextEditingController();
String? errorMessage;
@override
void initState() {
super.initState();
saleController.text = (widget.product.sale ?? '').toString();
inventoryController.text = (widget.product.inventory ?? '').toString();
}
void validateInput() {
setState(() {
String? saleError;
String? inventoryError;
if (saleController.text.isEmpty) {
saleError = 'Sale cannot be empty.';
}
if (inventoryController.text.isEmpty) {
inventoryError = 'Inventory cannot be empty.';
}
errorMessage = null;
if (saleError == null && inventoryError == null) {
int sale = int.parse(saleController.text);
int inventory = int.parse(inventoryController.text);
widget.onUpdate(ProductModel(
sku: widget.product.sku,
productName: widget.product.productName,
sale: sale,
inventory: inventory,
));
} else {
errorMessage = saleError ?? inventoryError;
}
});
}
@override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
margin: const EdgeInsets.all(8),
child: Stack(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Product: ${widget.product.productName}',
style: const TextStyle(fontSize: 16)),
Text('SKU: ${widget.product.sku}',
style: const TextStyle(fontSize: 15)),
const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: saleController,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
labelText: 'Sale',
errorText: saleController.text.isEmpty
? 'Sale cannot be empty.'
: null
),
keyboardType: TextInputType.number,
enabled: true,
onChanged: (_) => validateInput()
),
TextField(
controller: inventoryController,
onTapOutside: (event) => FocusScope.of(context).unfocus(),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: InputDecoration(
labelText: 'Inventory',
errorText: inventoryController.text.isEmpty
? 'Inventory cannot be empty.'
: null
),
keyboardType: TextInputType.number,
enabled: true,
onChanged: (_) => validateInput()
)
]
)
]
),
),
Positioned(
top: 4,
right: 4,
child: IconButton(
icon: const Icon(Icons.delete_outline, color: Colors.red),
onPressed: widget.onRemove,
),
),
],
)
);
}
}