feat:更新菜谱表单

This commit is contained in:
2026-06-28 23:17:00 +08:00
parent c10e8e5ffb
commit 613e50b0da
5 changed files with 275 additions and 330 deletions

View File

@@ -120,7 +120,7 @@ class MyApp extends StatelessWidget {
routes: { routes: {
'/home': (context) => HomePage(), '/home': (context) => HomePage(),
'/recordForm': (context) => RecordForm(), '/recordForm': (context) => RecordForm(),
'/recipeForm': (context) => RecipeFormPage(), '/recipeForm': (context) => RecipeForm(),
'/recipeDetail': (context) => RecipeDetailPage(), '/recipeDetail': (context) => RecipeDetailPage(),
'/momentForm': (context) => MomentForm(), '/momentForm': (context) => MomentForm(),
'/moment': (context) => MomentPage(), '/moment': (context) => MomentPage(),

View File

@@ -161,6 +161,29 @@ class Recipe {
factory Recipe.fromJson(Map<String, dynamic> json) => _$RecipeFromJson(json); factory Recipe.fromJson(Map<String, dynamic> json) => _$RecipeFromJson(json);
Map<String, dynamic> toJson() => _$RecipeToJson(this); Map<String, dynamic> toJson() => _$RecipeToJson(this);
static Recipe getEmpty() {
return Recipe(
id: 0,
name: '',
category: '家常菜',
recommendRate: 3,
remark: '',
isShare: true,
userId: 0,
username: '',
avatar: '',
materialList: [],
stepList: [],
recordList: [],
likeList: [],
likeCount: 0,
favouriteList: [],
favouriteCount: 0,
commentList: [],
commentCount: 0,
);
}
} }
@JsonSerializable() @JsonSerializable()

View File

@@ -13,6 +13,7 @@ import 'package:food_hub_app/views/record.dart';
class FoodProvider with ChangeNotifier { class FoodProvider with ChangeNotifier {
late RecordTab currentRecordTab = RecordTab.recipe; late RecordTab currentRecordTab = RecordTab.recipe;
late FoodRecord recordFormItem; late FoodRecord recordFormItem;
late Recipe recipeFormItem;
late Moment momentFormItem; late Moment momentFormItem;
late bool isEditing; late bool isEditing;
@@ -55,6 +56,16 @@ class FoodProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void resetRecipeForm() {
recipeFormItem = Recipe.getEmpty();
notifyListeners();
}
void initRecipeForm(Recipe recipe) {
recipeFormItem = recipe;
notifyListeners();
}
void updateRecordFormItem({String? name, String? date, String? imageUrl}) { void updateRecordFormItem({String? name, String? date, String? imageUrl}) {
if (name != null) recordFormItem.name = name; if (name != null) recordFormItem.name = name;
if (date != null) recordFormItem.date = date; if (date != null) recordFormItem.date = date;

View File

@@ -1,26 +1,29 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_common/widget/common_widget.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:food_hub_app/models/recipe.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/form.dart';
import 'package:food_hub_app/widgets/common/index.dart'; import 'package:food_hub_app/widgets/common/index.dart';
import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:flutter_common/utils/toast_util.dart'; import 'package:flutter_common/utils/toast_util.dart';
import 'package:flutter_common/widget/loading_widget.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 RecipeFormPage extends StatefulWidget { class RecipeForm extends StatefulWidget {
final RecipeDetail? initialData; const RecipeForm({super.key});
const RecipeFormPage({super.key, this.initialData});
@override @override
State<RecipeFormPage> createState() => _RecipeFormPageState(); State<RecipeForm> createState() => _RecipeFormState();
} }
class _RecipeFormPageState extends State<RecipeFormPage> { class _RecipeFormState extends State<RecipeForm> {
final _formKey = GlobalKey<FormBuilderState>(); final _formKey = GlobalKey<FormBuilderState>();
int _currentStep = 0; int _currentStep = 0;
List<RecipeMaterial> _materials = []; List<RecipeMaterial> _materials = [];
List<RecipeMaterial> _materialZhuliaos = [];
List<RecipeMaterial> _materialFuliaos = [];
List<RecipeStep> _steps = []; List<RecipeStep> _steps = [];
// 表单字段名称常量 // 表单字段名称常量
@@ -45,32 +48,24 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_initializeFormData();
}
void _initializeFormData() {
if (widget.initialData != null) {
_materials = List.from(widget.initialData!.materialList);
_steps = List.from(widget.initialData!.stepList);
}
} }
// 构建步骤内容 // 构建步骤内容
Widget _buildStepContent() { Widget _buildStepContent(FoodProvider provider) {
switch (_currentStep) { switch (_currentStep) {
case 0: case 0:
return _buildBasicInfoStep(); return _buildBasicInfoStep(provider);
case 1: case 1:
return _buildMaterialsStep(); return _buildMaterialsStep();
case 2: case 2:
return _buildStepsStep(); return _buildStepsStep();
default: default:
return _buildBasicInfoStep(); return _buildBasicInfoStep(provider);
} }
} }
// 第一步:基本信息 // 第一步:基本信息
Widget _buildBasicInfoStep() { Widget _buildBasicInfoStep(FoodProvider provider) {
return SingleChildScrollView( return SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -79,7 +74,7 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
const SizedBox(height: 8), const SizedBox(height: 8),
FormBuilderTextField( FormBuilderTextField(
name: _nameField, name: _nameField,
initialValue: widget.initialData?.name ?? '', initialValue: provider.recipeFormItem.name,
decoration: buildInputDecoration( decoration: buildInputDecoration(
context: context, context: context,
hintText: '请输入菜谱名称', hintText: '请输入菜谱名称',
@@ -96,7 +91,7 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
const SizedBox(height: 8), const SizedBox(height: 8),
FormBuilderDropdown( FormBuilderDropdown(
name: _categoryField, name: _categoryField,
initialValue: widget.initialData?.category ?? '家常菜', initialValue: provider.recipeFormItem.category,
decoration: buildInputDecoration( decoration: buildInputDecoration(
context: context, context: context,
hintText: '请选择分类', hintText: '请选择分类',
@@ -119,11 +114,11 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
const SizedBox(height: 8), const SizedBox(height: 8),
FormBuilderSlider( FormBuilderSlider(
name: _recommendRateField, name: _recommendRateField,
initialValue: widget.initialData?.recommendRate ?? 3.0, initialValue: provider.recipeFormItem.recommendRate,
min: 0, min: 1,
max: 5, max: 5,
divisions: 10, divisions: 4,
activeColor: Colors.orange, activeColor: Theme.of(context).colorScheme.primary,
decoration: const InputDecoration(border: InputBorder.none), decoration: const InputDecoration(border: InputBorder.none),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -132,18 +127,18 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
const SizedBox(height: 8), const SizedBox(height: 8),
FormBuilderTextField( FormBuilderTextField(
name: _remarkField, name: _remarkField,
initialValue: widget.initialData?.remark ?? '', initialValue: provider.recipeFormItem.remark,
maxLines: 3, maxLines: 3,
decoration: buildInputDecoration( decoration: buildInputDecoration(
context: context, context: context,
hintText: '请输入菜谱的特别说明或小贴士' hintText: '请输入菜谱的特别说明或小贴士',
) ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
FormBuilderCheckbox( FormBuilderCheckbox(
name: _isShareField, name: _isShareField,
initialValue: widget.initialData?.isShare ?? false, initialValue: provider.recipeFormItem.isShare,
title: const Text('公开分享此菜谱'), title: const Text('公开分享此菜谱'),
), ),
], ],
@@ -159,20 +154,23 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
children: [ children: [
Row( Row(
children: [ children: [
Text('食材清单', style: Theme.of(context).textTheme.headlineSmall), Text('食材清单', style: Theme.of(context).textTheme.titleMedium),
const Spacer(), const Spacer(),
IconButton( IconButton(
icon: const Icon(Icons.add, color: Colors.blue), icon: Icon(
Icons.add,
color: Theme.of(context).colorScheme.primary,
),
onPressed: _addMaterial, onPressed: _addMaterial,
tooltip: '添加食材', tooltip: '添加食材',
), ),
], ],
), ),
const SizedBox(height: 16),
if (_materials.isEmpty) if (_materials.isEmpty)
Container( Container(
padding: const EdgeInsets.symmetric(vertical: 40), color: Theme.of(context).colorScheme.surface,
margin: EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(10),
child: Column( child: Column(
children: [ children: [
Icon(Icons.inventory_2, size: 48, color: Colors.grey[400]), Icon(Icons.inventory_2, size: 48, color: Colors.grey[400]),
@@ -185,8 +183,19 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
), ),
) )
else else
..._materials.asMap().entries.map( Column(
(entry) => _buildMaterialItem(entry.key, entry.value), 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(),
), ),
], ],
), ),
@@ -194,64 +203,18 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
} }
Widget _buildMaterialItem(int index, RecipeMaterial material) { Widget _buildMaterialItem(int index, RecipeMaterial material) {
return Card( return Column(
key: ValueKey(material.id), key: ValueKey(material.id),
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [ children: [
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 4,
),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(12),
),
child: Text(
'食材 ${index + 1}',
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
),
const Spacer(),
IconButton(
icon: const Icon(Icons.delete, color: Colors.red, size: 20),
onPressed: () => _removeMaterial(index),
),
],
),
const SizedBox(height: 12),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: TextFormField(
initialValue: material.type,
decoration: const InputDecoration(
labelText: '类型',
border: OutlineInputBorder(),
hintText: '如:主料、辅料',
),
onChanged: (value) {
_updateMaterial(index, type: value);
},
),
),
const SizedBox(width: 12),
Expanded(
flex: 2,
child: TextFormField( child: TextFormField(
initialValue: material.name, initialValue: material.name,
decoration: const InputDecoration( decoration: buildInputDecoration(
labelText: '食材名称', context: context,
border: OutlineInputBorder(), hintText: '请输入名称',
hintText: '如:鸡蛋、西红柿', prefixIcon: Icons.content_paste,
), ),
onChanged: (value) { onChanged: (value) {
_updateMaterial(index, name: value); _updateMaterial(index, name: value);
@@ -262,21 +225,23 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
Expanded( Expanded(
child: TextFormField( child: TextFormField(
initialValue: material.amount, initialValue: material.amount,
decoration: const InputDecoration( decoration: buildInputDecoration(
labelText: '用量', context: context,
border: OutlineInputBorder(), hintText: '请输入用量',
hintText: '2个、100g', prefixIcon: Icons.scale,
), ),
onChanged: (value) { onChanged: (value) {
_updateMaterial(index, amount: value); _updateMaterial(index, amount: value);
}, },
), ),
), ),
], IconButton(
icon: const Icon(Icons.delete, color: Colors.red, size: 20),
onPressed: () => _removeMaterial(index),
), ),
], ],
), ),
), ],
); );
} }
@@ -324,20 +289,23 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
children: [ children: [
Row( Row(
children: [ children: [
Text('制作步骤', style: Theme.of(context).textTheme.headlineSmall), Text('制作步骤', style: Theme.of(context).textTheme.titleMedium),
const Spacer(), const Spacer(),
IconButton( IconButton(
icon: const Icon(Icons.add, color: Colors.blue), icon: Icon(
Icons.add,
color: Theme.of(context).colorScheme.primary,
),
onPressed: _addStep, onPressed: _addStep,
tooltip: '添加步骤', tooltip: '添加步骤',
), ),
], ],
), ),
const SizedBox(height: 16),
if (_steps.isEmpty) if (_steps.isEmpty)
Container( Container(
padding: const EdgeInsets.symmetric(vertical: 40), color: Theme.of(context).colorScheme.surface,
margin: EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(10),
child: Column( child: Column(
children: [ children: [
Icon(Icons.list_alt, size: 48, color: Colors.grey[400]), Icon(Icons.list_alt, size: 48, color: Colors.grey[400]),
@@ -350,14 +318,19 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
), ),
) )
else else
ReorderableListView.builder( Column(
shrinkWrap: true, children:
physics: const NeverScrollableScrollPhysics(), _steps.asMap().entries.map((entry) {
itemCount: _steps.length, final index = entry.key;
itemBuilder: final step = entry.value;
(context, index) => _buildStepItem(index, _steps[index]),
onReorder: return Column(
(oldIndex, newIndex) => _reorderStep(oldIndex, newIndex), children: [
_buildStepItem(index, step),
const SizedBox(height: 8)
],
);
}).toList(),
), ),
], ],
), ),
@@ -365,68 +338,50 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
} }
Widget _buildStepItem(int index, RecipeStep step) { Widget _buildStepItem(int index, RecipeStep step) {
return Card( return Row(
key: Key('step_$index'), key: ValueKey(step.id),
margin: const EdgeInsets.only(bottom: 12), crossAxisAlignment: CrossAxisAlignment.center,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
children: [ children: [
// 步骤编号
Container( Container(
padding: const EdgeInsets.symmetric( width: 28,
horizontal: 12, height: 28,
vertical: 6, alignment: Alignment.center,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.green.shade50, color: Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(12), shape: BoxShape.circle,
), ),
child: Text( child: Text(
'步骤 ${index + 1}', '${index + 1}',
style: const TextStyle( style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.green, fontSize: 14,
), ),
), ),
), ),
const Spacer(), const SizedBox(width: 12),
IconButton(
icon: const Icon(Icons.delete, color: Colors.red), // 步骤内容
onPressed: () => _removeStep(index), Expanded(
), child: TextFormField(
],
),
const SizedBox(height: 12),
TextFormField(
initialValue: step.content, initialValue: step.content,
maxLines: 3, decoration: buildInputDecoration(
decoration: const InputDecoration( context: context,
labelText: '步骤说明', hintText: '请输入内容',
border: OutlineInputBorder(), prefixIcon: Icons.content_paste
hintText: '详细描述此步骤的操作方法',
), ),
onChanged: (value) { onChanged: (value) {
_updateStep(index, content: value); _updateStep(index, content: value);
}, },
), ),
const SizedBox(height: 12),
TextFormField(
initialValue: step.imageUrl,
decoration: const InputDecoration(
labelText: '步骤图片URL可选',
border: OutlineInputBorder(),
hintText: '添加步骤演示图片链接',
prefixIcon: Icon(Icons.image),
), ),
onChanged: (value) { // 删除按钮
_updateStep(index, imageUrl: value); IconButton(
}, icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () => _removeStep(index),
), ),
], ],
),
),
); );
} }
@@ -434,7 +389,7 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
setState(() { setState(() {
_steps.add( _steps.add(
RecipeStep( RecipeStep(
id: DateTime.now().microsecond, id: DateTime.now().microsecondsSinceEpoch,
sort: _steps.length, sort: _steps.length,
content: '', content: '',
imageUrl: '', imageUrl: '',
@@ -446,15 +401,15 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
void _removeStep(int index) { void _removeStep(int index) {
setState(() { setState(() {
_steps.removeAt(index); _steps.removeAt(index);
// 重新排序 // // 重新排序
for (int i = 0; i < _steps.length; i++) { // for (int i = 0; i < _steps.length; i++) {
_steps[i] = RecipeStep( // _steps[i] = RecipeStep(
id: DateTime.now().microsecond, // id: DateTime.now().microsecond,
sort: i, // sort: i,
content: _steps[i].content, // content: _steps[i].content,
imageUrl: _steps[i].imageUrl, // imageUrl: _steps[i].imageUrl,
); // );
} // }
}); });
} }
@@ -481,7 +436,7 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
setState(() { setState(() {
final step = _steps[index]; final step = _steps[index];
_steps[index] = RecipeStep( _steps[index] = RecipeStep(
id: DateTime.now().microsecond, id: step.id,
sort: step.sort, sort: step.sort,
content: content ?? step.content, content: content ?? step.content,
imageUrl: imageUrl ?? step.imageUrl, imageUrl: imageUrl ?? step.imageUrl,
@@ -500,9 +455,13 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
_buildStepCircle(0), _buildStepCircle(0),
SizedBox(width: 10),
_buildStepConnector(0), _buildStepConnector(0),
SizedBox(width: 10),
_buildStepCircle(1), _buildStepCircle(1),
SizedBox(width: 10),
_buildStepConnector(1), _buildStepConnector(1),
SizedBox(width: 10),
_buildStepCircle(2), _buildStepCircle(2),
], ],
), ),
@@ -513,9 +472,9 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
_buildStepTitle(0, '基本信息'), _buildStepTitle(0, '基本信息'),
SizedBox(width: 25), SizedBox(width: 40),
_buildStepTitle(1, '食材清单'), _buildStepTitle(1, '食材清单'),
SizedBox(width: 25), SizedBox(width: 40),
_buildStepTitle(2, '制作步骤'), _buildStepTitle(2, '制作步骤'),
], ],
), ),
@@ -529,24 +488,23 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
Widget _buildStepCircle(int stepIndex) { Widget _buildStepCircle(int stepIndex) {
final isActive = _currentStep == stepIndex; final isActive = _currentStep == stepIndex;
final isCompleted = _currentStep > stepIndex; final isCompleted = _currentStep > stepIndex;
final boxColor = isActive || isCompleted final boxColor =
isActive || isCompleted
? Theme.of(context).colorScheme.primary ? Theme.of(context).colorScheme.primary
: Colors.grey; : Colors.grey;
return Container( return Container(
width: 32, width: 32,
height: 32, height: 32,
decoration: BoxDecoration( decoration: BoxDecoration(color: boxColor, shape: BoxShape.circle),
color: boxColor,
shape: BoxShape.circle,
),
child: Center( child: Center(
child: isCompleted child:
isCompleted
? const Icon(Icons.check, color: Colors.white, size: 16) ? const Icon(Icons.check, color: Colors.white, size: 16)
: Text( : Text(
'${stepIndex + 1}', '${stepIndex + 1}',
style: TextStyle( style: TextStyle(
color: isActive ? Colors.white : Colors.grey.shade700, color: Colors.white,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@@ -570,7 +528,8 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
Widget _buildStepTitle(int stepIndex, String title) { Widget _buildStepTitle(int stepIndex, String title) {
final isActive = _currentStep == stepIndex; final isActive = _currentStep == stepIndex;
final isCompleted = _currentStep > stepIndex; final isCompleted = _currentStep > stepIndex;
final textColor = isActive || isCompleted final textColor =
isActive || isCompleted
? Theme.of(context).colorScheme.primary ? Theme.of(context).colorScheme.primary
: Colors.grey; : Colors.grey;
@@ -594,16 +553,10 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
children: [ children: [
if (!isFirstStep) if (!isFirstStep)
Expanded( Expanded(
child: OutlinedButton( child: buildInfoButton(
context: context,
text: '上一步',
onPressed: _previousStep, onPressed: _previousStep,
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
side: BorderSide(color: Theme.of(context).colorScheme.primary),
),
child: Text(
'上一步',
style: TextStyle(color: Theme.of(context).colorScheme.primary),
),
), ),
), ),
if (!isFirstStep) const SizedBox(width: 12), if (!isFirstStep) const SizedBox(width: 12),
@@ -700,25 +653,25 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
final formData = _formKey.currentState!.value; final formData = _formKey.currentState!.value;
// 构建完整的RecipeDetail对象 // 构建完整的RecipeDetail对象
final recipe = RecipeDetail( // final recipe = RecipeDetail(
id: widget.initialData?.id ?? 0, // id: widget.initialData?.id ?? 0,
name: formData[_nameField], // name: formData[_nameField],
category: formData[_categoryField], // category: formData[_categoryField],
recommendRate: formData[_recommendRateField] ?? 3.0, // recommendRate: formData[_recommendRateField] ?? 3.0,
remark: formData[_remarkField] ?? '', // remark: formData[_remarkField] ?? '',
isShare: formData[_isShareField] ?? false, // isShare: formData[_isShareField] ?? false,
userId: widget.initialData?.userId ?? 0, // userId: widget.initialData?.userId ?? 0,
username: widget.initialData?.username ?? '', // username: widget.initialData?.username ?? '',
avatar: widget.initialData?.avatar ?? '', // avatar: widget.initialData?.avatar ?? '',
materialList: _materials, // materialList: _materials,
stepList: _steps, // stepList: _steps,
recordList: widget.initialData?.recordList ?? [], // recordList: widget.initialData?.recordList ?? [],
likeList: widget.initialData?.likeList ?? [], // likeList: widget.initialData?.likeList ?? [],
favouriteList: widget.initialData?.favouriteList ?? [], // favouriteList: widget.initialData?.favouriteList ?? [],
commentList: widget.initialData?.commentList ?? [], // commentList: widget.initialData?.commentList ?? [],
); // );
//
_saveRecipe(recipe); // _saveRecipe(recipe);
} }
} else { } else {
ToastUtil.error('请检查表单填写是否正确'); ToastUtil.error('请检查表单填写是否正确');
@@ -729,50 +682,37 @@ class _RecipeFormPageState extends State<RecipeFormPage> {
LoadingDialog.show(context, message: '提交中'); LoadingDialog.show(context, message: '提交中');
// 模拟保存操作 // 模拟保存操作
Future.delayed(const Duration(seconds: 2), () { // Future.delayed(const Duration(seconds: 2), () {
LoadingDialog.hide(context); // LoadingDialog.hide(context);
//
if (mounted) { // if (mounted) {
ToastUtil.success(widget.initialData == null ? '菜谱创建成功!' : '菜谱更新成功!'); // ToastUtil.success(widget.initialData == null ? '菜谱创建成功!' : '菜谱更新成功!');
//
Future.delayed(const Duration(milliseconds: 1500), () { // Future.delayed(const Duration(milliseconds: 1500), () {
if (mounted) { // if (mounted) {
Navigator.pop(context); // Navigator.pop(context);
} // }
}); // });
} // }
}); // });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme; final provider = context.watch<FoodProvider>();
final title = widget.initialData == null ? '创建新菜谱' : '编辑菜谱';
return Scaffold( return SingleChildScrollView(
appBar: AppBar( child: Padding(
title: Text(title, style: const TextStyle(color: Colors.white)),
backgroundColor: colors.primary,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.pop(context),
),
),
body: Padding(
padding: EdgeInsets.all(10), padding: EdgeInsets.all(10),
child: CommonCard( child: GlassCard(
useOwnLayer: true,
settings: LiquidGlassSettings(
glassColor: Theme.of(context).scaffoldBackgroundColor,
),
child: Column( child: Column(
children: [ children: [
_buildStepIndicator(), _buildStepIndicator(),
Expanded( FormBuilder(key: _formKey, child: _buildStepContent(provider)),
child: FormBuilder(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16),
child: _buildStepContent(),
),
),
),
_buildNavigationButtons(), _buildNavigationButtons(),
], ],
), ),

View File

@@ -1,10 +1,8 @@
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_common/widget/common_widget.dart';
import 'package:food_hub_app/provider/food_provider.dart'; import 'package:food_hub_app/provider/food_provider.dart';
import 'package:food_hub_app/views/moment_form.dart'; import 'package:food_hub_app/views/moment_form.dart';
import 'package:food_hub_app/views/recipe_form.dart';
import 'package:food_hub_app/views/record_form.dart'; import 'package:food_hub_app/views/record_form.dart';
import 'package:food_hub_app/widgets/common/easy_refresh.dart';
import 'package:food_hub_app/widgets/common/index.dart'; import 'package:food_hub_app/widgets/common/index.dart';
import 'package:food_hub_app/widgets/recipe/calendar.dart'; import 'package:food_hub_app/widgets/recipe/calendar.dart';
import 'package:food_hub_app/widgets/recipe/list.dart'; import 'package:food_hub_app/widgets/recipe/list.dart';
@@ -29,12 +27,6 @@ class RecordPage extends StatefulWidget {
} }
class _RecordPageState extends State<RecordPage> { class _RecordPageState extends State<RecordPage> {
final EasyRefreshController _freshController = EasyRefreshController(
controlFinishRefresh: true,
controlFinishLoad: true,
);
final ScrollController _scrollController = ScrollController();
bool _showScrollToTop = false;
final List<Widget> _tabPages = [ final List<Widget> _tabPages = [
RecipeList(userId: 0), RecipeList(userId: 0),
RecipeCalendar(), RecipeCalendar(),
@@ -44,53 +36,13 @@ class _RecordPageState extends State<RecordPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_scrollController.addListener(_onScroll);
} }
@override @override
void dispose() { void dispose() {
_scrollController.removeListener(_onScroll);
_freshController.dispose();
_scrollController.dispose();
super.dispose(); super.dispose();
} }
// 下拉刷新
Future<void> _onRefresh() async {
_freshController.finishRefresh();
}
// 上拉加载
Future<void> _onLoad() async {
final provider = context.read<FoodProvider>();
if (provider.hasMore) {
await provider.refreshFoodStats();
_freshController.finishLoad(IndicatorResult.success);
} else {
_freshController.finishLoad(IndicatorResult.noMore);
}
}
void _onScroll() {
// 当滚动距离超过时显示返回顶部按钮
if (_scrollController.offset > 100) {
if (!_showScrollToTop) {
setState(() {
_showScrollToTop = true;
});
}
} else {
if (_showScrollToTop) {
setState(() {
_showScrollToTop = false;
});
}
}
}
// 滚动到顶部
void _scrollToTop() => scrollToTopAnimateTo(_scrollController);
Widget _buildTabs({ Widget _buildTabs({
required BuildContext context, required BuildContext context,
required RecordTab currentTab, required RecordTab currentTab,
@@ -129,6 +81,25 @@ class _RecordPageState extends State<RecordPage> {
); );
} }
void _handleAddRecipe(FoodProvider provider) {
provider.resetRecipeForm();
provider.isEditing = false;
Navigator.pop(context);
showModalBottomSheet(
context: context,
constraints: BoxConstraints(
minHeight: 600,
),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) => RecipeForm(),
);
}
void _handleAddMoment(FoodProvider provider) { void _handleAddMoment(FoodProvider provider) {
provider.resetMomentForm(); provider.resetMomentForm();
provider.isEditing = false; provider.isEditing = false;
@@ -170,7 +141,7 @@ class _RecordPageState extends State<RecordPage> {
'新增菜谱', '新增菜谱',
style: Theme.of(context).textTheme.bodyLarge, style: Theme.of(context).textTheme.bodyLarge,
), ),
onTap: () => {}, onTap: () => _handleAddRecipe(provider),
), ),
ListTile( ListTile(
leading: Icon( leading: Icon(