import 'package:flutter/material.dart'; void test(BuildContext context) { final name = TextEditingController(); final barcode = TextEditingController(); final price = TextEditingController(); final priceReseller = TextEditingController(); final stock = TextEditingController(); final minStockCtrl = TextEditingController(text: '5'); final hargaPokokCtrl = TextEditingController(); final focusNodes = [ FocusNode(), FocusNode(), FocusNode(), FocusNode(), FocusNode(), FocusNode(), FocusNode(), ]; final _scrollTimers = {}; for (final node in focusNodes) { node.addListener(() { if (!node.hasFocus) { _scrollTimers[node]?.cancel(); _scrollTimers.remove(node); } else if (context.mounted) { _scrollTimers[node]?.cancel(); _scrollTimers[node] = Timer(const Duration(milliseconds: 350), () { if (node.context != null && node.context!.mounted) { Scrollable.ensureVisible( node.context!, duration: const Duration(milliseconds: 250), alignment: 0.1, ); } _scrollTimers.remove(node); }); } }); } final _scrollController = ScrollController(); showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (sheetContext) { final bottomInset = MediaQuery.of(context).viewInsets.bottom; return Container( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.92 - bottomInset, ), decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(24)), border: Border(top: BorderSide(color: Colors.grey)), ), child: Column( children: [ Container( margin: const EdgeInsets.only(top: 12), width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey, borderRadius: BorderRadius.circular(4), ), ), Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), child: Row( children: [ const Icon(Icons.add_box_outlined, color: Colors.amber), const SizedBox(width: 8), const Text('Tambah Produk', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700)), const Spacer(), IconButton( icon: const Icon(Icons.close, color: Colors.grey), onPressed: () => Navigator.pop(sheetContext), tooltip: 'Tutup', ), ], ), ), const Divider(height: 12), Expanded( child: SingleChildScrollView( controller: _scrollController, padding: EdgeInsets.fromLTRB(16, 8, 16, 16 + bottomInset), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: barcode, focusNode: focusNodes[0], decoration: InputDecoration( labelText: 'Barcode / No. Produk', prefixIcon: const Icon(Icons.qr_code_2_outlined), suffixIcon: IconButton( icon: const Icon(Icons.qr_code_scanner_outlined, color: Colors.amber), onPressed: () async { final code = await Navigator.push( context, MaterialPageRoute( builder: (_) => const Scaffold(body: Center(child: Text('Scan'))), ), ); if (code != null && context.mounted) { barcode.text = code; } }, tooltip: 'Scan barcode', )), ), const SizedBox(height: 10), TextField( controller: name, focusNode: focusNodes[1], decoration: const InputDecoration( labelText: 'Nama produk', prefixIcon: Icon(Icons.label_outline)), ), const SizedBox(height: 10), Row( children: [ Expanded( child: TextField( controller: price, focusNode: focusNodes[2], keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: 'Harga Umum', prefixText: 'Rp ', ), ), ), const SizedBox(width: 10), Expanded( child: TextField( controller: priceReseller, focusNode: focusNodes[3], keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: 'Harga Reseller', prefixText: 'Rp ', ), ), ), ], ), const SizedBox(height: 10), TextField( controller: stock, focusNode: focusNodes[4], keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: 'Stok awal', prefixIcon: Icon(Icons.numbers), ), ), const SizedBox(height: 10), TextField( controller: hargaPokokCtrl, focusNode: focusNodes[5], keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: 'Harga Pokok (modal)', prefixText: 'Rp ', helperText: 'Kosongkan jika tidak diketahui', ), ), const SizedBox(height: 10), TextField( controller: minStockCtrl, focusNode: focusNodes[6], keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: 'Stok minimum (warning)', prefixIcon: Icon(Icons.warning_outlined), helperText: 'Default: 5', ), ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: FilledButton.icon( onPressed: () async { final n = name.text.trim(); final bc = barcode.text.trim(); final pr = (price.text.isEmpty ? 0 : double.tryParse(price.text) ?? 0); final prR = (priceReseller.text.isEmpty ? pr : double.tryParse(priceReseller.text) ?? pr); final st = int.tryParse(stock.text) ?? 0; final ms = int.tryParse(minStockCtrl.text) ?? 5; final hp = (hargaPokokCtrl.text.isEmpty ? 0 : double.tryParse(hargaPokokCtrl.text) ?? 0); if (n.isEmpty || pr <= 0) return; final err = 'OK'; // await state.addProduct(n, bc, pr, prR, st, ms, hp); if (!context.mounted) return; final messenger = ScaffoldMessenger.of(context); if (err == null) { name.clear(); barcode.clear(); price.clear(); priceReseller.clear(); stock.clear(); minStockCtrl.clear(); hargaPokokCtrl.clear(); messenger.showSnackBar( const SnackBar( content: Text('Produk berhasil ditambahkan'), backgroundColor: Colors.green, ), ); Navigator.pop(sheetContext); } else { messenger.showSnackBar( SnackBar( content: Text('Gagal menambah produk: $err'), backgroundColor: Colors.red, ), ); } }, icon: const Icon(Icons.add), label: const Text('Tambah Produk'), ), ), ], ), ), ], ), ); }, ); }