import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:food_hub_app/models/recipe.dart'; import 'package:food_hub_app/provider/food_provider.dart'; import 'package:food_hub_app/widgets/common/form.dart'; import 'package:food_hub_app/widgets/common/index.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:flutter_common/utils/toast_util.dart'; import 'package:flutter_common/widget/loading_widget.dart'; import 'package:liquid_glass_widgets/liquid_glass_widgets.dart'; import 'package:liquid_glass_widgets/widgets/containers/glass_card.dart'; import 'package:provider/provider.dart'; class RecipeForm extends StatefulWidget { const RecipeForm({super.key}); @override State createState() => _RecipeFormState(); } class _RecipeFormState extends State { final _formKey = GlobalKey(); int _currentStep = 0; List _materials = []; List _materialZhuliaos = []; List _materialFuliaos = []; List _steps = []; // 表单字段名称常量 static const String _nameField = 'name'; static const String _categoryField = 'category'; static const String _recommendRateField = 'recommendRate'; static const String _remarkField = 'remark'; static const String _isShareField = 'isShare'; // 分类选项 final List _categoryOptions = [ '家常菜', '川菜', '粤菜', '湘菜', '西餐', '甜品', '汤羹', '其他', ]; @override void initState() { super.initState(); } // 构建步骤内容 Widget _buildStepContent(FoodProvider provider) { switch (_currentStep) { case 0: return _buildBasicInfoStep(provider); case 1: return _buildMaterialsStep(); case 2: return _buildStepsStep(); default: return _buildBasicInfoStep(provider); } } // 第一步:基本信息 Widget _buildBasicInfoStep(FoodProvider provider) { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildFormLabel(context: context, text: '菜谱名称', isRequired: true), const SizedBox(height: 8), FormBuilderTextField( name: _nameField, initialValue: provider.recipeFormItem.name, decoration: buildInputDecoration( context: context, hintText: '请输入菜谱名称', prefixIcon: Icons.restaurant_menu, ), validator: FormBuilderValidators.compose([ FormBuilderValidators.required(errorText: '菜谱名称不能为空'), FormBuilderValidators.maxLength(50, errorText: '名称不能超过50个字符'), ]), ), const SizedBox(height: 16), buildFormLabel(context: context, text: '分类', isRequired: true), const SizedBox(height: 8), FormBuilderDropdown( name: _categoryField, initialValue: provider.recipeFormItem.category, decoration: buildInputDecoration( context: context, hintText: '请选择分类', prefixIcon: Icons.widgets, ), items: _categoryOptions .map( (category) => DropdownMenuItem( value: category, child: Text(category), ), ) .toList(), validator: FormBuilderValidators.required(errorText: '请选择分类'), ), const SizedBox(height: 16), buildFormLabel(context: context, text: '推荐评分', isRequired: false), const SizedBox(height: 8), FormBuilderSlider( name: _recommendRateField, initialValue: provider.recipeFormItem.recommendRate, min: 1, max: 5, divisions: 4, activeColor: Theme.of(context).colorScheme.primary, decoration: const InputDecoration(border: InputBorder.none), ), const SizedBox(height: 16), buildFormLabel(context: context, text: '备注', isRequired: false), const SizedBox(height: 8), FormBuilderTextField( name: _remarkField, initialValue: provider.recipeFormItem.remark, maxLines: 3, decoration: buildInputDecoration( context: context, hintText: '请输入菜谱的特别说明或小贴士', ), ), const SizedBox(height: 16), FormBuilderCheckbox( name: _isShareField, initialValue: provider.recipeFormItem.isShare, title: const Text('公开分享此菜谱'), ), ], ), ); } // 第二步:食材清单 Widget _buildMaterialsStep() { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text('食材清单', style: Theme.of(context).textTheme.titleMedium), const Spacer(), IconButton( icon: Icon( Icons.add, color: Theme.of(context).colorScheme.primary, ), onPressed: _addMaterial, tooltip: '添加食材', ), ], ), if (_materials.isEmpty) Container( color: Theme.of(context).colorScheme.surface, margin: EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(10), child: Column( children: [ Icon(Icons.inventory_2, size: 48, color: Colors.grey[400]), const SizedBox(height: 16), Text( '暂无食材,请点击添加按钮添加食材', style: TextStyle(color: Colors.grey[600]), ), ], ), ) else Column( children: _materials.asMap().entries.map((entry) { final index = entry.key; final material = entry.value; return Column( children: [ _buildMaterialItem(index, material), const SizedBox(height: 8) ], ); }).toList(), ), ], ), ); } Widget _buildMaterialItem(int index, RecipeMaterial material) { return Column( key: ValueKey(material.id), children: [ Row( children: [ Expanded( child: TextFormField( initialValue: material.name, decoration: buildInputDecoration( context: context, hintText: '请输入名称', prefixIcon: Icons.content_paste, ), onChanged: (value) { _updateMaterial(index, name: value); }, ), ), const SizedBox(width: 12), Expanded( child: TextFormField( initialValue: material.amount, decoration: buildInputDecoration( context: context, hintText: '请输入用量', prefixIcon: Icons.scale, ), onChanged: (value) { _updateMaterial(index, amount: value); }, ), ), IconButton( icon: const Icon(Icons.delete, color: Colors.red, size: 20), onPressed: () => _removeMaterial(index), ), ], ), ], ); } void _addMaterial() { setState(() { _materials.add( RecipeMaterial( id: DateTime.now().microsecondsSinceEpoch, type: '主料', name: '', amount: '', ), ); }); } void _removeMaterial(int index) { setState(() { _materials.removeAt(index); }); } void _updateMaterial( int index, { String? type, String? name, String? amount, }) { setState(() { final material = _materials[index]; _materials[index] = RecipeMaterial( id: material.id, type: type ?? material.type, name: name ?? material.name, amount: amount ?? material.amount, ); }); } // 第三步:制作步骤 Widget _buildStepsStep() { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text('制作步骤', style: Theme.of(context).textTheme.titleMedium), const Spacer(), IconButton( icon: Icon( Icons.add, color: Theme.of(context).colorScheme.primary, ), onPressed: _addStep, tooltip: '添加步骤', ), ], ), if (_steps.isEmpty) Container( color: Theme.of(context).colorScheme.surface, margin: EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(10), child: Column( children: [ Icon(Icons.list_alt, size: 48, color: Colors.grey[400]), const SizedBox(height: 16), Text( '暂无步骤,请点击添加按钮添加制作步骤', style: TextStyle(color: Colors.grey[600]), ), ], ), ) else Column( children: _steps.asMap().entries.map((entry) { final index = entry.key; final step = entry.value; return Column( children: [ _buildStepItem(index, step), const SizedBox(height: 8) ], ); }).toList(), ), ], ), ); } Widget _buildStepItem(int index, RecipeStep step) { return Row( key: ValueKey(step.id), crossAxisAlignment: CrossAxisAlignment.center, children: [ // 步骤编号 Container( width: 28, height: 28, alignment: Alignment.center, decoration: BoxDecoration( color: Theme.of(context).colorScheme.primary, shape: BoxShape.circle, ), child: Text( '${index + 1}', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14, ), ), ), const SizedBox(width: 12), // 步骤内容 Expanded( child: TextFormField( initialValue: step.content, decoration: buildInputDecoration( context: context, hintText: '请输入内容', prefixIcon: Icons.content_paste ), onChanged: (value) { _updateStep(index, content: value); }, ), ), // 删除按钮 IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () => _removeStep(index), ), ], ); } void _addStep() { setState(() { _steps.add( RecipeStep( id: DateTime.now().microsecondsSinceEpoch, sort: _steps.length, content: '', imageUrl: '', ), ); }); } void _removeStep(int index) { setState(() { _steps.removeAt(index); // // 重新排序 // for (int i = 0; i < _steps.length; i++) { // _steps[i] = RecipeStep( // id: DateTime.now().microsecond, // sort: i, // content: _steps[i].content, // imageUrl: _steps[i].imageUrl, // ); // } }); } void _reorderStep(int oldIndex, int newIndex) { setState(() { if (oldIndex < newIndex) { newIndex -= 1; } final RecipeStep item = _steps.removeAt(oldIndex); _steps.insert(newIndex, item); // 更新排序序号 for (int i = 0; i < _steps.length; i++) { _steps[i] = RecipeStep( id: DateTime.now().microsecond, sort: i, content: _steps[i].content, imageUrl: _steps[i].imageUrl, ); } }); } void _updateStep(int index, {String? content, String? imageUrl}) { setState(() { final step = _steps[index]; _steps[index] = RecipeStep( id: step.id, sort: step.sort, content: content ?? step.content, imageUrl: imageUrl ?? step.imageUrl, ); }); } // 步骤指示器 Widget _buildStepIndicator() { return Container( padding: const EdgeInsets.symmetric(vertical: 16), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ _buildStepCircle(0), SizedBox(width: 10), _buildStepConnector(0), SizedBox(width: 10), _buildStepCircle(1), SizedBox(width: 10), _buildStepConnector(1), SizedBox(width: 10), _buildStepCircle(2), ], ), Container( margin: const EdgeInsets.only(top: 8), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ _buildStepTitle(0, '基本信息'), SizedBox(width: 40), _buildStepTitle(1, '食材清单'), SizedBox(width: 40), _buildStepTitle(2, '制作步骤'), ], ), ), ], ), ); } // 只构建圆圈,不包含标题文字 Widget _buildStepCircle(int stepIndex) { final isActive = _currentStep == stepIndex; final isCompleted = _currentStep > stepIndex; final boxColor = isActive || isCompleted ? Theme.of(context).colorScheme.primary : Colors.grey; return Container( width: 32, height: 32, decoration: BoxDecoration(color: boxColor, shape: BoxShape.circle), child: Center( child: isCompleted ? const Icon(Icons.check, color: Colors.white, size: 16) : Text( '${stepIndex + 1}', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ); } // 箭头连接器 Widget _buildStepConnector(int stepIndex) { final isActive = _currentStep > stepIndex; return SizedBox( width: 40, child: Icon( Icons.arrow_forward, size: 20, color: isActive ? Theme.of(context).colorScheme.primary : Colors.grey, ), ); } Widget _buildStepTitle(int stepIndex, String title) { final isActive = _currentStep == stepIndex; final isCompleted = _currentStep > stepIndex; final textColor = isActive || isCompleted ? Theme.of(context).colorScheme.primary : Colors.grey; return Text( title, textAlign: TextAlign.center, style: TextStyle( fontWeight: isActive ? FontWeight.bold : FontWeight.normal, color: textColor, fontSize: 12, ), ); } // 导航按钮 Widget _buildNavigationButtons() { final isLastStep = _currentStep == 2; final isFirstStep = _currentStep == 0; return Row( children: [ if (!isFirstStep) Expanded( child: buildInfoButton( context: context, text: '上一步', onPressed: _previousStep, ), ), if (!isFirstStep) const SizedBox(width: 12), Expanded( child: buildPrimaryButton( context: context, text: isLastStep ? '完成创建' : '下一步', onPressed: _nextStep, ), ), ], ); } void _nextStep() { if (_currentStep < 2) { if (_formKey.currentState?.saveAndValidate() ?? false) { setState(() { _currentStep++; }); } } else { _submitForm(); } } void _previousStep() { if (_currentStep > 0) { setState(() { _currentStep--; }); } } bool _validateCurrentStep() { return true; switch (_currentStep) { case 0: // 验证基本信息 final name = _formKey.currentState?.fields[_nameField]?.value?.toString() ?? ''; final category = _formKey.currentState?.fields[_categoryField]?.value?.toString() ?? ''; if (name.isEmpty) { ToastUtil.error('请输入菜谱名称'); return false; } if (category.isEmpty) { ToastUtil.error('请选择分类'); return false; } return true; case 1: // 验证食材清单 if (_materials.isEmpty) { ToastUtil.error('请至少添加一个食材'); return false; } for (var material in _materials) { if (material.name.isEmpty || material.amount.isEmpty) { ToastUtil.error('请完善食材信息'); return false; } } return true; case 2: // 验证制作步骤 if (_steps.isEmpty) { ToastUtil.error('请至少添加一个制作步骤'); return false; } for (var step in _steps) { if (step.content.isEmpty) { ToastUtil.error('请完善步骤说明'); return false; } } return true; default: return true; } } void _submitForm() { if (_formKey.currentState?.saveAndValidate() ?? false) { if (_validateCurrentStep()) { final formData = _formKey.currentState!.value; // 构建完整的RecipeDetail对象 // final recipe = RecipeDetail( // id: widget.initialData?.id ?? 0, // name: formData[_nameField], // category: formData[_categoryField], // recommendRate: formData[_recommendRateField] ?? 3.0, // remark: formData[_remarkField] ?? '', // isShare: formData[_isShareField] ?? false, // userId: widget.initialData?.userId ?? 0, // username: widget.initialData?.username ?? '', // avatar: widget.initialData?.avatar ?? '', // materialList: _materials, // stepList: _steps, // recordList: widget.initialData?.recordList ?? [], // likeList: widget.initialData?.likeList ?? [], // favouriteList: widget.initialData?.favouriteList ?? [], // commentList: widget.initialData?.commentList ?? [], // ); // // _saveRecipe(recipe); } } else { ToastUtil.error('请检查表单填写是否正确'); } } void _saveRecipe(RecipeDetail recipe) { LoadingDialog.show(context, message: '提交中'); // 模拟保存操作 // Future.delayed(const Duration(seconds: 2), () { // LoadingDialog.hide(context); // // if (mounted) { // ToastUtil.success(widget.initialData == null ? '菜谱创建成功!' : '菜谱更新成功!'); // // Future.delayed(const Duration(milliseconds: 1500), () { // if (mounted) { // Navigator.pop(context); // } // }); // } // }); } @override Widget build(BuildContext context) { final provider = context.watch(); return SingleChildScrollView( child: Padding( padding: EdgeInsets.all(10), child: GlassCard( useOwnLayer: true, settings: LiquidGlassSettings( glassColor: Theme.of(context).scaffoldBackgroundColor, ), child: Column( children: [ _buildStepIndicator(), FormBuilder(key: _formKey, child: _buildStepContent(provider)), _buildNavigationButtons(), ], ), ), ), ); } }