From 8c47eb9519500bee595546d651ca75ef2892e25d Mon Sep 17 00:00:00 2001 From: Hadi Mottale Date: Fri, 4 Jul 2025 18:16:00 +0330 Subject: [PATCH] Refactor: Separate UI screens into --- lib/main.dart | 38 ++---- lib/screens/add_edit_product_screen.dart | 162 +++++++++++++++++++++++ lib/screens/product_list_screen.dart | 139 +++++++++++++++++++ 3 files changed, 312 insertions(+), 27 deletions(-) create mode 100644 lib/screens/add_edit_product_screen.dart create mode 100644 lib/screens/product_list_screen.dart diff --git a/lib/main.dart b/lib/main.dart index a08e53e..98d2fb7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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 createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - - title: Text(widget.title), - ), - body: Center(), + home: ProductListScreen(), ); } } diff --git a/lib/screens/add_edit_product_screen.dart b/lib/screens/add_edit_product_screen.dart new file mode 100644 index 0000000..c1ac78d --- /dev/null +++ b/lib/screens/add_edit_product_screen.dart @@ -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 createState() => _AddEditProductScreenState(); +} + +class _AddEditProductScreenState extends State { + final _formKey = GlobalKey(); + 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 _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 ? 'ذخیره تغییرات' : 'افزودن کالا'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/screens/product_list_screen.dart b/lib/screens/product_list_screen.dart new file mode 100644 index 0000000..142d4fb --- /dev/null +++ b/lib/screens/product_list_screen.dart @@ -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 createState() => _ProductListScreenState(); +} + +class _ProductListScreenState extends State { + late DatabaseHelper _databaseHelper; + List _products = []; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _databaseHelper = DatabaseHelper(); + _loadProducts(); + } + + Future _loadProducts() async { + setState(() { + _isLoading = true; + }); + final products = await _databaseHelper.getProducts(); + if (!mounted) return; + setState(() { + _products = products; + _isLoading = false; + }); + } + + Future _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), + ), + ); + } +}