🙂 İNSANLARIN EN HAYIRLISI INSANLARA FAYDALI OLANDIR 🙂

Ramazan HABER / FLUTTER / Lazerli barkod okuyucudan barkod okuma ve kameradan

1-) FLUTTER - Lazerli barkod okuyucudan barkod okuma ve kameradan

 

TELEFON MARKA :

Movfast Ranger 2 Endüstriyel Android El Terminali - 4G / LTE ÇİFT SİM KART DESTEĞİ

açıklama : hepsine olur ama püf nokta : telefon -> telefondaki barkod uygulaması yoksa ayarlar -> function settings -> barcode data output mode -> output to broadcast/keyboard seçili olması lazım. muhtemelen biz keybord a basmış gibi kabul ediyoruz.

 

1.ADIM : pubspec.yaml

 

qr_code_scanner_plus: ^2.0.13

 

 

2.ADIM : barcode_inventory_screen.dart

 

import 'dart:async';

import 'dart:developer';

import 'dart:io';

import 'package:flutter/material.dart';

import 'package:flutter/cupertino.dart';

import 'package:flutter/services.dart';

import 'package:intl/intl.dart';

import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart';

import '../models/inventory_item.dart';

 

class BarcodeInventoryScreen extends StatefulWidget {

  const BarcodeInventoryScreen({super.key});

 

  @override

  State<BarcodeInventoryScreen> createState() => _BarcodeInventoryScreenState();

}

 

class _BarcodeInventoryScreenState extends State<BarcodeInventoryScreen> {

  DateTime _selectedDate = DateTime.now();

  String _selectedDepartment = 'Ana Depo';

  String _selectedType = 'stok'; // 'stok' or 'reçete'

  List<InventoryItem> _inventoryItems = [];

  bool _isLoading = false;

  final Map<String, int> _countedItems = {};

  final TextEditingController _manualBarcodeController =

      TextEditingController();

  final TextEditingController _quantityController = TextEditingController();

  final FocusNode _barcodeFocusNode = FocusNode(

    debugLabel: 'BarcodeInput',

    skipTraversal: true,

  );

 

  final List<String> _departments = ['Ana Depo', 'Yemek Sepeti'];

 

  @override

  void initState() {

    super.initState();

    _quantityController.text = '1';

 

    // Klavye açılmasını engelle

    _barcodeFocusNode.addListener(() {

      if (_barcodeFocusNode.hasFocus) {

        // Klavye açılmaya çalışırsa kapat

        SystemChannels.textInput.invokeMethod('TextInput.hide');

      }

    });

 

    // Lazer okuyucu için odaklanma

    WidgetsBinding.instance.addPostFrameCallback((_) {

      _barcodeFocusNode.requestFocus();

    });

  }

 

  @override

  void dispose() {

    _manualBarcodeController.dispose();

    _quantityController.dispose();

    _barcodeFocusNode.dispose();

    super.dispose();

  }

 

  void _loadDemoData() {

    // Demo data - sadece barkod örnekleri

    _inventoryItems = [

      InventoryItem(

        id: '1',

        barcode: '1213123',

        name: '',

        unit: 'Adet',

        quantity: 0,

        averagePrice: 0,

        totalAmount: 0,

        date: _selectedDate,

        department: _selectedDepartment,

      ),

      InventoryItem(

        id: '2',

        barcode: '1213123',

        name: '',

        unit: 'Adet',

        quantity: 0,

        averagePrice: 0,

        totalAmount: 0,

        date: _selectedDate,

        department: _selectedDepartment,

      ),

      InventoryItem(

        id: '3',

        barcode: '1213123',

        name: '',

        unit: 'Adet',

        quantity: 0,

        averagePrice: 0,

        totalAmount: 0,

        date: _selectedDate,

        department: _selectedDepartment,

      ),

      InventoryItem(

        id: '4',

        barcode: '1213123',

        name: '',

        unit: 'Adet',

        quantity: 0,

        averagePrice: 0,

        totalAmount: 0,

        date: _selectedDate,

        department: _selectedDepartment,

      ),

      InventoryItem(

        id: '5',

        barcode: '1213123',

        name: '',

        unit: 'Adet',

        quantity: 0,

        averagePrice: 0,

        totalAmount: 0,

        date: _selectedDate,

        department: _selectedDepartment,

      ),

    ];

  }

 

  void _selectDate() {

    showCupertinoModalPopup<void>(

      context: context,

      builder: (BuildContext context) => Container(

        height: 216,

        padding: const EdgeInsets.only(top: 6.0),

        margin: EdgeInsets.only(

          bottom: MediaQuery.of(context).viewInsets.bottom,

        ),

        color: CupertinoColors.systemBackground.resolveFrom(context),

        child: SafeArea(

          top: false,

          child: CupertinoDatePicker(

            initialDateTime: _selectedDate,

            mode: CupertinoDatePickerMode.date,

            use24hFormat: true,

            showDayOfWeek: true,

            onDateTimeChanged: (DateTime newDate) {

              setState(() {

                _selectedDate = newDate;

              });

            },

          ),

        ),

      ),

    );

  }

 

  void _selectDepartment() {

    showModalBottomSheet(

      context: context,

      builder: (context) => Container(

        padding: const EdgeInsets.all(16),

        child: Column(

          mainAxisSize: MainAxisSize.min,

          children: [

            Text(

              'Departman Seçin',

              style: Theme.of(context).textTheme.titleLarge,

            ),

            const SizedBox(height: 16),

            ..._departments.map(

              (dept) => ListTile(

                title: Text(dept),

                leading: Radio<String>(

                  value: dept,

                  groupValue: _selectedDepartment,

                  onChanged: (value) {

                    setState(() {

                      _selectedDepartment = value!;

                    });

                    Navigator.pop(context);

                  },

                ),

              ),

            ),

          ],

        ),

      ),

    );

  }

 

  void _showTypeSelection() {

    showModalBottomSheet(

      context: context,

      builder: (context) => Container(

        padding: const EdgeInsets.all(16),

        child: Column(

          mainAxisSize: MainAxisSize.min,

          children: [

            Text('Tür Seçin', style: Theme.of(context).textTheme.titleLarge),

            const SizedBox(height: 16),

            RadioListTile<String>(

              title: const Text('Stok'),

              value: 'stok',

              groupValue: _selectedType,

              onChanged: (value) {

                setState(() {

                  _selectedType = value!;

                });

                Navigator.pop(context);

              },

            ),

            RadioListTile<String>(

              title: const Text('Reçete'),

              value: 'reçete',

              groupValue: _selectedType,

              onChanged: (value) {

                setState(() {

                  _selectedType = value!;

                });

                Navigator.pop(context);

              },

            ),

          ],

        ),

      ),

    );

  }

 

  void _loadInventory() {

    setState(() {

      _isLoading = true;

    });

 

    // Simulate loading

    Future.delayed(const Duration(seconds: 1), () {

      setState(() {

        _isLoading = false;

        _loadDemoData();

      });

    });

  }

 

  void _processBarcode(String barcode) {

    // Barkod var mı kontrol et

    final existingItemIndex = _inventoryItems.indexWhere(

      (item) => item.barcode == barcode,

    );

 

    if (existingItemIndex != -1) {

      // Mevcut ürün - miktarını artır

      setState(() {

        _countedItems[barcode] = (_countedItems[barcode] ?? 0) + 1;

        // Ürünü en üste taşı

        _moveItemToTop(barcode);

      });

    } else {

      // Yeni ürün - listeye ekle

      final newItem = InventoryItem(

        id: DateTime.now().millisecondsSinceEpoch.toString(),

        barcode: barcode,

        name: '',

        unit: 'Adet',

        quantity: 0,

        averagePrice: 0,

        totalAmount: 0,

        date: _selectedDate,

        department: _selectedDepartment,

      );

 

      setState(() {

        _inventoryItems.insert(0, newItem);

        _countedItems[barcode] = 1;

      });

    }

 

    // Odaklanmayı koru

    _barcodeFocusNode.requestFocus();

  }

 

  void _moveItemToTop(String barcode) {

    // Barkod ile eşleşen ürünü bul ve en üste taşı

    final itemIndex = _inventoryItems.indexWhere(

      (item) => item.barcode == barcode,

    );

    if (itemIndex != -1) {

      final item = _inventoryItems.removeAt(itemIndex);

      _inventoryItems.insert(0, item);

      setState(() {});

    }

  }

 

  Future<void> _scanBarcode() async {

    try {

      final String? result = await Navigator.push(

        context,

        MaterialPageRoute(builder: (context) => const QRScannerPage()),

      );

 

      if (result != null && result.isNotEmpty) {

        _processBarcode(result);

      }

    } catch (e) {

      // Hata durumunda kullanıcıya bilgi ver

      log('Kamera okuma hatası: $e');

      if (mounted) {

        ScaffoldMessenger.of(context).showSnackBar(

          SnackBar(

            content: Text('Kamera hatası: ${e.toString()}'),

            backgroundColor: Colors.red,

          ),

        );

      }

    }

  }

 

  void _showProductQuantityDialog(String barcode, String itemName) {

    // Mevcut miktarı al

    final currentQuantity = _countedItems[barcode] ?? 0;

    final TextEditingController addQuantityController = TextEditingController();

    final FocusNode addQuantityFocusNode = FocusNode();

 

    showDialog(

      context: context,

      builder: (context) => StatefulBuilder(

        builder: (context, setDialogState) {

          final addQuantity = int.tryParse(addQuantityController.text) ?? 0;

          final newTotal = currentQuantity + addQuantity;

 

          return AlertDialog(

            title: Text('Miktar Ekle - Barkod: $barcode'),

            content: Column(

              mainAxisSize: MainAxisSize.min,

              children: [

                // Mevcut miktar

                Container(

                  width: double.infinity,

                  padding: const EdgeInsets.all(12),

                  decoration: BoxDecoration(

                    color: Colors.blue.shade50,

                    borderRadius: BorderRadius.circular(8),

                    border: Border.all(color: Colors.blue.shade200),

                  ),

                  child: Text(

                    'Mevcut Miktar: $currentQuantity',

                    style: const TextStyle(

                      fontSize: 16,

                      fontWeight: FontWeight.bold,

                    ),

                    textAlign: TextAlign.center,

                  ),

                ),

                const SizedBox(height: 16),

                // Eklenecek miktar

                TextField(

                  controller: addQuantityController,

                  focusNode: addQuantityFocusNode,

                  readOnly: true,

                  showCursor: true,

                  enableInteractiveSelection: true,

                  style: const TextStyle(

                    fontSize: 18,

                    fontWeight: FontWeight.bold,

                  ),

                  textAlign: TextAlign.center,

                  decoration: const InputDecoration(

                    labelText: 'Eklenecek Miktar',

                    hintText: 'Miktar girin',

                    border: OutlineInputBorder(),

                    contentPadding: EdgeInsets.symmetric(

                      horizontal: 16,

                      vertical: 12,

                    ),

                  ),

                ),

                const SizedBox(height: 16),

                // Yeni toplam

                Container(

                  width: double.infinity,

                  padding: const EdgeInsets.all(12),

                  decoration: BoxDecoration(

                    color: Colors.green.shade50,

                    borderRadius: BorderRadius.circular(8),

                    border: Border.all(color: Colors.green.shade200),

                  ),

                  child: Text(

                    'Yeni Toplam: $newTotal',

                    style: const TextStyle(

                      fontSize: 16,

                      fontWeight: FontWeight.bold,

                      color: Colors.green,

                    ),

                    textAlign: TextAlign.center,

                  ),

                ),

                const SizedBox(height: 16),

                // Özel sayısal klavye - Sadece miktar girişi

                _buildProductQuantityKeyboard(

                  addQuantityController,

                  setDialogState,

                ),

              ],

            ),

            actions: [

              TextButton(

                onPressed: () => Navigator.pop(context),

                child: const Text('İptal'),

              ),

              ElevatedButton(

                onPressed: () {

                  final addQuantity =

                      int.tryParse(addQuantityController.text) ?? 0;

                  if (addQuantity > 0) {

                    setState(() {

                      _countedItems[barcode] = currentQuantity + addQuantity;

                      // Ürünü en üste taşı

                      _moveItemToTop(barcode);

                    });

                    Navigator.pop(context);

                    // Odaklanmayı koru

                    _barcodeFocusNode.requestFocus();

                  }

                },

                child: const Text('Kaydet'),

              ),

            ],

          );

        },

      ),

    );

  }

 

  Widget _buildUnifiedKeyboard(

    TextEditingController barcodeController,

    TextEditingController quantityController,

    bool isBarcodeFocused,

    StateSetter setDialogState,

  ) {

    return Container(

      width: 240,

      child: Column(

        children: [

          // İlk satır: 1, 2, 3

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: [

              _buildUnifiedNumberButton(

                '1',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

              _buildUnifiedNumberButton(

                '2',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

              _buildUnifiedNumberButton(

                '3',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

            ],

          ),

          const SizedBox(height: 8),

          // İkinci satır: 4, 5, 6

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: [

              _buildUnifiedNumberButton(

                '4',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

              _buildUnifiedNumberButton(

                '5',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

              _buildUnifiedNumberButton(

                '6',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

            ],

          ),

          const SizedBox(height: 8),

          // Üçüncü satır: 7, 8, 9

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: [

              _buildUnifiedNumberButton(

                '7',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

              _buildUnifiedNumberButton(

                '8',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

              _buildUnifiedNumberButton(

                '9',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

            ],

          ),

          const SizedBox(height: 8),

          // Dördüncü satır: Temizle, 0, Sil

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: [

              _buildActionButton('C', () {

                if (isBarcodeFocused) {

                  barcodeController.text = '';

                  barcodeController.selection = TextSelection.fromPosition(

                    TextPosition(offset: barcodeController.text.length),

                  );

                } else {

                  quantityController.text = '';

                  quantityController.selection = TextSelection.fromPosition(

                    TextPosition(offset: quantityController.text.length),

                  );

                }

                setDialogState(() {});

              }),

              _buildUnifiedNumberButton(

                '0',

                barcodeController,

                quantityController,

                isBarcodeFocused,

                setDialogState,

              ),

              _buildActionButton('', () {

                if (isBarcodeFocused) {

                  if (barcodeController.text.isNotEmpty) {

                    barcodeController.text = barcodeController.text.substring(

                      0,

                      barcodeController.text.length - 1,

                    );

                  }

                  barcodeController.selection = TextSelection.fromPosition(

                    TextPosition(offset: barcodeController.text.length),

                  );

                } else {

                  if (quantityController.text.isNotEmpty) {

                    quantityController.text = quantityController.text.substring(

                      0,

                      quantityController.text.length - 1,

                    );

                  }

                  quantityController.selection = TextSelection.fromPosition(

                    TextPosition(offset: quantityController.text.length),

                  );

                }

                setDialogState(() {});

              }),

            ],

          ),

        ],

      ),

    );

  }

 

  Widget _buildUnifiedNumberButton(

    String number,

    TextEditingController barcodeController,

    TextEditingController quantityController,

    bool isBarcodeFocused,

    StateSetter setDialogState,

  ) {

    return SizedBox(

      width: 60,

      height: 50,

      child: ElevatedButton(

        onPressed: () {

          if (isBarcodeFocused) {

            // Barkod girişi

            barcodeController.text += number;

            barcodeController.selection = TextSelection.fromPosition(

              TextPosition(offset: barcodeController.text.length),

            );

          } else {

            // Miktar girişi - direkt ekleme

            quantityController.text += number;

            quantityController.selection = TextSelection.fromPosition(

              TextPosition(offset: quantityController.text.length),

            );

          }

          setDialogState(() {});

        },

        style: ElevatedButton.styleFrom(

          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),

        ),

        child: Text(

          number,

          style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),

        ),

      ),

    );

  }

 

  Widget _buildProductQuantityKeyboard(

    TextEditingController quantityController,

    StateSetter setDialogState,

  ) {

    return Container(

      width: 240,

      child: Column(

        children: [

          // İlk satır: 1, 2, 3

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: [

              _buildProductNumberButton(

                '1',

                quantityController,

                setDialogState,

              ),

              _buildProductNumberButton(

                '2',

                quantityController,

                setDialogState,

              ),

              _buildProductNumberButton(

                '3',

                quantityController,

                setDialogState,

              ),

            ],

          ),

          const SizedBox(height: 8),

          // İkinci satır: 4, 5, 6

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: [

              _buildProductNumberButton(

                '4',

                quantityController,

                setDialogState,

              ),

              _buildProductNumberButton(

                '5',

                quantityController,

                setDialogState,

              ),

              _buildProductNumberButton(

                '6',

                quantityController,

                setDialogState,

              ),

            ],

          ),

          const SizedBox(height: 8),

          // Üçüncü satır: 7, 8, 9

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: [

              _buildProductNumberButton(

                '7',

                quantityController,

                setDialogState,

              ),

              _buildProductNumberButton(

                '8',

                quantityController,

                setDialogState,

              ),

              _buildProductNumberButton(

                '9',

                quantityController,

                setDialogState,

              ),

            ],

          ),

          const SizedBox(height: 8),

          // Dördüncü satır: Temizle, 0, Sil

          Row(

            mainAxisAlignment: MainAxisAlignment.spaceEvenly,

            children: [

              _buildActionButton('C', () {

                quantityController.text = '';

                quantityController.selection = TextSelection.fromPosition(

                  TextPosition(offset: quantityController.text.length),

                );

                setDialogState(() {});

              }),

              _buildProductNumberButton(

                '0',

                quantityController,

                setDialogState,

              ),

              _buildActionButton('', () {

                if (quantityController.text.isNotEmpty) {

                  quantityController.text = quantityController.text.substring(

                    0,

                    quantityController.text.length - 1,

                  );

                }

                quantityController.selection = TextSelection.fromPosition(

                  TextPosition(offset: quantityController.text.length),

                );

                setDialogState(() {});

              }),

            ],

          ),

        ],

      ),

    );

  }

 

  Widget _buildProductNumberButton(

    String number,

    TextEditingController quantityController,

    StateSetter setDialogState,

  ) {

    return SizedBox(

      width: 60,

      height: 50,

      child: ElevatedButton(

        onPressed: () {

          // Miktar girişi - direkt ekleme

          quantityController.text += number;

          quantityController.selection = TextSelection.fromPosition(

            TextPosition(offset: quantityController.text.length),

          );

          setDialogState(() {});

        },

        style: ElevatedButton.styleFrom(

          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),

        ),

        child: Text(

          number,

          style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),

        ),

      ),

    );

  }

 

  Widget _buildActionButton(String text, VoidCallback onPressed) {

    return SizedBox(

      width: 60,

      height: 50,

      child: ElevatedButton(

        onPressed: onPressed,

        style: ElevatedButton.styleFrom(

          backgroundColor: Colors.orange,

          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),

        ),

        child: Text(

          text,

          style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),

        ),

      ),

    );

  }

 

  Widget _buildDuzeltButton(String barcode, String itemName) {

    return SizedBox(

      width: 120,

      height: 42,

      child: ElevatedButton.icon(

        onPressed: () => _showProductQuantityDialog(barcode, itemName),

        icon: Icon(Icons.add, size: 20),

        label: Padding(

          padding: const EdgeInsets.only(right: 8.0),

          child: Text('Düzelt'),

        ),

        style: ElevatedButton.styleFrom(

          backgroundColor: Colors.blue.shade700,

          foregroundColor: Colors.white,

          elevation: 2,

          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),

          padding: EdgeInsets.symmetric(vertical: 8),

        ),

      ),

    );

  }

 

  Widget _buildSilButton(String barcode, String itemName, bool isCounted) {

    return SizedBox(

      width: 120,

      height: 42,

      child: ElevatedButton.icon(

        onPressed: isCounted

            ? () => _showDeleteConfirmationDialog(barcode, itemName)

            : null,

        icon: Icon(Icons.delete, size: 20),

        label: Text('Sil'),

        style: ElevatedButton.styleFrom(

          backgroundColor: isCounted

              ? Colors.red.shade700

              : Colors.grey.shade600,

          foregroundColor: Colors.white,

          elevation: 2,

          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),

          padding: EdgeInsets.symmetric(vertical: 8),

        ),

      ),

    );

  }

 

  void _showManualBarcodeDialog() {

    final TextEditingController barcodeInputController =

        TextEditingController();

    final TextEditingController quantityInputController = TextEditingController(

      text: '1',

    );

    final FocusNode barcodeFocusNode = FocusNode();

    final FocusNode quantityFocusNode = FocusNode();

    bool isBarcodeFocused = true;

 

    // Miktar alanındaki metni seç

    WidgetsBinding.instance.addPostFrameCallback((_) {

      quantityInputController.selection = TextSelection(

        baseOffset: 0,

        extentOffset: quantityInputController.text.length,

      );

    });

 

    // Başlangıçta barkod alanına odaklan

    barcodeFocusNode.addListener(() {

      if (barcodeFocusNode.hasFocus) {

        isBarcodeFocused = true;

      }

    });

 

    quantityFocusNode.addListener(() {

      if (quantityFocusNode.hasFocus) {

        isBarcodeFocused = false;

      }

    });

 

    showDialog(

      context: context,

      builder: (context) => StatefulBuilder(

        builder: (context, setDialogState) {

          final currentQuantity =

              _countedItems[barcodeInputController.text.trim()] ?? 0;

          final addQuantity = int.tryParse(quantityInputController.text) ?? 0;

          final newTotal = currentQuantity + addQuantity;

 

          return AlertDialog(

            title: const Text('Manuel Barkod Ekle'),

            content: Column(

              mainAxisSize: MainAxisSize.min,

              children: [

                // Barkod girişi

                TextField(

                  controller: barcodeInputController,

                  focusNode: barcodeFocusNode,

                  readOnly: true,

                  showCursor: true,

                  enableInteractiveSelection: true,

                  style: const TextStyle(

                    fontSize: 18,

                    fontWeight: FontWeight.bold,

                  ),

                  textAlign: TextAlign.center,

                  decoration: InputDecoration(

                    labelText: 'Barkod',

                    hintText: 'Barkod numarasını girin',

                    border: const OutlineInputBorder(),

                    contentPadding: const EdgeInsets.symmetric(

                      horizontal: 16,

                      vertical: 12,

                    ),

                    suffixIcon: IconButton(

                      icon: const Icon(Icons.content_paste),

                      onPressed: () async {

                        final clipboardData = await Clipboard.getData(

                          'text/plain',

                        );

                        if (clipboardData?.text != null) {

                          barcodeInputController.text = clipboardData!.text!;

                          barcodeInputController.selection =

                              TextSelection.fromPosition(

                                TextPosition(

                                  offset: barcodeInputController.text.length,

                                ),

                              );

                          setDialogState(() {});

                        }

                      },

                      tooltip: 'Yapıştır',

                    ),

                  ),

                  onTap: () {

                    setDialogState(() {

                      isBarcodeFocused = true;

                      barcodeFocusNode.requestFocus();

                    });

                  },

                ),

                const SizedBox(height: 16),

                // Miktar girişi

                TextField(

                  controller: quantityInputController,

                  focusNode: quantityFocusNode,

                  readOnly: true,

                  showCursor: true,

                  enableInteractiveSelection: true,

                  style: const TextStyle(

                    fontSize: 18,

                    fontWeight: FontWeight.bold,

                  ),

                  textAlign: TextAlign.center,

                  decoration: const InputDecoration(

                    labelText: 'Eklenecek Miktar',

                    hintText: 'Miktar girin',

                    border: OutlineInputBorder(),

                    contentPadding: EdgeInsets.symmetric(

                      horizontal: 16,

                      vertical: 12,

                    ),

                  ),

                  onTap: () {

                    setDialogState(() {

                      isBarcodeFocused = false;

                      quantityFocusNode.requestFocus();

                    });

                  },

                ),

                const SizedBox(height: 16),

                // Mevcut miktar ve yeni toplam

                if (barcodeInputController.text.trim().isNotEmpty) ...[

                  Container(

                    width: double.infinity,

                    padding: const EdgeInsets.all(12),

                    decoration: BoxDecoration(

                      color: Colors.blue.shade50,

                      borderRadius: BorderRadius.circular(8),

                      border: Border.all(color: Colors.blue.shade200),

                    ),

                    child: Text(

                      'Mevcut Miktar: $currentQuantity',

                      style: const TextStyle(

                        fontSize: 16,

                        fontWeight: FontWeight.bold,

                      ),

                      textAlign: TextAlign.center,

                    ),

                  ),

                  const SizedBox(height: 8),

                  Container(

                    width: double.infinity,

                    padding: const EdgeInsets.all(12),

                    decoration: BoxDecoration(

                      color: Colors.green.shade50,

                      borderRadius: BorderRadius.circular(8),

                      border: Border.all(color: Colors.green.shade200),

                    ),

                    child: Text(

                      'Yeni Toplam: $newTotal',

                      style: const TextStyle(

                        fontSize: 16,

                        fontWeight: FontWeight.bold,

                        color: Colors.green,

                      ),

                      textAlign: TextAlign.center,

                    ),

                  ),

                  const SizedBox(height: 16),

                ],

                // Özel sayısal klavye

                _buildUnifiedKeyboard(

                  barcodeInputController,

                  quantityInputController,

                  isBarcodeFocused,

                  setDialogState,

                ),

              ],

            ),

            actions: [

              TextButton(

                onPressed: () => Navigator.pop(context),

                child: const Text('İptal'),

              ),

              ElevatedButton(

                onPressed: () {

                  final barcode = barcodeInputController.text.trim();

                  final quantityText = quantityInputController.text.trim();

                  final quantity = quantityText.isEmpty

                      ? 1

                      : int.tryParse(quantityText) ?? 1;

 

                  if (barcode.isNotEmpty) {

                    // Barkod var mı kontrol et

                    final existingItemIndex = _inventoryItems.indexWhere(

                      (item) => item.barcode == barcode,

                    );

 

                    if (existingItemIndex != -1) {

                      // Mevcut ürün - miktarını güncelle

                      setState(() {

                        _countedItems[barcode] =

                            (_countedItems[barcode] ?? 0) + quantity;

                        // Ürünü en üste taşı

                        _moveItemToTop(barcode);

                      });

                    } else {

                      // Yeni ürün - listeye ekle

                      final newItem = InventoryItem(

                        id: DateTime.now().millisecondsSinceEpoch.toString(),

                        barcode: barcode,

                        name: '',

                        unit: 'Adet',

                        quantity: 0,

                        averagePrice: 0,

                        totalAmount: 0,

                        date: _selectedDate,

                        department: _selectedDepartment,

                      );

 

                      setState(() {

                        _inventoryItems.insert(0, newItem);

                        _countedItems[barcode] = quantity;

                      });

                    }

 

                    Navigator.pop(context);

                    // Odaklanmayı koru

                    _barcodeFocusNode.requestFocus();

                  }

                },

                child: const Text('Kaydet'),

              ),

            ],

          );

        },

      ),

    );

  }

 

  void _showDeleteConfirmationDialog(String barcode, String itemName) {

    final currentQuantity = _countedItems[barcode] ?? 0;

 

    showDialog(

      context: context,

      builder: (context) => AlertDialog(

        title: const Text('Silme Onayı'),

        content: Column(

          mainAxisSize: MainAxisSize.min,

          children: [

            Text(

              'Barkod: $barcode',

              style: const TextStyle(fontWeight: FontWeight.bold),

            ),

            if (itemName.isNotEmpty) ...[

              const SizedBox(height: 8),

              Text('Ürün: $itemName'),

            ],

            const SizedBox(height: 16),

            Container(

              width: double.infinity,

              padding: const EdgeInsets.all(12),

              decoration: BoxDecoration(

                color: Colors.red.shade50,

                borderRadius: BorderRadius.circular(8),

                border: Border.all(color: Colors.red.shade200),

              ),

              child: Text(

                'Mevcut Miktar: $currentQuantity',

                style: const TextStyle(

                  fontSize: 16,

                  fontWeight: FontWeight.bold,

                  color: Colors.red,

                ),

                textAlign: TextAlign.center,

              ),

            ),

            const SizedBox(height: 16),

            const Text(

              'Bu ürünü listeden silmek istediğinize emin misiniz?',

              textAlign: TextAlign.center,

            ),

          ],

        ),

        actions: [

          TextButton(

            onPressed: () => Navigator.pop(context),

            child: const Text('İptal'),

          ),

          ElevatedButton(

            onPressed: () {

              setState(() {

                _countedItems.remove(barcode);

                // Eğer sayılan miktar yoksa, ürünü listeden de kaldır

                if (_countedItems[barcode] == null) {

                  _inventoryItems.removeWhere(

                    (item) => item.barcode == barcode,

                  );

                }

              });

              Navigator.pop(context);

              // Odaklanmayı koru

              _barcodeFocusNode.requestFocus();

            },

            style: ElevatedButton.styleFrom(

              backgroundColor: Colors.red,

              foregroundColor: Colors.white,

            ),

            child: const Text('Sil'),

          ),

        ],

      ),

    );

  }

 

  void _showSummaryDialog() {

    // Toplam sayılan miktarı hesapla

    final totalCounted = _countedItems.values.fold(

      0,

      (sum, count) => sum + count,

    );

 

    showDialog(

      context: context,

      builder: (context) => AlertDialog(

        title: const Text('Sayım Özeti'),

        content: SizedBox(

          width: double.maxFinite,

          child: Column(

            mainAxisSize: MainAxisSize.min,

            children: [

              Text('Toplam ${_inventoryItems.length} farklı barkod'),

              Text('Toplam ${totalCounted} adet sayıldı'),

              const SizedBox(height: 16),

              const Text(

                'Sayılan Barkodlar:',

                style: TextStyle(fontWeight: FontWeight.bold),

              ),

              const SizedBox(height: 8),

              ..._countedItems.entries.map((entry) {

                return ListTile(

                  title: Text('Barkod: ${entry.key}'),

                  trailing: Text('${entry.value} adet'),

                );

              }),

            ],

          ),

        ),

        actions: [

          TextButton(

            onPressed: () => Navigator.pop(context),

            child: const Text('Kapat'),

          ),

          ElevatedButton(

            onPressed: () {

              Navigator.pop(context);

            },

            child: const Text('Tamam'),

          ),

        ],

      ),

    );

  }

 

  @override

  Widget build(BuildContext context) {

    return _buildMainWidget();

  }

 

  Widget _buildInventoryListWidget() {

    return Expanded(

      child: Card(

        margin: const EdgeInsets.all(16),

        child: Column(

          children: [

            Expanded(

              child: _inventoryItems.isEmpty

                  ? Center(

                      child: Text(

                        'Listele butonuna basarak ürünleri yükleyin',

                        style: Theme.of(context).textTheme.bodyLarge?.copyWith(

                          color: Theme.of(context).colorScheme.onSurfaceVariant,

                        ),

                      ),

                    )

                  : ListView.builder(

                      padding: const EdgeInsets.only(bottom: 150),

                      itemCount: _inventoryItems.length,

                      itemBuilder: (context, index) {

                        final item = _inventoryItems[index];

                        final countedQuantity =

                            _countedItems[item.barcode] ?? 0;

                        final isCounted = countedQuantity > 0;

 

                        return Column(

                          children: [

                            Container(

                              padding: const EdgeInsets.only(

                                left: 16,

                                right: 0,

                                top: 12,

                                bottom: 12,

                              ),

                              child: Row(

                                children: [

                                  // Main content

                                  Expanded(

                                    child: Column(

                                      crossAxisAlignment:

                                          CrossAxisAlignment.start,

                                      children: [

                                        // Title

                                        Text(

                                          item.name.isEmpty

                                              ? 'Barkod: ${item.barcode}'

                                              : item.name,

                                          style: TextStyle(

                                            color: isCounted

                                                ? Colors.green

                                                : null,

                                            fontWeight: isCounted

                                                ? FontWeight.bold

                                                : null,

                                            fontSize: 16,

                                          ),

                                        ),

                                        if (item.name.isNotEmpty) ...[

                                          const SizedBox(height: 2),

                                          Text(

                                            'Barkod: ${item.barcode}',

                                            style: TextStyle(

                                              fontSize: 12,

                                              color: Colors.grey.shade600,

                                            ),

                                          ),

                                        ],

                                        const SizedBox(height: 4),

                                        // Sayılan adet göstergesi

                                        Container(

                                          padding: const EdgeInsets.symmetric(

                                            horizontal: 8,

                                            vertical: 3,

                                          ),

                                          decoration: BoxDecoration(

                                            color: isCounted

                                                ? Colors.green.shade100

                                                : Colors.grey.shade100,

                                            borderRadius: BorderRadius.circular(

                                              8,

                                            ),

                                            border: Border.all(

                                              color: isCounted

                                                  ? Colors.green.shade300

                                                  : Colors.grey.shade300,

                                              width: 1,

                                            ),

                                          ),

                                          child: Text(

                                            'Sayılan: $countedQuantity',

                                            style: TextStyle(

                                              color: isCounted

                                                  ? Colors.green.shade800

                                                  : Colors.grey.shade600,

                                              fontWeight: FontWeight.bold,

                                              fontSize: 18,

                                            ),

                                          ),

                                        ),

                                      ],

                                    ),

                                  ),

                                  const SizedBox(width: 20),

 

                                  // Buttons - Column with fixed width, right aligned

                                  Padding(

                                    padding: const EdgeInsets.only(right: 5),

                                    child: Column(

                                      crossAxisAlignment:

                                          CrossAxisAlignment.end,

                                      children: [

                                        // Düzelt button

                                        _buildDuzeltButton(

                                          item.barcode,

                                          item.name,

                                        ),

                                        const SizedBox(height: 6),

                                        // Sil button

                                        _buildSilButton(

                                          item.barcode,

                                          item.name,

                                          isCounted,

                                        ),

                                      ],

                                    ),

                                  ),

                                ],

                              ),

                            ),

 

                            // Divider between items

                            if (index < _inventoryItems.length - 1)

                              const Divider(

                                height: 1,

                                thickness: 1,

                                indent: 16,

                                endIndent: 16,

                              ),

                          ],

                        );

                      },

                    ),

            ),

          ],

        ),

      ),

    );

  }

 

  Widget _buildListButtonWidget() {

    return Padding(

      padding: const EdgeInsets.symmetric(horizontal: 16),

      child: SizedBox(

        width: double.infinity,

        height: 48,

        child: ElevatedButton.icon(

          onPressed: _isLoading ? null : _loadInventory,

          icon: _isLoading

              ? const SizedBox(

                  width: 20,

                  height: 20,

                  child: CircularProgressIndicator(strokeWidth: 2),

                )

              : const Icon(Icons.refresh, size: 20),

          label: Text(

            _isLoading ? 'Yükleniyor...' : 'Listele',

            style: const TextStyle(fontSize: 16),

          ),

        ),

      ),

    );

  }

 

  Widget _buildSelectionCardsWidget() {

    return Padding(

      padding: const EdgeInsets.symmetric(horizontal: 12),

      child: Row(

        children: [

          // Date Selection

          Expanded(

            child: Card(

              child: InkWell(

                onTap: _selectDate,

                borderRadius: BorderRadius.circular(12),

                child: Padding(

                  padding: const EdgeInsets.all(12),

                  child: Column(

                    children: [

                      Text(

                        'Tarih Seçiniz',

                        style: Theme.of(context).textTheme.labelSmall?.copyWith(

                          color: Theme.of(context).colorScheme.onSurfaceVariant,

                        ),

                        textAlign: TextAlign.center,

                      ),

                      const SizedBox(height: 4),

                      const Icon(Icons.calendar_today, size: 20),

                      const SizedBox(height: 4),

                      Text(

                        DateFormat(

                          'dd MMMM yyyy',

                          'tr_TR',

                        ).format(_selectedDate),

                        style: Theme.of(context).textTheme.bodySmall,

                        textAlign: TextAlign.center,

                      ),

                    ],

                  ),

                ),

              ),

            ),

          ),

          const SizedBox(width: 8),

          // Department Selection

          Expanded(

            child: Card(

              child: InkWell(

                onTap: _selectDepartment,

                borderRadius: BorderRadius.circular(12),

                child: Padding(

                  padding: const EdgeInsets.all(12),

                  child: Column(

                    children: [

                      Text(

                        'Departman Seçiniz',

                        style: Theme.of(context).textTheme.labelSmall?.copyWith(

                          color: Theme.of(context).colorScheme.onSurfaceVariant,

                        ),

                        textAlign: TextAlign.center,

                      ),

                      const SizedBox(height: 4),

                      const Icon(Icons.business, size: 20),

                      const SizedBox(height: 4),

                      Text(

                        _selectedDepartment,

                        style: Theme.of(context).textTheme.bodySmall,

                        textAlign: TextAlign.center,

                      ),

                    ],

                  ),

                ),

              ),

            ),

          ),

          const SizedBox(width: 8),

          // Type Selection

          Expanded(

            child: Card(

              child: InkWell(

                onTap: _showTypeSelection,

                borderRadius: BorderRadius.circular(12),

                child: Padding(

                  padding: const EdgeInsets.all(12),

                  child: Column(

                    children: [

                      Text(

                        'Tip Seçiniz',

                        style: Theme.of(context).textTheme.labelSmall?.copyWith(

                          color: Theme.of(context).colorScheme.onSurfaceVariant,

                        ),

                        textAlign: TextAlign.center,

                      ),

                      const SizedBox(height: 4),

                      Icon(

                        _selectedType == 'stok'

                            ? Icons.inventory

                            : Icons.receipt,

                        size: 20,

                      ),

                      const SizedBox(height: 4),

                      Text(

                        _selectedType == 'stok' ? 'Stok' : 'Reçete',

                        style: Theme.of(context).textTheme.bodySmall,

                        textAlign: TextAlign.center,

                      ),

                    ],

                  ),

                ),

              ),

            ),

          ),

        ],

      ),

    );

  }

 

  Widget _buildMainWidget() {

    return Scaffold(

      appBar: AppBar(

        actions: [

          // Lazer okuyucu TextField

          Expanded(

            child: Card(

              margin: const EdgeInsets.only(right: 5, left: 15),

              elevation: 2,

              shape: RoundedRectangleBorder(

                borderRadius: BorderRadius.circular(12),

              ),

              child: TextField(

                controller: _manualBarcodeController,

                focusNode: _barcodeFocusNode,

                keyboardType: TextInputType.none,

                textInputAction: TextInputAction.none,

                enableInteractiveSelection: false,

                showCursor: false,

                readOnly: false,

                decoration: const InputDecoration(

                  hintText: 'Lazer ile   barkod okutun...',

                  border: InputBorder.none,

                  contentPadding: EdgeInsets.symmetric(

                    horizontal: 16,

                    vertical: 12,

                  ),

                ),

                onChanged: (value) {

                  // Lazer okuyucu verisi kontrolü

                  print('TextField değişti: $value');

 

                  if (value.endsWith('\n')) {

                    final barcode = value.replaceAll('\n', '').trim();

                    if (barcode.isNotEmpty) {

                      print('Lazer okuyucu barkod tespit edildi: $barcode');

                      _processBarcode(barcode);

                      _manualBarcodeController.clear();

                      // Odaklanmayı koru

                      _barcodeFocusNode.requestFocus();

                    }

                  }

                },

                onSubmitted: (value) {

                  // Enter tuşu işleme

                  if (value.isNotEmpty) {

                    print('Manuel giriş barkod: $value');

                    _processBarcode(value);

                    _manualBarcodeController.clear();

                    // Odaklanmayı koru

                    _barcodeFocusNode.requestFocus();

                  }

                },

              ),

            ),

          ),

 

          // Kamera ile barkod okuma butonu

          IconButton(

            icon: const Icon(Icons.qr_code_scanner),

            onPressed: _scanBarcode,

            tooltip: 'Kamera ile Tara',

          ),

          // Manuel barkod ekleme butonu

          IconButton(

            icon: const Icon(Icons.add),

            onPressed: _showManualBarcodeDialog,

            tooltip: 'Manuel Barkod Ekle',

          ),

        ],

      ),

 

      body: Column(

        children: [

          _buildSelectionCardsWidget(),

          _buildListButtonWidget(),

          _buildInventoryListWidget(),

        ],

      ),

      floatingActionButton: FloatingActionButton.extended(

        onPressed: _countedItems.isNotEmpty ? _showSummaryDialog : null,

        icon: const Icon(Icons.check),

        label: const Text('KAYDET'),

      ),

    );

  }

}

 

class QRScannerPage extends StatefulWidget {

  const QRScannerPage({super.key});

 

  @override

  State<QRScannerPage> createState() => _QRScannerPageState();

}

 

class _QRScannerPageState extends State<QRScannerPage> {

  Barcode? result;

  QRViewController? controller;

  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');

  bool _isCameraInitialized = false;

  bool _isDisposed = false;

 

  // In order to get hot reload to work we need to pause the camera if the platform

  // is android, or resume the camera if the platform is iOS.

  @override

  void reassemble() {

    super.reassemble();

    if (!_isDisposed && controller != null) {

      if (Platform.isAndroid) {

        controller!.pauseCamera();

      }

      controller!.resumeCamera();

    }

  }

 

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: const Text('Barkod Tara'),

        backgroundColor: Colors.black,

        foregroundColor: Colors.white,

      ),

      body: Column(

        children: <Widget>[

          Expanded(flex: 4, child: _buildQrView(context)),

          Expanded(

            flex: 1,

            child: Container(

              color: Colors.black,

              child: FittedBox(

                fit: BoxFit.contain,

                child: Column(

                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,

                  children: <Widget>[

                    if (result != null)

                      Text(

                        'Barkod: ${result!.code}',

                        style: const TextStyle(

                          color: Colors.white,

                          fontSize: 18,

                          fontWeight: FontWeight.bold,

                        ),

                      )

                    else

                      const Text(

                        'Barkodu kameraya gösterin',

                        style: TextStyle(color: Colors.white, fontSize: 16),

                      ),

                    Row(

                      mainAxisAlignment: MainAxisAlignment.center,

                      crossAxisAlignment: CrossAxisAlignment.center,

                      children: <Widget>[

                        Container(

                          margin: const EdgeInsets.all(8),

                          child: ElevatedButton(

                            onPressed: _isCameraInitialized && !_isDisposed

                                ? () async {

                                    try {

                                      await controller?.toggleFlash();

                                      if (mounted) setState(() {});

                                    } catch (e) {

                                      log('Flash toggle error: $e');

                                    }

                                  }

                                : null,

                            child: _isCameraInitialized

                                ? FutureBuilder(

                                    future: controller?.getFlashStatus(),

                                    builder: (context, snapshot) {

                                      return Text(

                                        'Flash: ${snapshot.data ?? 'Bilinmiyor'}',

                                      );

                                    },

                                  )

                                : const Text('Flash: Yükleniyor...'),

                          ),

                        ),

                        Container(

                          margin: const EdgeInsets.all(8),

                          child: ElevatedButton(

                            onPressed: _isCameraInitialized && !_isDisposed

                                ? () async {

                                    try {

                                      await controller?.flipCamera();

                                      if (mounted) setState(() {});

                                    } catch (e) {

                                      log('Camera flip error: $e');

                                    }

                                  }

                                : null,

                            child: _isCameraInitialized

                                ? FutureBuilder(

                                    future: controller?.getCameraInfo(),

                                    builder: (context, snapshot) {

                                      if (snapshot.data != null) {

                                        return Text(

                                          'Kamera: ${snapshot.data!.name}',

                                        );

                                      } else {

                                        return const Text(

                                          'Kamera: Yükleniyor...',

                                        );

                                      }

                                    },

                                  )

                                : const Text('Kamera: Yükleniyor...'),

                          ),

                        ),

                      ],

                    ),

                    Row(

                      mainAxisAlignment: MainAxisAlignment.center,

                      crossAxisAlignment: CrossAxisAlignment.center,

                      children: <Widget>[

                        Container(

                          margin: const EdgeInsets.all(8),

                          child: ElevatedButton(

                            onPressed: _isCameraInitialized && !_isDisposed

                                ? () async {

                                    try {

                                      await controller?.pauseCamera();

                                    } catch (e) {

                                      log('Camera pause error: $e');

                                    }

                                  }

                                : null,

                            child: const Text(

                              'Duraklat',

                              style: TextStyle(fontSize: 16),

                            ),

                          ),

                        ),

                        Container(

                          margin: const EdgeInsets.all(8),

                          child: ElevatedButton(

                            onPressed: _isCameraInitialized && !_isDisposed

                                ? () async {

                                    try {

                                      await controller?.resumeCamera();

                                    } catch (e) {

                                      log('Camera resume error: $e');

                                    }

                                  }

                                : null,

                            child: const Text(

                              'Devam Et',

                              style: TextStyle(fontSize: 16),

                            ),

                          ),

                        ),

                      ],

                    ),

                  ],

                ),

              ),

            ),

          ),

        ],

      ),

    );

  }

 

  Widget _buildQrView(BuildContext context) {

    // For this example we check how width or tall the device is and change the scanArea and overlay accordingly.

    var scanArea =

        (MediaQuery.of(context).size.width < 400 ||

            MediaQuery.of(context).size.height < 400)

        ? 150.0

        : 300.0;

    // To ensure the Scanner view is properly sizes after rotation

    // we need to listen for Flutter SizeChanged notification and update controller

    return QRView(

      key: qrKey,

      onQRViewCreated: _onQRViewCreated,

      overlay: QrScannerOverlayShape(

        borderColor: Colors.red,

        borderRadius: 10,

        borderLength: 30,

        borderWidth: 10,

        cutOutSize: scanArea,

      ),

      onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),

    );

  }

 

  void _onQRViewCreated(QRViewController controller) {

    if (_isDisposed) return;

 

    setState(() {

      this.controller = controller;

      _isCameraInitialized = true;

    });

 

    controller.scannedDataStream.listen((scanData) {

      if (_isDisposed) return;

 

      setState(() {

        result = scanData;

      });

 

      // Barkod okunduğunda ana sayfaya dön ve işle

      if (result != null && result!.code != null) {

        Navigator.pop(context, result!.code);

      }

    });

  }

 

  void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {

    log('${DateTime.now().toIso8601String()}_onPermissionSet $p');

    if (!p) {

      ScaffoldMessenger.of(

        context,

      ).showSnackBar(const SnackBar(content: Text('Kamera izni gerekli')));

    }

  }

 

  @override

  void dispose() {

    _isDisposed = true;

    if (controller != null) {

      controller!.pauseCamera();

      controller!.dispose();

      controller = null;

    }

    super.dispose();

  }

}

 

 

 2025 Ekim 16 Perşembe
 56