import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../models/sake_item.dart'; import '../services/pricing_helper.dart'; import '../theme/app_colors.dart'; /// 価格設定ダイアログ /// /// 「一合の価格入力」→「提供サイズ選択」→「お品書き掲載価格」の /// シンプルなフローで価格を設定します。 /// /// 使用例: /// ```dart /// showDialog( /// context: context, /// builder: (context) => SakePriceDialog( /// sakeItem: item, /// onSave: (basePrice, variants) { /// // 保存処理 /// }, /// ), /// ); /// ``` class SakePriceDialog extends StatefulWidget { final SakeItem sakeItem; final Function(int basePrice, Map variants) onSave; const SakePriceDialog({ super.key, required this.sakeItem, required this.onSave, }); @override State createState() => _SakePriceDialogState(); } class _SakePriceDialogState extends State { final TextEditingController _basePriceController = TextEditingController(); final FocusNode _basePriceFocus = FocusNode(); int? _basePrice; // 一合価格 Map _variants = {}; // 選択されたサイズと価格 @override void initState() { super.initState(); // 既存の価格データを読み込む if (widget.sakeItem.userData.price != null) { _basePrice = widget.sakeItem.userData.price!; _basePriceController.text = PricingHelper.formatPrice(_basePrice!); } // 既存のバリエーションを読み込む if (widget.sakeItem.userData.priceVariants != null) { _variants = Map.from(widget.sakeItem.userData.priceVariants!); } // フォーカスアウト時にカンマ区切りをフォーマット _basePriceFocus.addListener(() { if (!_basePriceFocus.hasFocus && _basePrice != null) { setState(() { _basePriceController.text = PricingHelper.formatPrice(_basePrice!); }); } }); } @override void dispose() { _basePriceController.dispose(); _basePriceFocus.dispose(); super.dispose(); } /// サイズを追加 void _addSize(String size) { if (_basePrice == null || _basePrice! <= 0) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('先に一合の価格を入力してください')), ); return; } setState(() { final calculatedPrice = PricingHelper.calculateSizePrice(_basePrice!, size); _variants[size] = calculatedPrice; }); // ハプティックフィードバック HapticFeedback.lightImpact(); } /// サイズを削除 void _removeSize(String size) { setState(() { _variants.remove(size); }); HapticFeedback.lightImpact(); } /// 価格を保存 void _save() { if (_basePrice == null || _basePrice! <= 0) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('一合の価格を入力してください')), ); return; } widget.onSave(_basePrice!, _variants); Navigator.of(context).pop(); } @override Widget build(BuildContext context) { final screenHeight = MediaQuery.of(context).size.height; final appColors = Theme.of(context).extension()!; return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Container( constraints: BoxConstraints( maxWidth: 500, maxHeight: screenHeight * 0.85, // 画面の85%まで ), child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(20), // 24 → 20に縮小 child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // ヘッダー: 銘柄名 Text( widget.sakeItem.displayData.displayName, style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), // 8 → 4 Text( '${widget.sakeItem.displayData.displayBrewery} / ${widget.sakeItem.displayData.displayPrefecture}', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: appColors.textSecondary, ), ), const SizedBox(height: 16), // 24 → 16 // セクション1: 一合の税込価格 Text( '一合の税込価格', style: Theme.of(context).textTheme.titleSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), TextField( controller: _basePriceController, focusNode: _basePriceFocus, keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], decoration: InputDecoration( hintText: '例: 1800', suffixText: '円', border: const OutlineInputBorder(), filled: true, fillColor: _basePrice != null && _basePrice! > 0 ? Colors.green.withValues(alpha: 0.05) : null, ), style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), onChanged: (value) { setState(() { _basePrice = PricingHelper.parsePrice(value); }); }, ), const SizedBox(height: 16), // 24 → 16 // セクション2: 提供サイズ選択 Text( '提供サイズ選択', style: Theme.of(context).textTheme.titleSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: PricingHelper.availableSizes.map((size) { final isSelected = _variants.containsKey(size); return FilterChip( label: Text(size), selected: isSelected, onSelected: (selected) { if (selected) { _addSize(size); } else { _removeSize(size); } }, selectedColor: appColors.brandPrimary.withValues(alpha: 0.2), checkmarkColor: appColors.brandPrimary, backgroundColor: appColors.surfaceSubtle, labelStyle: TextStyle( color: isSelected ? appColors.brandPrimary : appColors.textPrimary, fontWeight: isSelected ? FontWeight.bold : null, ), ); }).toList(), ), const SizedBox(height: 16), // 24 → 16 // セクション3: お品書き掲載価格 if (_variants.isNotEmpty) ...[ Text( 'お品書き掲載価格', style: Theme.of(context).textTheme.titleSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Container( decoration: BoxDecoration( border: Border.all(color: appColors.divider), borderRadius: BorderRadius.circular(8), ), child: Column( children: _variants.entries.map((entry) { return _buildPriceItem(entry.key, entry.value); }).toList(), ), ), const SizedBox(height: 16), // 24 → 16 ], // ボタン Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('キャンセル'), ), const SizedBox(width: 8), ElevatedButton( onPressed: _save, style: ElevatedButton.styleFrom( backgroundColor: appColors.brandPrimary, foregroundColor: appColors.surfaceSubtle, ), child: const Text('保存'), ), ], ), ], ), ), ), ), ); } /// 価格アイテム (インライン編集可能) Widget _buildPriceItem(String size, int price) { final appColors = Theme.of(context).extension()!; return InkWell( onTap: () => _showPriceEditDialog(size, price), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), // 12 → 10 decoration: BoxDecoration( border: Border( bottom: BorderSide(color: appColors.divider), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( size, style: const TextStyle(fontSize: 15), // 16 → 15 ), Row( children: [ Text( '${PricingHelper.formatPrice(price)}円', style: const TextStyle( fontSize: 16, // 18 → 16 fontWeight: FontWeight.bold, ), ), const SizedBox(width: 4), // 8 → 4 IconButton( icon: const Icon(Icons.close, size: 18), // 20 → 18 onPressed: () => _removeSize(size), tooltip: '削除', padding: EdgeInsets.zero, constraints: const BoxConstraints(), ), ], ), ], ), ), ); } /// 価格編集ダイアログ (インライン編集) void _showPriceEditDialog(String size, int currentPrice) { final controller = TextEditingController( text: PricingHelper.formatPrice(currentPrice), ); showDialog( context: context, builder: (context) => AlertDialog( title: Text('$size の価格を変更'), content: TextField( controller: controller, keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], decoration: const InputDecoration( hintText: '価格を入力', suffixText: '円', border: OutlineInputBorder(), ), autofocus: true, ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('キャンセル'), ), ElevatedButton( onPressed: () { final newPrice = PricingHelper.parsePrice(controller.text); if (newPrice != null && newPrice > 0) { setState(() { _variants[size] = newPrice; }); Navigator.pop(context); } }, child: const Text('変更'), ), ], ), ); } }