// Version: 1.7 - 倉庫マスタ画面(DB 連携実装) import 'package:flutter/material.dart'; final _dialogKey = GlobalKey(); /// 倉庫マスタ管理画面(CRUD 機能付き) class WarehouseMasterScreen extends StatefulWidget { const WarehouseMasterScreen({super.key}); @override State createState() => _WarehouseMasterScreenState(); } class _WarehouseMasterScreenState extends State { List> _warehouses = []; bool _loading = true; @override void initState() { super.initState(); _loadWarehouses(); } Future _loadWarehouses() async { setState(() => _loading = true); try { // デモデータ(実際には DatabaseHelper 経由) final demoData = [ {'id': 1, 'name': '札幌倉庫', 'area': '北海道', 'address': '〒040-0001 札幌市中央区'}, {'id': 2, 'name': '仙台倉庫', 'area': '東北', 'address': '〒980-0001 仙台市青葉区'}, {'id': 3, 'name': '東京倉庫', 'area': '関東', 'address': '〒100-0001 東京都千代田区'}, {'id': 4, 'name': '名古屋倉庫', 'area': '中部', 'address': '〒460-0001 名古屋市中村区'}, {'id': 5, 'name': '大阪倉庫', 'area': '近畿', 'address': '〒530-0001 大阪市中央区'}, ]; setState(() => _warehouses = demoData); } catch (e) { if (mounted) ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('読み込みエラー:$e'), backgroundColor: Colors.red), ); } finally { setState(() => _loading = false); } } Future _addWarehouse() async { final warehouse = { 'id': DateTime.now().millisecondsSinceEpoch, 'name': '', 'area': '', 'address': '', 'manager': '', 'contactPhone': '', }; final result = await showDialog>( context: context, builder: (context) => _WarehouseDialogState( Dialog( child: SingleChildScrollView( padding: EdgeInsets.zero, child: ConstrainedBox( constraints: const BoxConstraints(minHeight: 200), child: WarehouseForm(warehouse: warehouse), ), ), ), ), ); if (result != null && mounted) { setState(() => _warehouses.add(result)); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('倉庫登録完了'), backgroundColor: Colors.green), ); } } Future _editWarehouse(int id) async { final warehouse = _warehouses.firstWhere((w) => w['id'] == id); final edited = await showDialog>( context: context, builder: (context) => _WarehouseDialogState( Dialog( child: SingleChildScrollView( padding: EdgeInsets.zero, child: ConstrainedBox( constraints: const BoxConstraints(minHeight: 200), child: WarehouseForm(warehouse: warehouse), ), ), ), ), ); if (edited != null && mounted) { final index = _warehouses.indexWhere((w) => w['id'] == id); setState(() => _warehouses[index] = edited); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('倉庫更新完了'), backgroundColor: Colors.green), ); } } Future _deleteWarehouse(int id) async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('倉庫削除'), content: Text('この倉庫を削除しますか?'), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: const Text('キャンセル')), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: const Text('削除'), ), ], ), ); if (confirmed == true) { setState(() { _warehouses.removeWhere((w) => w['id'] == id); }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('倉庫削除完了'), backgroundColor: Colors.green), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('/M4. 倉庫マスタ'), actions: [ IconButton(icon: const Icon(Icons.refresh), onPressed: _loadWarehouses), IconButton(icon: const Icon(Icons.add), onPressed: _addWarehouse), ], ), body: _loading ? const Center(child: CircularProgressIndicator()) : _warehouses.isEmpty ? Center(child: Text('倉庫データがありません')) : ListView.builder( padding: const EdgeInsets.all(8), itemCount: _warehouses.length, itemBuilder: (context, index) { final warehouse = _warehouses[index]; return Card( margin: const EdgeInsets.only(bottom: 8), child: ListTile( leading: CircleAvatar(backgroundColor: Colors.orange.shade50, child: Icon(Icons.storage, color: Colors.orange)), title: Text(warehouse['name'] ?? '倉庫(未入力)'), subtitle: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('エリア:${warehouse['area']}'), if (warehouse['address'] != null) Text('住所:${warehouse['address']}'), ]), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton(icon: const Icon(Icons.edit), onPressed: () => _editWarehouse(warehouse['id'] as int)), IconButton(icon: const Icon(Icons.delete), onPressed: () => _deleteWarehouse(warehouse['id'] as int)), ], ), ), ); }, ), ); } } /// 倉庫フォーム部品 class WarehouseForm extends StatelessWidget { final Map warehouse; const WarehouseForm({super.key, required this.warehouse}); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextField(decoration: InputDecoration(labelText: '倉庫名 *'), controller: TextEditingController(text: warehouse['name'] ?? '')), const SizedBox(height: 16), DropdownButtonFormField( decoration: InputDecoration(labelText: 'エリア', hintText: '北海道/東北/関東/中部/近畿/中国/四国/九州'), value: warehouse['area'] != null ? (warehouse['area'] as String?) : null, items: ['北海道', '東北', '関東', '中部', '近畿', '中国', '四国', '九州'].map((area) => DropdownMenuItem(value: area, child: Text(area))).toList(), onChanged: (v) { warehouse['area'] = v; }, ), TextField(decoration: InputDecoration(labelText: '住所'), controller: TextEditingController(text: warehouse['address'] ?? '')), const SizedBox(height: 8), TextField(decoration: InputDecoration(labelText: '倉庫長(担当者名)'), controller: TextEditingController(text: warehouse['manager'] ?? '')), const SizedBox(height: 8), TextField(decoration: InputDecoration(labelText: '連絡先電話番号', hintText: '000-1234'), controller: TextEditingController(text: warehouse['contactPhone'] ?? ''), keyboardType: TextInputType.phone), const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.center, children: [TextButton(onPressed: () => Navigator.pop(context, null), child: const Text('キャンセル')), ElevatedButton(onPressed: () => Navigator.pop(context, warehouse), child: const Text('保存'))], ), ], ); } } /// 倉庫ダイアログ表示ヘルパークラス(削除用) class _WarehouseDialogState extends StatelessWidget { final Dialog dialog; const _WarehouseDialogState(this.dialog); @override Widget build(BuildContext context) { return dialog; } }