Refactor: Separate UI screens into
This commit is contained in:
parent
dd623f1b04
commit
8c47eb9519
|
@ -1,6 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'screens/product_list_screen.dart';
|
||||
import 'screens/add_edit_product_screen.dart';
|
||||
|
||||
void main() {
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
sqfliteFfiInit();
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
|
@ -10,34 +16,12 @@ class MyApp extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
title: 'ابزار ساده ثبت کالا',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
primarySwatch: Colors.blueGrey,
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
),
|
||||
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
const MyHomePage({super.key, required this.title});
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Center(),
|
||||
home: ProductListScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import '../models/product.dart';
|
||||
import '../services/database_helper.dart';
|
||||
|
||||
class AddEditProductScreen extends StatefulWidget {
|
||||
final Product? product;
|
||||
|
||||
const AddEditProductScreen({super.key, this.product});
|
||||
|
||||
@override
|
||||
State<AddEditProductScreen> createState() => _AddEditProductScreenState();
|
||||
}
|
||||
|
||||
class _AddEditProductScreenState extends State<AddEditProductScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late TextEditingController _nameController;
|
||||
late TextEditingController _codeController;
|
||||
late TextEditingController _quantityController;
|
||||
late TextEditingController _priceController;
|
||||
late TextEditingController _descriptionController;
|
||||
late DatabaseHelper _databaseHelper;
|
||||
|
||||
bool get isEditing => widget.product != null;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_databaseHelper = DatabaseHelper();
|
||||
_nameController = TextEditingController(text: widget.product?.name);
|
||||
_codeController = TextEditingController(text: widget.product?.code);
|
||||
_quantityController = TextEditingController(
|
||||
text: widget.product?.quantity.toString(),
|
||||
);
|
||||
_priceController = TextEditingController(
|
||||
text: widget.product?.price.toString(),
|
||||
);
|
||||
_descriptionController = TextEditingController(
|
||||
text: widget.product?.description,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
_codeController.dispose();
|
||||
_quantityController.dispose();
|
||||
_priceController.dispose();
|
||||
_descriptionController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _saveProduct() async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_formKey.currentState!.save();
|
||||
|
||||
final product = Product(
|
||||
id: isEditing ? widget.product!.id : null,
|
||||
name: _nameController.text,
|
||||
code: _codeController.text,
|
||||
quantity: int.parse(_quantityController.text),
|
||||
price: double.parse(_priceController.text),
|
||||
description:
|
||||
_descriptionController.text.isEmpty
|
||||
? null
|
||||
: _descriptionController.text,
|
||||
);
|
||||
|
||||
if (isEditing) {
|
||||
await _databaseHelper.updateProduct(product);
|
||||
} else {
|
||||
await _databaseHelper.insertProduct(product);
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(isEditing ? 'ویرایش کالا' : 'افزودن کالا'),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _nameController,
|
||||
decoration: const InputDecoration(labelText: 'نام کالا'),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'لطفا نام کالا را وارد کنید';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
TextFormField(
|
||||
controller: _codeController,
|
||||
decoration: const InputDecoration(labelText: 'کد کالا'),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
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,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'توضیحات (اختیاری)',
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
onPressed: _saveProduct,
|
||||
child: Text(isEditing ? 'ذخیره تغییرات' : 'افزودن کالا'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import '../services/database_helper.dart';
|
||||
import '../models/product.dart';
|
||||
import 'add_edit_product_screen.dart';
|
||||
|
||||
class ProductListScreen extends StatefulWidget {
|
||||
const ProductListScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ProductListScreen> createState() => _ProductListScreenState();
|
||||
}
|
||||
|
||||
class _ProductListScreenState extends State<ProductListScreen> {
|
||||
late DatabaseHelper _databaseHelper;
|
||||
List<Product> _products = [];
|
||||
bool _isLoading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_databaseHelper = DatabaseHelper();
|
||||
_loadProducts();
|
||||
}
|
||||
|
||||
Future<void> _loadProducts() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
final products = await _databaseHelper.getProducts();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_products = products;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _deleteProduct(int id) async {
|
||||
await _databaseHelper.deleteProduct(id);
|
||||
_loadProducts();
|
||||
}
|
||||
|
||||
void _navigateToAddEditProduct([Product? product]) async {
|
||||
final result = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AddEditProductScreen(product: product),
|
||||
),
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
if (result == true) {
|
||||
_loadProducts();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('لیست کالاها'), centerTitle: true),
|
||||
body:
|
||||
_isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: _products.isEmpty
|
||||
? const Center(
|
||||
child: Text(
|
||||
'هیچ کالایی یافت نشد. برای شروع، یک کالا اضافه کنید.',
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: _products.length,
|
||||
itemBuilder: (context, index) {
|
||||
final product = _products[index];
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 5,
|
||||
),
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
child: Text(product.quantity.toString()),
|
||||
),
|
||||
title: Text(product.name),
|
||||
subtitle: Text(
|
||||
'کد: ${product.code} | قیمت: ${product.price} تومان',
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit, color: Colors.blue),
|
||||
onPressed: () => _navigateToAddEditProduct(product),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
title: const Text('حذف کالا'),
|
||||
content: Text(
|
||||
'آیا مطمئنید که میخواهید ${product.name} را حذف کنید؟',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed:
|
||||
() => Navigator.pop(context),
|
||||
child: const Text('خیر'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_deleteProduct(product.id!);
|
||||
if (!mounted)
|
||||
return; // Add this check
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('بله'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
_navigateToAddEditProduct(product);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _navigateToAddEditProduct(),
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue