Refactor ReceiptScreen: Memindahkan logika kompleks ke service terpisah dan membuat widget terpisah untuk SpeedDial
							parent
							
								
									3e1c34d1ce
								
							
						
					
					
						commit
						3baa17e9a5
					
				| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import 'package:intl/intl.dart';
 | 
			
		||||
 | 
			
		||||
extension DoubleFormatting on double {
 | 
			
		||||
  /// Memformat angka menjadi format mata uang Rupiah
 | 
			
		||||
  String toRupiah() {
 | 
			
		||||
    final formatter = NumberFormat("#,##0", "id_ID");
 | 
			
		||||
    return "Rp ${formatter.format(this)}";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
class AccountDialogService {
 | 
			
		||||
  /// Menampilkan dialog untuk memilih akun sumber (revenue)
 | 
			
		||||
  static Future<Map<String, dynamic>?> showSourceAccountDialog(
 | 
			
		||||
    BuildContext context,
 | 
			
		||||
    List<Map<String, dynamic>> accounts,
 | 
			
		||||
  ) async {
 | 
			
		||||
    if (accounts.isEmpty) {
 | 
			
		||||
      ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
        const SnackBar(
 | 
			
		||||
          content: Text(
 | 
			
		||||
            'Daftar akun kosong. Pastikan kredensial sudah diatur dan akun telah dimuat. Klik "Muat Ulang Akun" untuk mencoba lagi.')),
 | 
			
		||||
      );
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Filter akun sumber (revenue)
 | 
			
		||||
    final revenueAccounts =
 | 
			
		||||
        accounts.where((account) => account['type'] == 'revenue').toList();
 | 
			
		||||
 | 
			
		||||
    if (revenueAccounts.isEmpty) {
 | 
			
		||||
      ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
        const SnackBar(
 | 
			
		||||
          content: Text(
 | 
			
		||||
            'Tidak ada akun sumber (revenue) yang ditemukan. Klik "Muat Ulang Akun" untuk mencoba lagi atau periksa akun Anda di Firefly III.')),
 | 
			
		||||
      );
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return await showDialog<Map<String, dynamic>?>(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (context) {
 | 
			
		||||
        return AlertDialog(
 | 
			
		||||
          title: const Text('Pilih Akun Sumber'),
 | 
			
		||||
          content: SizedBox(
 | 
			
		||||
            width: double.maxFinite,
 | 
			
		||||
            child: ListView.builder(
 | 
			
		||||
              shrinkWrap: true,
 | 
			
		||||
              itemCount: revenueAccounts.length,
 | 
			
		||||
              itemBuilder: (context, index) {
 | 
			
		||||
                final account = revenueAccounts[index];
 | 
			
		||||
                return ListTile(
 | 
			
		||||
                  title: Text(account['name']),
 | 
			
		||||
                  subtitle: Text(account['type']),
 | 
			
		||||
                  onTap: () => Navigator.of(context).pop(account),
 | 
			
		||||
                );
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Menampilkan dialog untuk memilih akun tujuan (asset)
 | 
			
		||||
  static Future<Map<String, dynamic>?> showDestinationAccountDialog(
 | 
			
		||||
    BuildContext context,
 | 
			
		||||
    List<Map<String, dynamic>> accounts,
 | 
			
		||||
  ) async {
 | 
			
		||||
    if (accounts.isEmpty) {
 | 
			
		||||
      ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
        const SnackBar(
 | 
			
		||||
          content: Text(
 | 
			
		||||
            'Daftar akun kosong. Pastikan kredensial sudah diatur dan akun telah dimuat. Klik "Muat Ulang Akun" untuk mencoba lagi.')),
 | 
			
		||||
      );
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Filter akun tujuan (asset)
 | 
			
		||||
    final assetAccounts =
 | 
			
		||||
        accounts.where((account) => account['type'] == 'asset').toList();
 | 
			
		||||
 | 
			
		||||
    if (assetAccounts.isEmpty) {
 | 
			
		||||
      ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
        const SnackBar(
 | 
			
		||||
          content: Text(
 | 
			
		||||
            'Tidak ada akun tujuan (asset) yang ditemukan. Klik "Muat Ulang Akun" untuk mencoba lagi atau periksa akun Anda di Firefly III.')),
 | 
			
		||||
      );
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return await showDialog<Map<String, dynamic>?>(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (context) {
 | 
			
		||||
        return AlertDialog(
 | 
			
		||||
          title: const Text('Pilih Akun Tujuan'),
 | 
			
		||||
          content: SizedBox(
 | 
			
		||||
            width: double.maxFinite,
 | 
			
		||||
            child: ListView.builder(
 | 
			
		||||
              shrinkWrap: true,
 | 
			
		||||
              itemCount: assetAccounts.length,
 | 
			
		||||
              itemBuilder: (context, index) {
 | 
			
		||||
                final account = assetAccounts[index];
 | 
			
		||||
                return ListTile(
 | 
			
		||||
                  title: Text(account['name']),
 | 
			
		||||
                  subtitle: Text(account['type']),
 | 
			
		||||
                  onTap: () => Navigator.of(context).pop(account),
 | 
			
		||||
                );
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,140 @@
 | 
			
		|||
import 'package:bluetooth_print/bluetooth_print.dart';
 | 
			
		||||
import 'package:bluetooth_print/bluetooth_print_model.dart';
 | 
			
		||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
			
		||||
import 'dart:typed_data';
 | 
			
		||||
 | 
			
		||||
class BluetoothService {
 | 
			
		||||
  late BluetoothPrint _bluetoothPrint;
 | 
			
		||||
  BluetoothDevice? _connectedDevice;
 | 
			
		||||
  bool _isConnected = false;
 | 
			
		||||
  bool _isPrinting = false;
 | 
			
		||||
 | 
			
		||||
  BluetoothService() {
 | 
			
		||||
    _bluetoothPrint = BluetoothPrint.instance;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Getter untuk status koneksi
 | 
			
		||||
  bool get isConnected => _isConnected;
 | 
			
		||||
  bool get isPrinting => _isPrinting;
 | 
			
		||||
  BluetoothDevice? get connectedDevice => _connectedDevice;
 | 
			
		||||
 | 
			
		||||
  /// Inisialisasi Bluetooth printer
 | 
			
		||||
  Future<void> initialize() async {
 | 
			
		||||
    // Memeriksa status koneksi Bluetooth
 | 
			
		||||
    final isConnected = await _bluetoothPrint.isConnected ?? false;
 | 
			
		||||
    _isConnected = isConnected;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Memuat device bluetooth yang tersimpan
 | 
			
		||||
  Future<void> loadSavedDevice() async {
 | 
			
		||||
    final prefs = await SharedPreferences.getInstance();
 | 
			
		||||
    final deviceAddress = prefs.getString('bluetooth_device_address');
 | 
			
		||||
    final deviceName = prefs.getString('bluetooth_device_name');
 | 
			
		||||
 | 
			
		||||
    if (deviceAddress != null && deviceName != null) {
 | 
			
		||||
      _connectedDevice = BluetoothDevice();
 | 
			
		||||
      _connectedDevice!.name = deviceName;
 | 
			
		||||
      _connectedDevice!.address = deviceAddress;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Memeriksa status koneksi Bluetooth printer secara real-time
 | 
			
		||||
  Future<bool> checkConnection() async {
 | 
			
		||||
    try {
 | 
			
		||||
      final isConnected = await _bluetoothPrint.isConnected ?? false;
 | 
			
		||||
      _isConnected = isConnected;
 | 
			
		||||
      return isConnected;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Menghubungkan ke device bluetooth
 | 
			
		||||
  Future<bool> connectToDevice(BluetoothDevice device) async {
 | 
			
		||||
    try {
 | 
			
		||||
      await _bluetoothPrint.connect(device);
 | 
			
		||||
      _connectedDevice = device;
 | 
			
		||||
      _isConnected = true;
 | 
			
		||||
      
 | 
			
		||||
      // Simpan device ke SharedPreferences
 | 
			
		||||
      final prefs = await SharedPreferences.getInstance();
 | 
			
		||||
      await prefs.setString('bluetooth_device_address', device.address ?? '');
 | 
			
		||||
      await prefs.setString('bluetooth_device_name', device.name ?? '');
 | 
			
		||||
      
 | 
			
		||||
      return true;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Memutuskan koneksi dari device bluetooth
 | 
			
		||||
  Future<void> disconnect() async {
 | 
			
		||||
    try {
 | 
			
		||||
      await _bluetoothPrint.disconnect();
 | 
			
		||||
      _isConnected = false;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      rethrow;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Mencetak struk ke printer thermal
 | 
			
		||||
  Future<void> printReceipt(Uint8List data) async {
 | 
			
		||||
    if (!_isConnected) {
 | 
			
		||||
      throw Exception('Printer tidak terhubung');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isPrinting = true;
 | 
			
		||||
    try {
 | 
			
		||||
      await _bluetoothPrint.printRawData(data);
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isPrinting = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Mencoba menyambungkan kembali ke printer jika terputus
 | 
			
		||||
  Future<bool> reconnectIfNeeded() async {
 | 
			
		||||
    // Periksa koneksi printer secara real-time
 | 
			
		||||
    bool isConnected = await checkConnection();
 | 
			
		||||
    
 | 
			
		||||
    if (!isConnected) {
 | 
			
		||||
      // Coba sambungkan kembali jika ada device yang tersimpan
 | 
			
		||||
      if (_connectedDevice != null) {
 | 
			
		||||
        try {
 | 
			
		||||
          // Putuskan koneksi yang mungkin tersisa
 | 
			
		||||
          try {
 | 
			
		||||
            await disconnect();
 | 
			
		||||
          } catch (disconnectError) {
 | 
			
		||||
            // Tidak ada koneksi yang perlu diputuskan
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
          // Tunggu sebentar sebelum menyambungkan kembali
 | 
			
		||||
          await Future.delayed(const Duration(milliseconds: 500));
 | 
			
		||||
          
 | 
			
		||||
          // Sambungkan kembali
 | 
			
		||||
          bool connectResult = await connectToDevice(_connectedDevice!);
 | 
			
		||||
          
 | 
			
		||||
          if (!connectResult) {
 | 
			
		||||
            return false;
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
          // Tunggu sebentar untuk memastikan koneksi stabil
 | 
			
		||||
          await Future.delayed(const Duration(milliseconds: 500));
 | 
			
		||||
          
 | 
			
		||||
          // Periksa koneksi lagi
 | 
			
		||||
          isConnected = await checkConnection();
 | 
			
		||||
          
 | 
			
		||||
          return isConnected;
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Mendengarkan perubahan status bluetooth
 | 
			
		||||
  Stream<dynamic> get state => _bluetoothPrint.state;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ import 'package:image/image.dart' as img;
 | 
			
		|||
import 'dart:io';
 | 
			
		||||
import 'dart:ui' as ui;
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
/// Service untuk menghasilkan perintah ESC/POS menggunakan flutter_esc_pos_utils
 | 
			
		||||
class EscPosPrintService {
 | 
			
		||||
| 
						 | 
				
			
			@ -357,6 +358,77 @@ class EscPosPrintService {
 | 
			
		|||
    
 | 
			
		||||
    return bytes;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Mencetak struk ke printer thermal
 | 
			
		||||
  static Future<void> printToThermalPrinter({
 | 
			
		||||
    required List<ReceiptItem> items,
 | 
			
		||||
    required DateTime transactionDate,
 | 
			
		||||
    required BuildContext context,
 | 
			
		||||
    required dynamic bluetoothService, // Kita akan sesuaikan tipe ini nanti
 | 
			
		||||
  }) async {
 | 
			
		||||
    // Definisikan cashierId dan transactionId di sini karena tidak berubah
 | 
			
		||||
    final String cashierId = 'KSR001';
 | 
			
		||||
    final String transactionId = 'TXN202508200001';
 | 
			
		||||
    
 | 
			
		||||
    print('=== FUNGSI printToThermalPrinter DIPANGGIL ===');
 | 
			
		||||
    print('Memulai proses pencetakan struk...');
 | 
			
		||||
    print('Jumlah item: ${items.length}');
 | 
			
		||||
    print('Tanggal transaksi: ${transactionDate}');
 | 
			
		||||
    print('ID kasir: $cashierId');
 | 
			
		||||
    print('ID transaksi: $transactionId');
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      print('Menghasilkan byte array ESC/POS menggunakan flutter_esc_pos_utils...');
 | 
			
		||||
      print('Jumlah item: ${items.length}');
 | 
			
		||||
      print('Tanggal transaksi: ${transactionDate}');
 | 
			
		||||
      print('ID kasir: $cashierId');
 | 
			
		||||
      print('ID transaksi: $transactionId');
 | 
			
		||||
      
 | 
			
		||||
      // Generate struk dalam format byte array menggunakan EscPosPrintService
 | 
			
		||||
      final bytes = await generateEscPosBytes(
 | 
			
		||||
        items: items,
 | 
			
		||||
        transactionDate: transactionDate,
 | 
			
		||||
        cashierId: cashierId,
 | 
			
		||||
        transactionId: transactionId,
 | 
			
		||||
      );
 | 
			
		||||
      
 | 
			
		||||
      print('Byte array ESC/POS berhasil dihasilkan');
 | 
			
		||||
      print('Jumlah byte: ${bytes.length}');
 | 
			
		||||
 | 
			
		||||
      // Tampilkan byte array untuk debugging (dalam format hex)
 | 
			
		||||
      print('Isi byte array (hex):');
 | 
			
		||||
      if (bytes.length <= 1000) { // Batasi tampilan untuk mencegah output terlalu panjang
 | 
			
		||||
        print(bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' '));
 | 
			
		||||
      } else {
 | 
			
		||||
        print('Terlalu banyak byte untuk ditampilkan (${bytes.length} bytes)');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Verifikasi koneksi sebelum mencetak
 | 
			
		||||
      final isConnectedBeforePrint = await bluetoothService.checkConnection();
 | 
			
		||||
      if (!isConnectedBeforePrint) {
 | 
			
		||||
        print('Printer tidak terhubung saat akan mencetak');
 | 
			
		||||
        throw Exception('Printer tidak terhubung saat akan mencetak');
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      print('Mengirim byte array ke printer...');
 | 
			
		||||
      
 | 
			
		||||
      try {
 | 
			
		||||
        // Konversi List<int> ke Uint8List
 | 
			
		||||
        final Uint8List data = Uint8List.fromList(bytes);
 | 
			
		||||
        await bluetoothService.printReceipt(data);
 | 
			
		||||
        print('Perintah cetak berhasil dikirim');
 | 
			
		||||
      } catch (printError) {
 | 
			
		||||
        print('Error saat mengirim perintah cetak ke printer: $printError');
 | 
			
		||||
        throw Exception('Gagal mengirim perintah cetak: $printError');
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e, stackTrace) {
 | 
			
		||||
      print('Error saat mencetak struk: $e');
 | 
			
		||||
      print('Stack trace: $stackTrace');
 | 
			
		||||
      // Cetak detail error tambahan
 | 
			
		||||
      print('Tipe error: ${e.runtimeType}');
 | 
			
		||||
      throw Exception('Gagal mencetak struk: $e');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Data class to hold image processing parameters
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,124 @@
 | 
			
		|||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
 | 
			
		||||
import 'package:cashumit/services/bluetooth_service.dart';
 | 
			
		||||
 | 
			
		||||
class ReceiptSpeedDial extends StatelessWidget {
 | 
			
		||||
  final BluetoothService bluetoothService;
 | 
			
		||||
  final Future<bool> Function() onCheckConnection;
 | 
			
		||||
  final Future<void> Function() onPrint;
 | 
			
		||||
  final VoidCallback onSettings;
 | 
			
		||||
  final Future<void> Function() onReloadAccounts;
 | 
			
		||||
  final bool hasItems;
 | 
			
		||||
  final bool hasSourceAccount;
 | 
			
		||||
  final bool hasDestinationAccount;
 | 
			
		||||
  final Future<void> Function() onSendToFirefly;
 | 
			
		||||
 | 
			
		||||
  const ReceiptSpeedDial({
 | 
			
		||||
    Key? key,
 | 
			
		||||
    required this.bluetoothService,
 | 
			
		||||
    required this.onCheckConnection,
 | 
			
		||||
    required this.onPrint,
 | 
			
		||||
    required this.onSettings,
 | 
			
		||||
    required this.onReloadAccounts,
 | 
			
		||||
    required this.hasItems,
 | 
			
		||||
    required this.hasSourceAccount,
 | 
			
		||||
    required this.hasDestinationAccount,
 | 
			
		||||
    required this.onSendToFirefly,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return SpeedDial(
 | 
			
		||||
      icon: Icons.menu,
 | 
			
		||||
      activeIcon: Icons.close,
 | 
			
		||||
      spacing: 3,
 | 
			
		||||
      spaceBetweenChildren: 4,
 | 
			
		||||
      children: [
 | 
			
		||||
        SpeedDialChild(
 | 
			
		||||
          child: const Icon(Icons.send),
 | 
			
		||||
          label: 'Kirim ke Firefly',
 | 
			
		||||
          onTap: hasItems && hasSourceAccount && hasDestinationAccount
 | 
			
		||||
              ? onSendToFirefly
 | 
			
		||||
              : () {
 | 
			
		||||
                  ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                    const SnackBar(
 | 
			
		||||
                      content: Text(
 | 
			
		||||
                          'Pilih akun sumber dan tujuan terlebih dahulu'),
 | 
			
		||||
                      duration: Duration(seconds: 2),
 | 
			
		||||
                    ),
 | 
			
		||||
                  );
 | 
			
		||||
                },
 | 
			
		||||
          backgroundColor: hasItems &&
 | 
			
		||||
                  hasSourceAccount &&
 | 
			
		||||
                  hasDestinationAccount
 | 
			
		||||
              ? Colors.blue
 | 
			
		||||
              : Colors.grey,
 | 
			
		||||
        ),
 | 
			
		||||
        SpeedDialChild(
 | 
			
		||||
          child: const Icon(Icons.refresh),
 | 
			
		||||
          label: 'Muat Ulang Akun',
 | 
			
		||||
          onTap: onReloadAccounts,
 | 
			
		||||
          backgroundColor: Colors.orange,
 | 
			
		||||
        ),
 | 
			
		||||
        SpeedDialChild(
 | 
			
		||||
          child: const Icon(Icons.settings),
 | 
			
		||||
          label: 'Pengaturan',
 | 
			
		||||
          onTap: onSettings,
 | 
			
		||||
          backgroundColor: Colors.green,
 | 
			
		||||
        ),
 | 
			
		||||
        SpeedDialChild(
 | 
			
		||||
          child: bluetoothService.isPrinting
 | 
			
		||||
              ? const CircularProgressIndicator(color: Colors.white, strokeWidth: 2)
 | 
			
		||||
              : const Icon(Icons.receipt),
 | 
			
		||||
          label: 'Cetak Struk',
 | 
			
		||||
          onTap: bluetoothService.isPrinting
 | 
			
		||||
              ? null
 | 
			
		||||
              : () async {
 | 
			
		||||
                  // Periksa koneksi secara real-time
 | 
			
		||||
                  final isConnected = await onCheckConnection();
 | 
			
		||||
                  if (isConnected) {
 | 
			
		||||
                    onPrint();
 | 
			
		||||
                  } else {
 | 
			
		||||
                    // Coba sambungkan kembali jika ada device yang tersimpan
 | 
			
		||||
                    if (bluetoothService.connectedDevice != null) {
 | 
			
		||||
                      ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                        const SnackBar(
 | 
			
		||||
                          content: Text('Mencoba menyambungkan ke printer...')),
 | 
			
		||||
                      );
 | 
			
		||||
                      try {
 | 
			
		||||
                        bool connectResult = await bluetoothService.connectToDevice(bluetoothService.connectedDevice!);
 | 
			
		||||
                        if (!connectResult) {
 | 
			
		||||
                          throw Exception('Gagal menyambungkan ke printer');
 | 
			
		||||
                        }
 | 
			
		||||
                        // Tunggu sebentar untuk memastikan koneksi stabil
 | 
			
		||||
                        await Future.delayed(const Duration(milliseconds: 500));
 | 
			
		||||
                        // Periksa koneksi lagi
 | 
			
		||||
                        final isConnectedAfterConnect = await onCheckConnection();
 | 
			
		||||
                        if (isConnectedAfterConnect) {
 | 
			
		||||
                          onPrint();
 | 
			
		||||
                        } else {
 | 
			
		||||
                          ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                            const SnackBar(
 | 
			
		||||
                              content: Text('Gagal menyambungkan ke printer')),
 | 
			
		||||
                          );
 | 
			
		||||
                        }
 | 
			
		||||
                      } catch (e) {
 | 
			
		||||
                        ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                          SnackBar(
 | 
			
		||||
                            content: Text('Gagal menyambungkan ke printer: $e')),
 | 
			
		||||
                        );
 | 
			
		||||
                      }
 | 
			
		||||
                    } else {
 | 
			
		||||
                      ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
                        const SnackBar(
 | 
			
		||||
                          content: Text('Hubungkan printer terlebih dahulu')),
 | 
			
		||||
                      );
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
          backgroundColor: Colors.purple,
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:cashumit/extensions/double_extensions.dart';
 | 
			
		||||
 | 
			
		||||
class ReceiptTotal extends StatelessWidget {
 | 
			
		||||
  final double total;
 | 
			
		||||
 | 
			
		||||
  const ReceiptTotal({Key? key, required this.total}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Container(
 | 
			
		||||
      width: double.infinity,
 | 
			
		||||
      padding: const EdgeInsets.all(8.0),
 | 
			
		||||
      color: Colors.white,
 | 
			
		||||
      child: Column(
 | 
			
		||||
        children: [
 | 
			
		||||
          Row(
 | 
			
		||||
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
            children: [
 | 
			
		||||
              const Expanded(
 | 
			
		||||
                flex: 4,
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  'TOTAL:',
 | 
			
		||||
                  style: TextStyle(
 | 
			
		||||
                    fontSize: 16,
 | 
			
		||||
                    fontWeight: FontWeight.bold,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              Expanded(
 | 
			
		||||
                flex: 4,
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  total.toRupiah(),
 | 
			
		||||
                  style: const TextStyle(
 | 
			
		||||
                    fontSize: 16,
 | 
			
		||||
                    fontWeight: FontWeight.bold,
 | 
			
		||||
                  ),
 | 
			
		||||
                  textAlign: TextAlign.right,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue