Implement printing status card and fix transaction screen issues
							parent
							
								
									9fe79d5ab7
								
							
						
					
					
						commit
						a2eedc8efc
					
				| 
						 | 
				
			
			@ -5,6 +5,35 @@ plugins {
 | 
			
		|||
    id "dev.flutter.flutter-gradle-plugin"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def localProperties = new Properties()
 | 
			
		||||
def localPropertiesFile = rootProject.file('local.properties')
 | 
			
		||||
if (localPropertiesFile.exists()) {
 | 
			
		||||
    localPropertiesFile.withReader('UTF-8') { reader ->
 | 
			
		||||
        localProperties.load(reader)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
 | 
			
		||||
if (flutterRoot == null) {
 | 
			
		||||
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
 | 
			
		||||
if (flutterVersionCode == null) {
 | 
			
		||||
    flutterVersionCode = '1'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
 | 
			
		||||
if (flutterVersionName == null) {
 | 
			
		||||
    flutterVersionName = '1.0'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def keystoreProperties = new Properties()
 | 
			
		||||
def keystorePropertiesFile = rootProject.file('key.properties')
 | 
			
		||||
if (keystorePropertiesFile.exists()) {
 | 
			
		||||
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    namespace = "com.example.cashumit"
 | 
			
		||||
    compileSdk = 35
 | 
			
		||||
| 
						 | 
				
			
			@ -26,15 +55,22 @@ android {
 | 
			
		|||
        // For more information, see: https://flutter.dev/to/review-gradle-config.
 | 
			
		||||
        minSdk = flutter.minSdkVersion
 | 
			
		||||
        targetSdk = flutter.targetSdkVersion
 | 
			
		||||
        versionCode = flutter.versionCode
 | 
			
		||||
        versionCode = flutter.versionCode.toInteger()
 | 
			
		||||
        versionName = flutter.versionName
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    signingConfigs {
 | 
			
		||||
        release {
 | 
			
		||||
            keyAlias keystoreProperties['keyAlias']
 | 
			
		||||
            keyPassword keystoreProperties['keyPassword']
 | 
			
		||||
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
 | 
			
		||||
            storePassword keystoreProperties['storePassword']
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    buildTypes {
 | 
			
		||||
        release {
 | 
			
		||||
            // TODO: Add your own signing config for the release build.
 | 
			
		||||
            // Signing with the debug keys for now, so `flutter run --release` works.
 | 
			
		||||
            signingConfig = signingConfigs.debug
 | 
			
		||||
            signingConfig signingConfigs.release
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ import 'package:cashumit/services/firefly_api_service.dart';
 | 
			
		|||
import 'package:bluetooth_print/bluetooth_print.dart';
 | 
			
		||||
import 'package:bluetooth_print/bluetooth_print_model.dart';
 | 
			
		||||
import 'package:cashumit/utils/currency_format.dart';
 | 
			
		||||
import 'package:cashumit/widgets/printing_status_card.dart';
 | 
			
		||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
			
		||||
 | 
			
		||||
class TransactionScreen extends StatefulWidget {
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +41,9 @@ class _TransactionScreenState extends State<TransactionScreen> {
 | 
			
		|||
  String? _destinationAccountName;
 | 
			
		||||
  bool _isLoadingAccounts = false;
 | 
			
		||||
  
 | 
			
		||||
  // Printing status
 | 
			
		||||
  bool _isPrinting = false;
 | 
			
		||||
  
 | 
			
		||||
  // Controllers for manual account input
 | 
			
		||||
  final TextEditingController _sourceAccountController = TextEditingController();
 | 
			
		||||
  final TextEditingController _destinationAccountController = TextEditingController();
 | 
			
		||||
| 
						 | 
				
			
			@ -451,6 +455,11 @@ class _TransactionScreenState extends State<TransactionScreen> {
 | 
			
		|||
 | 
			
		||||
    if (confirmed != true) return;
 | 
			
		||||
 | 
			
		||||
    // Tampilkan status printing
 | 
			
		||||
    setState(() {
 | 
			
		||||
      _isPrinting = true;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Cetak struk
 | 
			
		||||
    final printService = PrintService();
 | 
			
		||||
    final printed = await printService.printTransaction(
 | 
			
		||||
| 
						 | 
				
			
			@ -459,6 +468,11 @@ class _TransactionScreenState extends State<TransactionScreen> {
 | 
			
		|||
      'Jl. Merdeka No. 123, Jakarta',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Sembunyikan status printing
 | 
			
		||||
    setState(() {
 | 
			
		||||
      _isPrinting = false;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!printed) {
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
| 
						 | 
				
			
			@ -507,224 +521,236 @@ class _TransactionScreenState extends State<TransactionScreen> {
 | 
			
		|||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: const Text('Aplikasi Kasir'),
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            onPressed: _loadAccounts,
 | 
			
		||||
            icon: _isLoadingAccounts
 | 
			
		||||
                ? const CircularProgressIndicator()
 | 
			
		||||
                : const Icon(Icons.refresh),
 | 
			
		||||
          ),
 | 
			
		||||
          IconButton(
 | 
			
		||||
            onPressed: _startScan,
 | 
			
		||||
            icon: _isScanning
 | 
			
		||||
                ? const CircularProgressIndicator()
 | 
			
		||||
                : const Icon(Icons.bluetooth),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      body: Column(
 | 
			
		||||
        children: [
 | 
			
		||||
          // Pencarian item
 | 
			
		||||
          Padding(
 | 
			
		||||
            padding: const EdgeInsets.all(8.0),
 | 
			
		||||
            child: TextField(
 | 
			
		||||
              controller: _searchController,
 | 
			
		||||
              decoration: const InputDecoration(
 | 
			
		||||
                labelText: 'Cari barang...',
 | 
			
		||||
                prefixIcon: Icon(Icons.search),
 | 
			
		||||
                border: OutlineInputBorder(),
 | 
			
		||||
    return Stack(
 | 
			
		||||
      children: [
 | 
			
		||||
        Scaffold(
 | 
			
		||||
          appBar: AppBar(
 | 
			
		||||
            title: const Text('Aplikasi Kasir'),
 | 
			
		||||
            actions: [
 | 
			
		||||
              IconButton(
 | 
			
		||||
                onPressed: _loadAccounts,
 | 
			
		||||
                icon: _isLoadingAccounts
 | 
			
		||||
                    ? const CircularProgressIndicator()
 | 
			
		||||
                    : const Icon(Icons.refresh),
 | 
			
		||||
              ),
 | 
			
		||||
              onChanged: _searchItems,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          // Dropdown printer
 | 
			
		||||
          if (_devices.isNotEmpty)
 | 
			
		||||
            Padding(
 | 
			
		||||
              padding: const EdgeInsets.symmetric(horizontal: 8.0),
 | 
			
		||||
              child: DropdownButton<BluetoothDevice>(
 | 
			
		||||
                hint: const Text('Pilih Printer'),
 | 
			
		||||
                value: _selectedDevice,
 | 
			
		||||
                items: _devices
 | 
			
		||||
                    .map((device) => DropdownMenuItem(
 | 
			
		||||
                          value: device,
 | 
			
		||||
                          child: Text(device.name ?? device.address ?? '-'),
 | 
			
		||||
                        ))
 | 
			
		||||
                    .toList(),
 | 
			
		||||
                onChanged: (device) {
 | 
			
		||||
                  setState(() {
 | 
			
		||||
                    _selectedDevice = device;
 | 
			
		||||
                  });
 | 
			
		||||
                },
 | 
			
		||||
                isExpanded: true,
 | 
			
		||||
              IconButton(
 | 
			
		||||
                onPressed: _startScan,
 | 
			
		||||
                icon: _isScanning
 | 
			
		||||
                    ? const CircularProgressIndicator()
 | 
			
		||||
                    : const Icon(Icons.bluetooth),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          // Dropdown metode pembayaran
 | 
			
		||||
          Padding(
 | 
			
		||||
            padding: const EdgeInsets.all(8.0),
 | 
			
		||||
            child: DropdownButton<String>(
 | 
			
		||||
              value: _paymentMethod,
 | 
			
		||||
              items: ['Tunai', 'Debit', 'Kredit', 'QRIS']
 | 
			
		||||
                  .map((method) => DropdownMenuItem(
 | 
			
		||||
                        value: method,
 | 
			
		||||
                        child: Text(method),
 | 
			
		||||
                      ))
 | 
			
		||||
                  .toList(),
 | 
			
		||||
              onChanged: (method) {
 | 
			
		||||
                setState(() {
 | 
			
		||||
                  _paymentMethod = method!;
 | 
			
		||||
                });
 | 
			
		||||
              },
 | 
			
		||||
              isExpanded: true,
 | 
			
		||||
            ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          // Transaction settings directly in this screen
 | 
			
		||||
          Card(
 | 
			
		||||
            margin: const EdgeInsets.all(8.0),
 | 
			
		||||
            child: Padding(
 | 
			
		||||
              padding: const EdgeInsets.all(12.0),
 | 
			
		||||
              child: Column(
 | 
			
		||||
                crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                children: [
 | 
			
		||||
                  const Text(
 | 
			
		||||
                    'Pengaturan Transaksi:',
 | 
			
		||||
                    style: TextStyle(fontWeight: FontWeight.bold),
 | 
			
		||||
          body: Column(
 | 
			
		||||
            children: [
 | 
			
		||||
              // Pencarian item
 | 
			
		||||
              Padding(
 | 
			
		||||
                padding: const EdgeInsets.all(8.0),
 | 
			
		||||
                child: TextField(
 | 
			
		||||
                  controller: _searchController,
 | 
			
		||||
                  decoration: const InputDecoration(
 | 
			
		||||
                    labelText: 'Cari barang...',
 | 
			
		||||
                    prefixIcon: Icon(Icons.search),
 | 
			
		||||
                    border: OutlineInputBorder(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  const SizedBox(height: 8),
 | 
			
		||||
                  // Date picker
 | 
			
		||||
                  const Text('Tanggal Transaksi:'),
 | 
			
		||||
                  ListTile(
 | 
			
		||||
                    title: Text(
 | 
			
		||||
                      '${_transactionDate.day}/${_transactionDate.month}/${_transactionDate.year}',
 | 
			
		||||
                    ),
 | 
			
		||||
                    trailing: const Icon(Icons.calendar_today),
 | 
			
		||||
                    onTap: () => _selectDate(context),
 | 
			
		||||
                  ),
 | 
			
		||||
                  const SizedBox(height: 8),
 | 
			
		||||
                  // Source account
 | 
			
		||||
                  const Text('Akun Sumber:'),
 | 
			
		||||
                  _sourceAccountId != null
 | 
			
		||||
                      ? Card(
 | 
			
		||||
                          child: ListTile(
 | 
			
		||||
                            title: Text(_sourceAccountName ?? ''),
 | 
			
		||||
                            trailing: const Icon(Icons.edit),
 | 
			
		||||
                            onTap: _selectSourceAccount,
 | 
			
		||||
                          ),
 | 
			
		||||
                        )
 | 
			
		||||
                      : Column(
 | 
			
		||||
                          children: [
 | 
			
		||||
                            TextField(
 | 
			
		||||
                              controller: _sourceAccountController,
 | 
			
		||||
                              decoration: const InputDecoration(
 | 
			
		||||
                                labelText: 'Nama Akun Sumber',
 | 
			
		||||
                                hintText: 'Ketik nama akun atau pilih dari daftar',
 | 
			
		||||
                              ),
 | 
			
		||||
                              onChanged: _onSourceAccountChanged,
 | 
			
		||||
                            ),
 | 
			
		||||
                            ElevatedButton(
 | 
			
		||||
                              onPressed: _selectSourceAccount,
 | 
			
		||||
                              child: const Text('Pilih Akun Sumber dari Daftar'),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ],
 | 
			
		||||
                        ),
 | 
			
		||||
                  const SizedBox(height: 8),
 | 
			
		||||
                  // Destination account
 | 
			
		||||
                  const Text('Akun Tujuan:'),
 | 
			
		||||
                  _destinationAccountId != null
 | 
			
		||||
                      ? Card(
 | 
			
		||||
                          child: ListTile(
 | 
			
		||||
                            title: Text(_destinationAccountName ?? ''),
 | 
			
		||||
                            trailing: const Icon(Icons.edit),
 | 
			
		||||
                            onTap: _selectDestinationAccount,
 | 
			
		||||
                          ),
 | 
			
		||||
                        )
 | 
			
		||||
                      : Column(
 | 
			
		||||
                          children: [
 | 
			
		||||
                            TextField(
 | 
			
		||||
                              controller: _destinationAccountController,
 | 
			
		||||
                              decoration: const InputDecoration(
 | 
			
		||||
                                labelText: 'Nama Akun Tujuan',
 | 
			
		||||
                                hintText: 'Ketik nama akun atau pilih dari daftar',
 | 
			
		||||
                              ),
 | 
			
		||||
                              onChanged: _onDestinationAccountChanged,
 | 
			
		||||
                            ),
 | 
			
		||||
                            ElevatedButton(
 | 
			
		||||
                              onPressed: _selectDestinationAccount,
 | 
			
		||||
                              child: const Text('Pilih Akun Tujuan dari Daftar'),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ],
 | 
			
		||||
                        ),
 | 
			
		||||
                  if (_isLoadingAccounts)
 | 
			
		||||
                    const Padding(
 | 
			
		||||
                      padding: EdgeInsets.all(8.0),
 | 
			
		||||
                      child: Center(child: CircularProgressIndicator()),
 | 
			
		||||
                    ),
 | 
			
		||||
                ],
 | 
			
		||||
                  onChanged: _searchItems,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          // Total
 | 
			
		||||
          Container(
 | 
			
		||||
            padding: const EdgeInsets.all(16.0),
 | 
			
		||||
            color: Colors.grey[200],
 | 
			
		||||
            child: Row(
 | 
			
		||||
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
              children: [
 | 
			
		||||
                const Text(
 | 
			
		||||
                  'Total:',
 | 
			
		||||
                  style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
 | 
			
		||||
              // Dropdown printer
 | 
			
		||||
              if (_devices.isNotEmpty)
 | 
			
		||||
                Padding(
 | 
			
		||||
                  padding: const EdgeInsets.symmetric(horizontal: 8.0),
 | 
			
		||||
                  child: DropdownButton<BluetoothDevice>(
 | 
			
		||||
                    hint: const Text('Pilih Printer'),
 | 
			
		||||
                    value: _selectedDevice,
 | 
			
		||||
                    items: _devices
 | 
			
		||||
                        .map((device) => DropdownMenuItem(
 | 
			
		||||
                              value: device,
 | 
			
		||||
                              child: Text(device.name ?? device.address ?? '-'),
 | 
			
		||||
                            ))
 | 
			
		||||
                        .toList(),
 | 
			
		||||
                    onChanged: (device) {
 | 
			
		||||
                      setState(() {
 | 
			
		||||
                        _selectedDevice = device;
 | 
			
		||||
                      });
 | 
			
		||||
                    },
 | 
			
		||||
                    isExpanded: true,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                Text(
 | 
			
		||||
                  'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(_total)}',
 | 
			
		||||
                  style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
 | 
			
		||||
              // Dropdown metode pembayaran
 | 
			
		||||
              Padding(
 | 
			
		||||
                padding: const EdgeInsets.all(8.0),
 | 
			
		||||
                child: DropdownButton<String>(
 | 
			
		||||
                  value: _paymentMethod,
 | 
			
		||||
                  items: ['Tunai', 'Debit', 'Kredit', 'QRIS']
 | 
			
		||||
                      .map((method) => DropdownMenuItem(
 | 
			
		||||
                            value: method,
 | 
			
		||||
                            child: Text(method),
 | 
			
		||||
                          ))
 | 
			
		||||
                      .toList(),
 | 
			
		||||
                  onChanged: (method) {
 | 
			
		||||
                    setState(() {
 | 
			
		||||
                      _paymentMethod = method!;
 | 
			
		||||
                    });
 | 
			
		||||
                  },
 | 
			
		||||
                  isExpanded: true,
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          // TabBar untuk navigasi
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: DefaultTabController(
 | 
			
		||||
              length: 2,
 | 
			
		||||
              child: Column(
 | 
			
		||||
                children: [
 | 
			
		||||
                  const TabBar(
 | 
			
		||||
                    tabs: [
 | 
			
		||||
                      Tab(text: 'Barang'),
 | 
			
		||||
                      Tab(text: 'Keranjang'),
 | 
			
		||||
              ),
 | 
			
		||||
              // Transaction settings directly in this screen
 | 
			
		||||
              Card(
 | 
			
		||||
                margin: const EdgeInsets.all(8.0),
 | 
			
		||||
                child: Padding(
 | 
			
		||||
                  padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                  child: Column(
 | 
			
		||||
                    crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                    children: [
 | 
			
		||||
                      const Text(
 | 
			
		||||
                        'Pengaturan Transaksi:',
 | 
			
		||||
                        style: TextStyle(fontWeight: FontWeight.bold),
 | 
			
		||||
                      ),
 | 
			
		||||
                      const SizedBox(height: 8),
 | 
			
		||||
                      // Date picker
 | 
			
		||||
                      const Text('Tanggal Transaksi:'),
 | 
			
		||||
                      ListTile(
 | 
			
		||||
                        title: Text(
 | 
			
		||||
                          '${_transactionDate.day}/${_transactionDate.month}/${_transactionDate.year}',
 | 
			
		||||
                        ),
 | 
			
		||||
                        trailing: const Icon(Icons.calendar_today),
 | 
			
		||||
                        onTap: () => _selectDate(context),
 | 
			
		||||
                      ),
 | 
			
		||||
                      const SizedBox(height: 8),
 | 
			
		||||
                      // Source account
 | 
			
		||||
                      const Text('Akun Sumber:'),
 | 
			
		||||
                      _sourceAccountId != null
 | 
			
		||||
                          ? Card(
 | 
			
		||||
                              child: ListTile(
 | 
			
		||||
                                title: Text(_sourceAccountName ?? ''),
 | 
			
		||||
                                trailing: const Icon(Icons.edit),
 | 
			
		||||
                                onTap: _selectSourceAccount,
 | 
			
		||||
                              ),
 | 
			
		||||
                            )
 | 
			
		||||
                          : Column(
 | 
			
		||||
                              children: [
 | 
			
		||||
                                TextField(
 | 
			
		||||
                                  controller: _sourceAccountController,
 | 
			
		||||
                                  decoration: const InputDecoration(
 | 
			
		||||
                                    labelText: 'Nama Akun Sumber',
 | 
			
		||||
                                    hintText: 'Ketik nama akun atau pilih dari daftar',
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  onChanged: _onSourceAccountChanged,
 | 
			
		||||
                                ),
 | 
			
		||||
                                ElevatedButton(
 | 
			
		||||
                                  onPressed: _selectSourceAccount,
 | 
			
		||||
                                  child: const Text('Pilih Akun Sumber dari Daftar'),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ],
 | 
			
		||||
                            ),
 | 
			
		||||
                      const SizedBox(height: 8),
 | 
			
		||||
                      // Destination account
 | 
			
		||||
                      const Text('Akun Tujuan:'),
 | 
			
		||||
                      _destinationAccountId != null
 | 
			
		||||
                          ? Card(
 | 
			
		||||
                              child: ListTile(
 | 
			
		||||
                                title: Text(_destinationAccountName ?? ''),
 | 
			
		||||
                                trailing: const Icon(Icons.edit),
 | 
			
		||||
                                onTap: _selectDestinationAccount,
 | 
			
		||||
                              ),
 | 
			
		||||
                            )
 | 
			
		||||
                          : Column(
 | 
			
		||||
                              children: [
 | 
			
		||||
                                TextField(
 | 
			
		||||
                                  controller: _destinationAccountController,
 | 
			
		||||
                                  decoration: const InputDecoration(
 | 
			
		||||
                                    labelText: 'Nama Akun Tujuan',
 | 
			
		||||
                                    hintText: 'Ketik nama akun atau pilih dari daftar',
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  onChanged: _onDestinationAccountChanged,
 | 
			
		||||
                                ),
 | 
			
		||||
                                ElevatedButton(
 | 
			
		||||
                                  onPressed: _selectDestinationAccount,
 | 
			
		||||
                                  child: const Text('Pilih Akun Tujuan dari Daftar'),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ],
 | 
			
		||||
                            ),
 | 
			
		||||
                      if (_isLoadingAccounts)
 | 
			
		||||
                        const Padding(
 | 
			
		||||
                          padding: EdgeInsets.all(8.0),
 | 
			
		||||
                          child: Center(child: CircularProgressIndicator()),
 | 
			
		||||
                        ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                  Expanded(
 | 
			
		||||
                    child: TabBarView(
 | 
			
		||||
                      children: [
 | 
			
		||||
                        // Tab Barang
 | 
			
		||||
                        _buildItemsTab(),
 | 
			
		||||
                        // Tab Keranjang
 | 
			
		||||
                        _buildCartTab(),
 | 
			
		||||
                      ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              // Total
 | 
			
		||||
              Container(
 | 
			
		||||
                padding: const EdgeInsets.all(16.0),
 | 
			
		||||
                color: Colors.grey[200],
 | 
			
		||||
                child: Row(
 | 
			
		||||
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
                  children: [
 | 
			
		||||
                    const Text(
 | 
			
		||||
                      'Total:',
 | 
			
		||||
                      style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
 | 
			
		||||
                    ),
 | 
			
		||||
                    Text(
 | 
			
		||||
                      'Rp ${CurrencyFormat.formatRupiahWithoutSymbol(_total)}',
 | 
			
		||||
                      style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              // TabBar untuk navigasi
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: DefaultTabController(
 | 
			
		||||
                  length: 2,
 | 
			
		||||
                  child: Column(
 | 
			
		||||
                    children: [
 | 
			
		||||
                      const TabBar(
 | 
			
		||||
                        tabs: [
 | 
			
		||||
                          Tab(text: 'Barang'),
 | 
			
		||||
                          Tab(text: 'Keranjang'),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                      Expanded(
 | 
			
		||||
                        child: TabBarView(
 | 
			
		||||
                          children: [
 | 
			
		||||
                            // Tab Barang
 | 
			
		||||
                            _buildItemsTab(),
 | 
			
		||||
                            // Tab Keranjang
 | 
			
		||||
                            _buildCartTab(),
 | 
			
		||||
                          ],
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          bottomNavigationBar: Padding(
 | 
			
		||||
            padding: const EdgeInsets.all(8.0),
 | 
			
		||||
            child: ElevatedButton(
 | 
			
		||||
              onPressed: _completeTransaction,
 | 
			
		||||
              style: ElevatedButton.styleFrom(
 | 
			
		||||
                padding: const EdgeInsets.all(16.0),
 | 
			
		||||
                backgroundColor: Colors.green,
 | 
			
		||||
              ),
 | 
			
		||||
              child: const Text(
 | 
			
		||||
                'BAYAR',
 | 
			
		||||
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      bottomNavigationBar: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(8.0),
 | 
			
		||||
        child: ElevatedButton(
 | 
			
		||||
          onPressed: _completeTransaction,
 | 
			
		||||
          style: ElevatedButton.styleFrom(
 | 
			
		||||
            padding: const EdgeInsets.all(16.0),
 | 
			
		||||
            backgroundColor: Colors.green,
 | 
			
		||||
          ),
 | 
			
		||||
          child: const Text(
 | 
			
		||||
            'BAYAR',
 | 
			
		||||
            style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
        PrintingStatusCard(
 | 
			
		||||
          isVisible: _isPrinting,
 | 
			
		||||
          onDismiss: () {
 | 
			
		||||
            setState(() {
 | 
			
		||||
              _isPrinting = false;
 | 
			
		||||
            });
 | 
			
		||||
          },
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,10 +5,10 @@ class PrintingStatusCard extends StatefulWidget {
 | 
			
		|||
  final VoidCallback? onDismiss;
 | 
			
		||||
 | 
			
		||||
  const PrintingStatusCard({
 | 
			
		||||
    Key? key,
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.isVisible,
 | 
			
		||||
    this.onDismiss,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
  }) : super();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<PrintingStatusCard> createState() => _PrintingStatusCardState();
 | 
			
		||||
| 
						 | 
				
			
			@ -71,81 +71,84 @@ class _PrintingStatusCardState extends State<PrintingStatusCard>
 | 
			
		|||
    return AnimatedBuilder(
 | 
			
		||||
      animation: _controller,
 | 
			
		||||
      builder: (context, child) {
 | 
			
		||||
        return Positioned(
 | 
			
		||||
          top: 100,
 | 
			
		||||
          left: MediaQuery.of(context).size.width * 0.1,
 | 
			
		||||
          right: MediaQuery.of(context).size.width * 0.1,
 | 
			
		||||
          child: IgnorePointer(
 | 
			
		||||
            ignoring: !widget.isVisible,
 | 
			
		||||
            child: Opacity(
 | 
			
		||||
              opacity: _fadeAnimation.value,
 | 
			
		||||
              child: Transform.scale(
 | 
			
		||||
                scale: _scaleAnimation.value,
 | 
			
		||||
                child: Card(
 | 
			
		||||
                  elevation: 12,
 | 
			
		||||
                  shape: RoundedRectangleBorder(
 | 
			
		||||
                    borderRadius: BorderRadius.circular(20),
 | 
			
		||||
                  ),
 | 
			
		||||
                  child: Container(
 | 
			
		||||
                    padding: const EdgeInsets.all(20),
 | 
			
		||||
                    decoration: BoxDecoration(
 | 
			
		||||
                      borderRadius: BorderRadius.circular(20),
 | 
			
		||||
                      gradient: const LinearGradient(
 | 
			
		||||
                        begin: Alignment.topLeft,
 | 
			
		||||
                        end: Alignment.bottomRight,
 | 
			
		||||
                        colors: [
 | 
			
		||||
                          Color(0xFF6A11CB),
 | 
			
		||||
                          Color(0xFF2575FC),
 | 
			
		||||
        return Visibility(
 | 
			
		||||
          visible: widget.isVisible,
 | 
			
		||||
          child: Positioned(
 | 
			
		||||
            top: 100,
 | 
			
		||||
            left: MediaQuery.of(context).size.width * 0.1,
 | 
			
		||||
            right: MediaQuery.of(context).size.width * 0.1,
 | 
			
		||||
            child: IgnorePointer(
 | 
			
		||||
              ignoring: !_fadeAnimation.value.isNaN && _fadeAnimation.value < 0.5,
 | 
			
		||||
              child: Opacity(
 | 
			
		||||
                  opacity: _fadeAnimation.value,
 | 
			
		||||
                  child: Transform.scale(
 | 
			
		||||
                    scale: _scaleAnimation.value,
 | 
			
		||||
                    child: Card(
 | 
			
		||||
                      elevation: 12,
 | 
			
		||||
                      shape: RoundedRectangleBorder(
 | 
			
		||||
                        borderRadius: BorderRadius.circular(20),
 | 
			
		||||
                      ),
 | 
			
		||||
                    child: Container(
 | 
			
		||||
                      padding: const EdgeInsets.all(20),
 | 
			
		||||
                      decoration: BoxDecoration(
 | 
			
		||||
                        borderRadius: BorderRadius.circular(20),
 | 
			
		||||
                        gradient: const LinearGradient(
 | 
			
		||||
                          begin: Alignment.topLeft,
 | 
			
		||||
                          end: Alignment.bottomRight,
 | 
			
		||||
                          colors: [
 | 
			
		||||
                            Color(0xFF6A11CB),
 | 
			
		||||
                            Color(0xFF2575FC),
 | 
			
		||||
                          ],
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      child: Row(
 | 
			
		||||
                        mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                        children: [
 | 
			
		||||
                          const SizedBox(width: 8),
 | 
			
		||||
                          const Icon(
 | 
			
		||||
                            Icons.print,
 | 
			
		||||
                            color: Colors.white,
 | 
			
		||||
                            size: 36,
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(width: 16),
 | 
			
		||||
                          Expanded(
 | 
			
		||||
                            child: Column(
 | 
			
		||||
                              crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                              mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                              children: [
 | 
			
		||||
                                const Text(
 | 
			
		||||
                                  'Mencetak Struk',
 | 
			
		||||
                                  style: TextStyle(
 | 
			
		||||
                                    fontSize: 20,
 | 
			
		||||
                                    fontWeight: FontWeight.bold,
 | 
			
		||||
                                    color: Colors.white,
 | 
			
		||||
                                  ),
 | 
			
		||||
                                ),
 | 
			
		||||
                                const SizedBox(height: 4),
 | 
			
		||||
                                const Text(
 | 
			
		||||
                                  'Mohon tunggu...',
 | 
			
		||||
                                  style: TextStyle(
 | 
			
		||||
                                    fontSize: 16,
 | 
			
		||||
                                    color: Colors.white70,
 | 
			
		||||
                                  ),
 | 
			
		||||
                                ),
 | 
			
		||||
                                const SizedBox(height: 12),
 | 
			
		||||
                                LinearProgressIndicator(
 | 
			
		||||
                                  backgroundColor: Colors.white30,
 | 
			
		||||
                                  color: Colors.white,
 | 
			
		||||
                                  minHeight: 6,
 | 
			
		||||
                                  value: null,
 | 
			
		||||
                                ),
 | 
			
		||||
                              ],
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                          IconButton(
 | 
			
		||||
                            icon: const Icon(Icons.close, color: Colors.white70),
 | 
			
		||||
                            onPressed: widget.onDismiss,
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    child: Row(
 | 
			
		||||
                      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                      children: [
 | 
			
		||||
                        const SizedBox(width: 8),
 | 
			
		||||
                        const Icon(
 | 
			
		||||
                          Icons.print,
 | 
			
		||||
                          color: Colors.white,
 | 
			
		||||
                          size: 36,
 | 
			
		||||
                        ),
 | 
			
		||||
                        const SizedBox(width: 16),
 | 
			
		||||
                        Expanded(
 | 
			
		||||
                          child: Column(
 | 
			
		||||
                            crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                            mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              const Text(
 | 
			
		||||
                                'Mencetak Struk',
 | 
			
		||||
                                style: TextStyle(
 | 
			
		||||
                                  fontSize: 20,
 | 
			
		||||
                                  fontWeight: FontWeight.bold,
 | 
			
		||||
                                  color: Colors.white,
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                              const SizedBox(height: 4),
 | 
			
		||||
                              const Text(
 | 
			
		||||
                                'Mohon tunggu...',
 | 
			
		||||
                                style: TextStyle(
 | 
			
		||||
                                  fontSize: 16,
 | 
			
		||||
                                  color: Colors.white70,
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                              const SizedBox(height: 12),
 | 
			
		||||
                              LinearProgressIndicator(
 | 
			
		||||
                                backgroundColor: Colors.white30,
 | 
			
		||||
                                color: Colors.white,
 | 
			
		||||
                                minHeight: 6,
 | 
			
		||||
                                value: null,
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                        IconButton(
 | 
			
		||||
                          icon: const Icon(Icons.close, color: Colors.white70),
 | 
			
		||||
                          onPressed: widget.onDismiss,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue