h-1.flutter.4/lib/screens/master/warehouse_master_screen.dart
joe 9cec464868 feat: 各画面の AppBar に画面 ID を追加
- estimate_screen.dart: /S1. 見積入力
- invoice_screen.dart: /S2. 請求書入力
- order_screen.dart: /S3. 受発注入力
- sales_return_screen.dart: /S5. 売上返品入力
- sales_screen.dart: /S4. 売上入力(レジ)
- product_master_screen.dart: /M1. 商品マスタ
- customer_master_screen.dart: /M2. 得意先マスタ
- supplier_master_screen.dart: /M3. 仕入先マスタ
- warehouse_master_screen.dart: /M4. 倉庫マスタ
- employee_master_screen.dart: /M5. 担当者マスタ

README.md にも画面 ID マッピングを明記
2026-03-10 16:33:07 +09:00

217 lines
No EOL
8.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Version: 1.7 - 倉庫マスタ画面DB 連携実装)
import 'package:flutter/material.dart';
final _dialogKey = GlobalKey();
/// 倉庫マスタ管理画面CRUD 機能付き)
class WarehouseMasterScreen extends StatefulWidget {
const WarehouseMasterScreen({super.key});
@override
State<WarehouseMasterScreen> createState() => _WarehouseMasterScreenState();
}
class _WarehouseMasterScreenState extends State<WarehouseMasterScreen> {
List<Map<String, dynamic>> _warehouses = [];
bool _loading = true;
@override
void initState() {
super.initState();
_loadWarehouses();
}
Future<void> _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<void> _addWarehouse() async {
final warehouse = <String, dynamic>{
'id': DateTime.now().millisecondsSinceEpoch,
'name': '',
'area': '',
'address': '',
'manager': '',
'contactPhone': '',
};
final result = await showDialog<Map<String, dynamic>>(
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<void> _editWarehouse(int id) async {
final warehouse = _warehouses.firstWhere((w) => w['id'] == id);
final edited = await showDialog<Map<String, dynamic>>(
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<void> _deleteWarehouse(int id) async {
final confirmed = await showDialog<bool>(
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<String, dynamic> 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<String>(
decoration: InputDecoration(labelText: 'エリア', hintText: '北海道/東北/関東/中部/近畿/中国/四国/九州'),
value: warehouse['area'] != null ? (warehouse['area'] as String?) : null,
items: ['北海道', '東北', '関東', '中部', '近畿', '中国', '四国', '九州'].map((area) => DropdownMenuItem<String>(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;
}
}