Update: lib/screens/add_edit_product_screen.dart

This commit is contained in:
Hadi Mottale 2025-07-08 12:17:29 +03:30
parent 697ed887f1
commit c5ca9acdac
1 changed files with 288 additions and 73 deletions

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../models/product.dart'; import '../models/product.dart';
import '../services/database_helper.dart'; import '../services/database_helper.dart';
@ -18,6 +19,30 @@ class _AddEditProductScreenState extends State<AddEditProductScreen> {
late TextEditingController _quantityController; late TextEditingController _quantityController;
late TextEditingController _priceController; late TextEditingController _priceController;
late TextEditingController _descriptionController; late TextEditingController _descriptionController;
late TextEditingController _imageUrlController;
late TextEditingController _serialNumberController;
late TextEditingController _modelController;
late TextEditingController _tagsController;
late TextEditingController _costPriceController;
late TextEditingController _taxRateController;
late TextEditingController _discountController;
late TextEditingController _profitMarginController;
late TextEditingController _unitOfMeasureController;
late TextEditingController _locationController;
late TextEditingController _minimumStockController;
late TextEditingController _supplierController;
late TextEditingController _entryDateController;
late TextEditingController _manufactureDateController;
late TextEditingController _expirationDateController;
late TextEditingController _statusController;
late TextEditingController _categoryController;
late TextEditingController _brandController;
late DatabaseHelper _databaseHelper; late DatabaseHelper _databaseHelper;
bool get isEditing => widget.product != null; bool get isEditing => widget.product != null;
@ -26,17 +51,37 @@ class _AddEditProductScreenState extends State<AddEditProductScreen> {
void initState() { void initState() {
super.initState(); super.initState();
_databaseHelper = DatabaseHelper(); _databaseHelper = DatabaseHelper();
_nameController = TextEditingController(text: widget.product?.name); _nameController = TextEditingController(text: widget.product?.name);
_codeController = TextEditingController(text: widget.product?.code); _codeController = TextEditingController(text: widget.product?.code);
_quantityController = TextEditingController( _descriptionController = TextEditingController(text: widget.product?.description);
text: widget.product?.quantity.toString(), _imageUrlController = TextEditingController(text: widget.product?.imageUrl);
); _serialNumberController = TextEditingController(text: widget.product?.serialNumber);
_priceController = TextEditingController( _modelController = TextEditingController(text: widget.product?.model);
text: widget.product?.price.toString(), _tagsController = TextEditingController(text: widget.product?.tags?.join(', '));
);
_descriptionController = TextEditingController( _priceController = TextEditingController(text: widget.product?.price.toString());
text: widget.product?.description, _costPriceController = TextEditingController(text: widget.product?.costPrice?.toString());
); _taxRateController = TextEditingController(text: widget.product?.taxRate?.toString());
_discountController = TextEditingController(text: widget.product?.discount?.toString());
_profitMarginController = TextEditingController(text: widget.product?.profitMargin?.toString());
_quantityController = TextEditingController(text: widget.product?.quantity.toString());
_unitOfMeasureController = TextEditingController(text: widget.product?.unitOfMeasure);
_locationController = TextEditingController(text: widget.product?.location);
_minimumStockController = TextEditingController(text: widget.product?.minimumStock?.toString());
_supplierController = TextEditingController(text: widget.product?.supplier);
_entryDateController = TextEditingController(
text: widget.product?.entryDate != null ? DateFormat('yyyy-MM-dd').format(widget.product!.entryDate!) : '');
_manufactureDateController = TextEditingController(
text: widget.product?.manufactureDate != null ? DateFormat('yyyy-MM-dd').format(widget.product!.manufactureDate!) : '');
_expirationDateController = TextEditingController(
text: widget.product?.expirationDate != null ? DateFormat('yyyy-MM-dd').format(widget.product!.expirationDate!) : '');
_statusController = TextEditingController(text: widget.product?.status);
_categoryController = TextEditingController(text: widget.product?.category);
_brandController = TextEditingController(text: widget.product?.brand);
} }
@override @override
@ -46,9 +91,47 @@ class _AddEditProductScreenState extends State<AddEditProductScreen> {
_quantityController.dispose(); _quantityController.dispose();
_priceController.dispose(); _priceController.dispose();
_descriptionController.dispose(); _descriptionController.dispose();
_imageUrlController.dispose();
_serialNumberController.dispose();
_modelController.dispose();
_tagsController.dispose();
_costPriceController.dispose();
_taxRateController.dispose();
_discountController.dispose();
_profitMarginController.dispose();
_unitOfMeasureController.dispose();
_locationController.dispose();
_minimumStockController.dispose();
_supplierController.dispose();
_entryDateController.dispose();
_manufactureDateController.dispose();
_expirationDateController.dispose();
_statusController.dispose();
_categoryController.dispose();
_brandController.dispose();
super.dispose(); super.dispose();
} }
Future<void> _selectDate(TextEditingController controller) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2101),
);
if (picked != null) {
setState(() {
controller.text = DateFormat('yyyy-MM-dd').format(picked);
});
}
}
Future<void> _saveProduct() async { Future<void> _saveProduct() async {
if (_formKey.currentState!.validate()) { if (_formKey.currentState!.validate()) {
_formKey.currentState!.save(); _formKey.currentState!.save();
@ -57,12 +140,36 @@ class _AddEditProductScreenState extends State<AddEditProductScreen> {
id: isEditing ? widget.product!.id : null, id: isEditing ? widget.product!.id : null,
name: _nameController.text, name: _nameController.text,
code: _codeController.text, code: _codeController.text,
quantity: int.parse(_quantityController.text), description: _descriptionController.text.isEmpty ? null : _descriptionController.text,
imageUrl: _imageUrlController.text.isEmpty ? null : _imageUrlController.text,
serialNumber: _serialNumberController.text.isEmpty ? null : _serialNumberController.text,
model: _modelController.text.isEmpty ? null : _modelController.text,
tags: _tagsController.text.isEmpty ? null : _tagsController.text.split(',').map((e) => e.trim()).toList(),
price: double.parse(_priceController.text), price: double.parse(_priceController.text),
description: costPrice: double.tryParse(_costPriceController.text),
_descriptionController.text.isEmpty taxRate: double.tryParse(_taxRateController.text),
? null discount: double.tryParse(_discountController.text),
: _descriptionController.text, profitMargin: double.tryParse(_profitMarginController.text),
quantity: int.parse(_quantityController.text),
unitOfMeasure: _unitOfMeasureController.text.isEmpty ? null : _unitOfMeasureController.text,
location: _locationController.text.isEmpty ? null : _locationController.text,
minimumStock: int.tryParse(_minimumStockController.text),
supplier: _supplierController.text.isEmpty ? null : _supplierController.text,
entryDate: _entryDateController.text.isNotEmpty ? DateTime.parse(_entryDateController.text) : null,
manufactureDate: _manufactureDateController.text.isNotEmpty ? DateTime.parse(_manufactureDateController.text) : null,
expirationDate: _expirationDateController.text.isNotEmpty ? DateTime.parse(_expirationDateController.text) : null,
status: _statusController.text.isEmpty ? null : _statusController.text,
category: _categoryController.text.isEmpty ? null : _categoryController.text,
brand: _brandController.text.isEmpty ? null : _brandController.text,
createdBy: isEditing ? widget.product?.createdBy : null,
createdAt: isEditing ? widget.product?.createdAt : null,
lastModifiedBy: isEditing ? widget.product?.lastModifiedBy : null,
lastModifiedAt: isEditing ? widget.product?.lastModifiedAt : null,
); );
if (isEditing) { if (isEditing) {
@ -89,74 +196,182 @@ class _AddEditProductScreenState extends State<AddEditProductScreen> {
key: _formKey, key: _formKey,
child: ListView( child: ListView(
children: [ children: [
TextFormField( _buildSectionTitle('مشخصات شناسایی و ظاهری'),
_buildTextFormField(
controller: _nameController, controller: _nameController,
decoration: const InputDecoration(labelText: 'نام کالا'), labelText: 'نام کالا',
validator: (value) { validator: (value) => value!.isEmpty ? 'لطفا نام کالا را وارد کنید' : null),
if (value == null || value.isEmpty) { _buildTextFormField(
return 'لطفا نام کالا را وارد کنید';
}
return null;
},
),
const SizedBox(height: 10),
TextFormField(
controller: _codeController, controller: _codeController,
decoration: const InputDecoration(labelText: 'کد کالا'), labelText: 'کد کالا',
validator: (value) { validator: (value) => value!.isEmpty ? 'لطفا کد کالا را وارد کنید' : null),
if (value == null || value.isEmpty) { _buildTextFormField(
return 'لطفا کد کالا را وارد کنید';
}
return null;
},
),
const SizedBox(height: 10),
TextFormField(
controller: _quantityController,
decoration: const InputDecoration(labelText: 'تعداد'),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'لطفا تعداد را وارد کنید';
}
if (int.tryParse(value) == null) {
return 'تعداد باید عدد باشد';
}
return null;
},
),
const SizedBox(height: 10),
TextFormField(
controller: _priceController,
decoration: const InputDecoration(labelText: 'قیمت'),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'لطفا قیمت را وارد کنید';
}
if (double.tryParse(value) == null) {
return 'قیمت باید عدد باشد';
}
return null;
},
),
const SizedBox(height: 10),
TextFormField(
controller: _descriptionController, controller: _descriptionController,
decoration: const InputDecoration(
labelText: 'توضیحات (اختیاری)', labelText: 'توضیحات (اختیاری)',
), maxLines: 3),
maxLines: 3, _buildTextFormField(
), controller: _imageUrlController,
labelText: 'آدرس عکس کالا (URL/مسیر)',
keyboardType: TextInputType.url),
_buildTextFormField(
controller: _serialNumberController,
labelText: 'شماره سریال (اختیاری)'),
_buildTextFormField(
controller: _modelController,
labelText: 'شماره مدل (اختیاری)'),
_buildTextFormField(
controller: _tagsController,
labelText: 'هشتگ‌ها (با کاما جدا کنید)',
hintText: 'مثال: لوازم، برقی، آشپزخانه'),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildSectionTitle('اطلاعات مالی و تجاری'),
_buildTextFormField(
controller: _priceController,
labelText: 'قیمت فروش (تومان)',
keyboardType: TextInputType.number,
validator: (value) => value!.isEmpty || double.tryParse(value) == null ? 'لطفا قیمت معتبر وارد کنید' : null),
_buildTextFormField(
controller: _costPriceController,
labelText: 'قیمت خرید / بهای تمام شده (اختیاری)',
keyboardType: TextInputType.number),
_buildTextFormField(
controller: _taxRateController,
labelText: 'نرخ مالیات (مثلاً 0.09 برای 9%) (اختیاری)',
keyboardType: TextInputType.number),
_buildTextFormField(
controller: _discountController,
labelText: 'میزان تخفیف (درصد یا مبلغ) (اختیاری)',
keyboardType: TextInputType.number),
_buildTextFormField(
controller: _profitMarginController,
labelText: 'درصد سود مورد انتظار (اختیاری)',
keyboardType: TextInputType.number),
const SizedBox(height: 20),
_buildSectionTitle('موجودی و لجستیک'),
_buildTextFormField(
controller: _quantityController,
labelText: 'تعداد',
keyboardType: TextInputType.number,
validator: (value) => value!.isEmpty || int.tryParse(value) == null ? 'لطفا تعداد معتبر وارد کنید' : null),
_buildTextFormField(
controller: _unitOfMeasureController,
labelText: 'واحد اندازه‌گیری (مثلاً عدد، کیلوگرم) (اختیاری)'),
_buildTextFormField(
controller: _locationController,
labelText: 'مکان فیزیکی در انبار (اختیاری)'),
_buildTextFormField(
controller: _minimumStockController,
labelText: 'حداقل موجودی برای سفارش مجدد (اختیاری)',
keyboardType: TextInputType.number),
_buildTextFormField(
controller: _supplierController,
labelText: 'تامین‌کننده (اختیاری)'),
const SizedBox(height: 20),
_buildSectionTitle('تاریخ‌گذاری و وضعیت'),
_buildDateField(
controller: _entryDateController,
labelText: 'تاریخ ورود/ثبت (اختیاری)'),
_buildDateField(
controller: _manufactureDateController,
labelText: 'تاریخ تولید (اختیاری)'),
_buildDateField(
controller: _expirationDateController,
labelText: 'تاریخ انقضا (اختیاری)'),
_buildTextFormField(
controller: _statusController,
labelText: 'وضعیت کالا (مثلاً موجود، ناموجود) (اختیاری)'),
const SizedBox(height: 20),
_buildSectionTitle('دسته‌بندی و طبقه‌بندی'),
_buildTextFormField(
controller: _categoryController,
labelText: 'دسته بندی (مثلاً الکترونیک) (اختیاری)'),
_buildTextFormField(
controller: _brandController,
labelText: 'برند/شرکت سازنده (اختیاری)'),
const SizedBox(height: 20),
ElevatedButton( ElevatedButton(
onPressed: _saveProduct, onPressed: _saveProduct,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
textStyle: const TextStyle(fontSize: 18),
),
child: Text(isEditing ? 'ذخیره تغییرات' : 'افزودن کالا'), child: Text(isEditing ? 'ذخیره تغییرات' : 'افزودن کالا'),
), ),
const SizedBox(height: 20),
], ],
), ),
), ),
), ),
); );
} }
Widget _buildTextFormField({
required TextEditingController controller,
required String labelText,
String? Function(String?)? validator,
TextInputType keyboardType = TextInputType.text,
int maxLines = 1,
String? hintText,
bool readOnly = false,
}) {
return Column(
children: [
TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: labelText,
hintText: hintText,
border: const OutlineInputBorder(),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
),
keyboardType: keyboardType,
maxLines: maxLines,
validator: validator,
readOnly: readOnly,
),
const SizedBox(height: 10),
],
);
}
Widget _buildDateField({
required TextEditingController controller,
required String labelText,
}) {
return Column(
children: [
TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: labelText,
border: const OutlineInputBorder(),
suffixIcon: const Icon(Icons.calendar_today),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
),
readOnly: true,
onTap: () => _selectDate(controller),
),
const SizedBox(height: 10),
],
);
}
Widget _buildSectionTitle(String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 10, top: 10),
child: Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.blueGrey,
),
),
);
}
} }