feat:更新编辑菜谱功能
This commit is contained in:
@@ -6,10 +6,10 @@ import 'package:food_hub_app/utils/index.dart';
|
|||||||
|
|
||||||
final httpUtil = HttpUtil(baseUrl: AppConfig.baseApiUrl);
|
final httpUtil = HttpUtil(baseUrl: AppConfig.baseApiUrl);
|
||||||
|
|
||||||
Future<RecipeDetail> queryRecipeByIdApi(int id) {
|
Future<Recipe> queryRecipeByIdApi(int id) {
|
||||||
return httpUtil.get<RecipeDetail>(
|
return httpUtil.get<Recipe>(
|
||||||
"/food/recipe/$id",
|
"/food/recipe/$id",
|
||||||
converter: (data) => RecipeDetail.fromJson(data),
|
converter: (data) => Recipe.fromJson(data),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ void main() async {
|
|||||||
|
|
||||||
FormBuilderLocalizations.delegate.load(const Locale('zh', 'CN'));
|
FormBuilderLocalizations.delegate.load(const Locale('zh', 'CN'));
|
||||||
await SPUtil.init();
|
await SPUtil.init();
|
||||||
// await initLogger();
|
await initLogger();
|
||||||
|
|
||||||
// 预编译 shader,防止首帧白闪
|
// 预编译 shader,防止首帧白闪
|
||||||
await LiquidGlassWidgets.initialize();
|
await LiquidGlassWidgets.initialize();
|
||||||
|
|||||||
@@ -131,11 +131,8 @@ class Recipe {
|
|||||||
List<RecipeStep> stepList;
|
List<RecipeStep> stepList;
|
||||||
List<FoodRecord> recordList;
|
List<FoodRecord> recordList;
|
||||||
List<int> likeList;
|
List<int> likeList;
|
||||||
int likeCount;
|
|
||||||
List<int> favouriteList;
|
List<int> favouriteList;
|
||||||
int favouriteCount;
|
|
||||||
List<RecipeComment> commentList;
|
List<RecipeComment> commentList;
|
||||||
int commentCount;
|
|
||||||
|
|
||||||
Recipe({
|
Recipe({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -151,11 +148,8 @@ class Recipe {
|
|||||||
required this.stepList,
|
required this.stepList,
|
||||||
required this.recordList,
|
required this.recordList,
|
||||||
required this.likeList,
|
required this.likeList,
|
||||||
required this.likeCount,
|
|
||||||
required this.favouriteList,
|
required this.favouriteList,
|
||||||
required this.favouriteCount,
|
|
||||||
required this.commentList,
|
required this.commentList,
|
||||||
required this.commentCount,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Recipe.fromJson(Map<String, dynamic> json) => _$RecipeFromJson(json);
|
factory Recipe.fromJson(Map<String, dynamic> json) => _$RecipeFromJson(json);
|
||||||
@@ -177,11 +171,8 @@ class Recipe {
|
|||||||
stepList: [],
|
stepList: [],
|
||||||
recordList: [],
|
recordList: [],
|
||||||
likeList: [],
|
likeList: [],
|
||||||
likeCount: 0,
|
|
||||||
favouriteList: [],
|
favouriteList: [],
|
||||||
favouriteCount: 0,
|
commentList: []
|
||||||
commentList: [],
|
|
||||||
commentCount: 0,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,17 +106,14 @@ Recipe _$RecipeFromJson(Map<String, dynamic> json) => Recipe(
|
|||||||
(json['likeList'] as List<dynamic>)
|
(json['likeList'] as List<dynamic>)
|
||||||
.map((e) => (e as num).toInt())
|
.map((e) => (e as num).toInt())
|
||||||
.toList(),
|
.toList(),
|
||||||
likeCount: (json['likeCount'] as num).toInt(),
|
|
||||||
favouriteList:
|
favouriteList:
|
||||||
(json['favouriteList'] as List<dynamic>)
|
(json['favouriteList'] as List<dynamic>)
|
||||||
.map((e) => (e as num).toInt())
|
.map((e) => (e as num).toInt())
|
||||||
.toList(),
|
.toList(),
|
||||||
favouriteCount: (json['favouriteCount'] as num).toInt(),
|
|
||||||
commentList:
|
commentList:
|
||||||
(json['commentList'] as List<dynamic>)
|
(json['commentList'] as List<dynamic>)
|
||||||
.map((e) => RecipeComment.fromJson(e as Map<String, dynamic>))
|
.map((e) => RecipeComment.fromJson(e as Map<String, dynamic>))
|
||||||
.toList(),
|
.toList(),
|
||||||
commentCount: (json['commentCount'] as num).toInt(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$RecipeToJson(Recipe instance) => <String, dynamic>{
|
Map<String, dynamic> _$RecipeToJson(Recipe instance) => <String, dynamic>{
|
||||||
@@ -133,11 +130,8 @@ Map<String, dynamic> _$RecipeToJson(Recipe instance) => <String, dynamic>{
|
|||||||
'stepList': instance.stepList,
|
'stepList': instance.stepList,
|
||||||
'recordList': instance.recordList,
|
'recordList': instance.recordList,
|
||||||
'likeList': instance.likeList,
|
'likeList': instance.likeList,
|
||||||
'likeCount': instance.likeCount,
|
|
||||||
'favouriteList': instance.favouriteList,
|
'favouriteList': instance.favouriteList,
|
||||||
'favouriteCount': instance.favouriteCount,
|
|
||||||
'commentList': instance.commentList,
|
'commentList': instance.commentList,
|
||||||
'commentCount': instance.commentCount,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RecipeSummary _$RecipeSummaryFromJson(Map<String, dynamic> json) =>
|
RecipeSummary _$RecipeSummaryFromJson(Map<String, dynamic> json) =>
|
||||||
|
|||||||
@@ -66,6 +66,86 @@ class FoodProvider with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateRecipeFormItem({
|
||||||
|
String? name,
|
||||||
|
String? category,
|
||||||
|
double? recommendRate,
|
||||||
|
String? remark,
|
||||||
|
bool? isShare,
|
||||||
|
}) {
|
||||||
|
if (name != null) recipeFormItem.name = name;
|
||||||
|
if (category != null) recipeFormItem.category = category;
|
||||||
|
if (recommendRate != null) recipeFormItem.recommendRate = recommendRate;
|
||||||
|
if (remark != null) recipeFormItem.remark = remark;
|
||||||
|
if (isShare != null) recipeFormItem.isShare = isShare;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addRecipeMaterial() {
|
||||||
|
recipeFormItem.materialList.add(
|
||||||
|
RecipeMaterial(
|
||||||
|
id: DateTime.now().microsecondsSinceEpoch,
|
||||||
|
type: '主料',
|
||||||
|
name: '',
|
||||||
|
amount: '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateRecipeMaterial(
|
||||||
|
int index, {
|
||||||
|
String? type,
|
||||||
|
String? name,
|
||||||
|
String? amount,
|
||||||
|
}) {
|
||||||
|
final material = recipeFormItem.materialList[index];
|
||||||
|
recipeFormItem.materialList[index] = RecipeMaterial(
|
||||||
|
id: material.id,
|
||||||
|
type: type ?? material.type,
|
||||||
|
name: name ?? material.name,
|
||||||
|
amount: amount ?? material.amount,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeRecipeMaterial(int index) {
|
||||||
|
recipeFormItem.materialList.removeAt(index);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addRecipeStep() {
|
||||||
|
recipeFormItem.stepList.add(
|
||||||
|
RecipeStep(
|
||||||
|
id: DateTime.now().microsecondsSinceEpoch,
|
||||||
|
sort: recipeFormItem.stepList.length,
|
||||||
|
content: '',
|
||||||
|
imageUrl: '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateRecipeStep(int index, {String? content, String? imageUrl}) {
|
||||||
|
final step = recipeFormItem.stepList[index];
|
||||||
|
recipeFormItem.stepList[index] = RecipeStep(
|
||||||
|
id: step.id,
|
||||||
|
sort: step.sort,
|
||||||
|
content: content ?? step.content,
|
||||||
|
imageUrl: imageUrl ?? step.imageUrl,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeRecipeStep(int index) {
|
||||||
|
recipeFormItem.stepList.removeAt(index);
|
||||||
|
|
||||||
|
for (int i = 0; i < recipeFormItem.stepList.length; i++) {
|
||||||
|
recipeFormItem.stepList[i].sort = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -167,6 +247,30 @@ class FoodProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> handleRecipe() async {
|
||||||
|
if (isLoading) return true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
isLoading = true;
|
||||||
|
error = null;
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
if (isEditing) {
|
||||||
|
await updateRecipeApi(recipeFormItem.id!, recipeFormItem);
|
||||||
|
} else {
|
||||||
|
await addRecipeApi(recipeFormItem);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
error = '处理数据失败: $e';
|
||||||
|
debugPrint('处理数据失败: $e');
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> refreshRecordList() async {
|
Future<void> refreshRecordList() async {
|
||||||
if (isLoading) return;
|
if (isLoading) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_common/utils/sp_utils.dart';
|
|
||||||
import 'package:flutter_common/widget/common_widget.dart';
|
import 'package:flutter_common/widget/common_widget.dart';
|
||||||
import 'package:flutter_common/widget/dialog_widget.dart';
|
|
||||||
import 'package:food_hub_app/models/session.dart';
|
|
||||||
import 'package:food_hub_app/provider/app_provider.dart';
|
|
||||||
import 'package:food_hub_app/provider/user_provider.dart';
|
import 'package:food_hub_app/provider/user_provider.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/moment/list.dart';
|
import 'package:food_hub_app/widgets/moment/list.dart';
|
||||||
import 'package:food_hub_app/widgets/profile/basic_info.dart';
|
import 'package:food_hub_app/widgets/profile/basic_info.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';
|
||||||
import 'package:food_hub_app/widgets/recipe/timeline.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
enum ProfileTab {
|
enum ProfileTab {
|
||||||
|
profile('基础信息'),
|
||||||
moment('朋友圈'),
|
moment('朋友圈'),
|
||||||
recipe('菜谱');
|
recipe('菜谱');
|
||||||
|
|
||||||
@@ -30,7 +25,7 @@ class ProfileUserPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ProfileUserPageState extends State<ProfileUserPage> {
|
class ProfileUserPageState extends State<ProfileUserPage> {
|
||||||
late ProfileTab currentTab = ProfileTab.moment;
|
late ProfileTab currentTab = ProfileTab.profile;
|
||||||
|
|
||||||
void _onTabChange(ProfileTab tab) {
|
void _onTabChange(ProfileTab tab) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -60,10 +55,6 @@ class ProfileUserPageState extends State<ProfileUserPage> {
|
|||||||
Widget _buildContent(UserProvider provider) {
|
Widget _buildContent(UserProvider provider) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: ProfileInfo(user: provider.currentUser),
|
|
||||||
),
|
|
||||||
_buildTabs(
|
_buildTabs(
|
||||||
context: context,
|
context: context,
|
||||||
currentTab: currentTab,
|
currentTab: currentTab,
|
||||||
@@ -73,6 +64,9 @@ class ProfileUserPageState extends State<ProfileUserPage> {
|
|||||||
child: IndexedStack(
|
child: IndexedStack(
|
||||||
index: currentTab.index,
|
index: currentTab.index,
|
||||||
children: [
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity, child: ProfileInfo(user: provider.currentUser)
|
||||||
|
),
|
||||||
MomentList(momentList: provider.momentList, isUser: false),
|
MomentList(momentList: provider.momentList, isUser: false),
|
||||||
RecipeList(userId: provider.currentUser.id!),
|
RecipeList(userId: provider.currentUser.id!),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_common/widget/common_widget.dart';
|
import 'package:flutter_common/widget/common_widget.dart';
|
||||||
import 'package:food_hub_app/apis/recipe.dart';
|
import 'package:food_hub_app/apis/recipe.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/utils/screenshot_util.dart';
|
import 'package:food_hub_app/utils/screenshot_util.dart';
|
||||||
|
import 'package:food_hub_app/views/recipe_form.dart';
|
||||||
import 'package:food_hub_app/widgets/common/index.dart';
|
import 'package:food_hub_app/widgets/common/index.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class RecipeDetailPage extends StatefulWidget {
|
class RecipeDetailPage extends StatefulWidget {
|
||||||
const RecipeDetailPage({super.key});
|
const RecipeDetailPage({super.key});
|
||||||
@@ -13,14 +16,11 @@ class RecipeDetailPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _RecipeDetailState extends State<RecipeDetailPage> {
|
class _RecipeDetailState extends State<RecipeDetailPage> {
|
||||||
RecipeDetail recipe = RecipeDetail.getEmpty();
|
Recipe recipe = Recipe.getEmpty();
|
||||||
final GlobalKey _recipeDetailKey = GlobalKey();
|
final GlobalKey _recipeDetailKey = GlobalKey();
|
||||||
bool _isSharing = false;
|
bool _isSharing = false;
|
||||||
|
|
||||||
// 定义三个分类列表
|
List<RecipeMaterial> materialList = [];
|
||||||
List<RecipeMaterial> mainMaterialList = []; // 主料
|
|
||||||
List<RecipeMaterial> auxiliaryMaterialList = []; // 配料
|
|
||||||
List<RecipeMaterial> accessoryMaterialList = []; // 辅料
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
@@ -40,25 +40,29 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void getRecipeMaterial() {
|
void getRecipeMaterial() {
|
||||||
mainMaterialList.clear();
|
materialList.clear();
|
||||||
auxiliaryMaterialList.clear();
|
|
||||||
accessoryMaterialList.clear();
|
|
||||||
|
|
||||||
for (var material in recipe.materialList) {
|
for (var material in recipe.materialList) {
|
||||||
switch (material.type) {
|
materialList.add(material);
|
||||||
case '主料':
|
|
||||||
mainMaterialList.add(material);
|
|
||||||
break;
|
|
||||||
case '配料':
|
|
||||||
auxiliaryMaterialList.add(material);
|
|
||||||
break;
|
|
||||||
case '辅料':
|
|
||||||
accessoryMaterialList.add(material);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handleEditRecipe(FoodProvider provider) {
|
||||||
|
provider.initRecipeForm(recipe);
|
||||||
|
provider.isEditing = true;
|
||||||
|
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 分享图片
|
// 分享图片
|
||||||
Future<void> _shareRecipeAsImage() async {
|
Future<void> _shareRecipeAsImage() async {
|
||||||
if (_isSharing) return;
|
if (_isSharing) return;
|
||||||
@@ -83,6 +87,7 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
final provider = context.watch<FoodProvider>();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@@ -104,7 +109,7 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
child: _buildRecipeDetail(),
|
child: _buildRecipeDetail(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildButtons(),
|
_buildButtons(provider),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -121,7 +126,23 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
context,
|
context,
|
||||||
icon: Icons.info,
|
icon: Icons.info,
|
||||||
title: "基础信息",
|
title: "基础信息",
|
||||||
content: buildTag(context, recipe.category),
|
content: Text(recipe.category),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
_buildInfoCard(
|
||||||
|
context,
|
||||||
|
icon: Icons.star,
|
||||||
|
title: "推荐指数",
|
||||||
|
content: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: List.generate(5, (index) {
|
||||||
|
return Icon(
|
||||||
|
index < recipe.recommendRate ? Icons.star : Icons.star_border,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
size: 20,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
_buildInfoCard(
|
_buildInfoCard(
|
||||||
@@ -130,11 +151,7 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
title: "食材信息",
|
title: "食材信息",
|
||||||
content: Column(
|
content: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [_buildMaterialSection(materialList)],
|
||||||
_buildMaterialSection(context, '主料', mainMaterialList),
|
|
||||||
_buildMaterialSection(context, '辅料', auxiliaryMaterialList),
|
|
||||||
_buildMaterialSection(context, '调料', accessoryMaterialList),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
@@ -142,10 +159,7 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
context,
|
context,
|
||||||
icon: Icons.list,
|
icon: Icons.list,
|
||||||
title: "步骤信息",
|
title: "步骤信息",
|
||||||
content: Column(
|
content: Column(children: [_buildStepSection(recipe.stepList)]),
|
||||||
children:
|
|
||||||
recipe.stepList.map((step) => _buildStepSection(step)).toList(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
_buildInfoCard(
|
_buildInfoCard(
|
||||||
@@ -168,34 +182,43 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMaterialSection(
|
Widget _buildMaterialSection(List<RecipeMaterial> materials) {
|
||||||
BuildContext context,
|
|
||||||
String title,
|
|
||||||
List<RecipeMaterial> materials,
|
|
||||||
) {
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 8, top: 12),
|
|
||||||
child: Text(title, style: TextStyle(fontWeight: FontWeight.bold)),
|
|
||||||
),
|
|
||||||
if (materials.isEmpty)
|
if (materials.isEmpty)
|
||||||
buildTag(context, '无')
|
buildTag(context, '无')
|
||||||
else
|
else
|
||||||
Wrap(
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 8.0,
|
spacing: 8.0,
|
||||||
runSpacing: 8.0,
|
children: materials.map((m) => _buildMaterialItem(m)).toList(),
|
||||||
children:
|
|
||||||
materials
|
|
||||||
.map((m) => buildTag(context, '${m.name} ${m.amount}'))
|
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStepSection(RecipeStep step) {
|
Widget _buildMaterialItem(RecipeMaterial material) {
|
||||||
|
return Text('${material.name} ${material.amount}');
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStepSection(List<RecipeStep> steps) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (steps.isEmpty)
|
||||||
|
buildTag(context, '无')
|
||||||
|
else
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 8.0,
|
||||||
|
children: steps.map((m) => _buildStepItem(m)).toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStepItem(RecipeStep step) {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -213,16 +236,6 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
if (step.imageUrl.isNotEmpty)
|
|
||||||
ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Image.network(
|
|
||||||
step.imageUrl,
|
|
||||||
width: double.infinity,
|
|
||||||
height: 180,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -257,10 +270,16 @@ class _RecipeDetailState extends State<RecipeDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildButtons() {
|
Widget _buildButtons(FoodProvider provider) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
circleIconButton(
|
||||||
|
context: context,
|
||||||
|
icon: Icons.edit,
|
||||||
|
onPressed: () => _handleEditRecipe(provider),
|
||||||
|
),
|
||||||
|
SizedBox(width: 8),
|
||||||
circleIconButton(
|
circleIconButton(
|
||||||
context: context,
|
context: context,
|
||||||
icon: _isSharing ? Icons.hourglass_top : Icons.share,
|
icon: _isSharing ? Icons.hourglass_top : Icons.share,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_common/widget/dialog_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/provider/food_provider.dart';
|
||||||
@@ -21,10 +22,6 @@ class RecipeForm extends StatefulWidget {
|
|||||||
class _RecipeFormState extends State<RecipeForm> {
|
class _RecipeFormState extends State<RecipeForm> {
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
int _currentStep = 0;
|
int _currentStep = 0;
|
||||||
List<RecipeMaterial> _materials = [];
|
|
||||||
List<RecipeMaterial> _materialZhuliaos = [];
|
|
||||||
List<RecipeMaterial> _materialFuliaos = [];
|
|
||||||
List<RecipeStep> _steps = [];
|
|
||||||
|
|
||||||
// 表单字段名称常量
|
// 表单字段名称常量
|
||||||
static const String _nameField = 'name';
|
static const String _nameField = 'name';
|
||||||
@@ -54,18 +51,18 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
Widget _buildStepContent(FoodProvider provider) {
|
Widget _buildStepContent(FoodProvider provider) {
|
||||||
switch (_currentStep) {
|
switch (_currentStep) {
|
||||||
case 0:
|
case 0:
|
||||||
return _buildBasicInfoStep(provider);
|
return _buildInfo(provider);
|
||||||
case 1:
|
case 1:
|
||||||
return _buildMaterialsStep();
|
return _buildMaterials(provider);
|
||||||
case 2:
|
case 2:
|
||||||
return _buildStepsStep();
|
return _buildSteps(provider);
|
||||||
default:
|
default:
|
||||||
return _buildBasicInfoStep(provider);
|
return _buildInfo(provider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第一步:基本信息
|
// 第一步:基本信息
|
||||||
Widget _buildBasicInfoStep(FoodProvider provider) {
|
Widget _buildInfo(FoodProvider provider) {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -80,6 +77,9 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
hintText: '请输入菜谱名称',
|
hintText: '请输入菜谱名称',
|
||||||
prefixIcon: Icons.restaurant_menu,
|
prefixIcon: Icons.restaurant_menu,
|
||||||
),
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
provider.updateRecipeFormItem(name: value ?? '');
|
||||||
|
},
|
||||||
validator: FormBuilderValidators.compose([
|
validator: FormBuilderValidators.compose([
|
||||||
FormBuilderValidators.required(errorText: '菜谱名称不能为空'),
|
FormBuilderValidators.required(errorText: '菜谱名称不能为空'),
|
||||||
FormBuilderValidators.maxLength(50, errorText: '名称不能超过50个字符'),
|
FormBuilderValidators.maxLength(50, errorText: '名称不能超过50个字符'),
|
||||||
@@ -106,6 +106,9 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
|
onChanged: (value) {
|
||||||
|
provider.updateRecipeFormItem(category: value ?? '');
|
||||||
|
},
|
||||||
validator: FormBuilderValidators.required(errorText: '请选择分类'),
|
validator: FormBuilderValidators.required(errorText: '请选择分类'),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
@@ -120,6 +123,9 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
divisions: 4,
|
divisions: 4,
|
||||||
activeColor: Theme.of(context).colorScheme.primary,
|
activeColor: Theme.of(context).colorScheme.primary,
|
||||||
decoration: const InputDecoration(border: InputBorder.none),
|
decoration: const InputDecoration(border: InputBorder.none),
|
||||||
|
onChanged: (value) {
|
||||||
|
provider.updateRecipeFormItem(recommendRate: value ?? 0);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
@@ -133,6 +139,9 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
context: context,
|
context: context,
|
||||||
hintText: '请输入菜谱的特别说明或小贴士',
|
hintText: '请输入菜谱的特别说明或小贴士',
|
||||||
),
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
provider.updateRecipeFormItem(remark: value ?? '');
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
@@ -140,6 +149,9 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
name: _isShareField,
|
name: _isShareField,
|
||||||
initialValue: provider.recipeFormItem.isShare,
|
initialValue: provider.recipeFormItem.isShare,
|
||||||
title: const Text('公开分享此菜谱'),
|
title: const Text('公开分享此菜谱'),
|
||||||
|
onChanged: (value) {
|
||||||
|
provider.updateRecipeFormItem(isShare: value ?? false);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -147,7 +159,9 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 第二步:食材清单
|
// 第二步:食材清单
|
||||||
Widget _buildMaterialsStep() {
|
Widget _buildMaterials(FoodProvider provider) {
|
||||||
|
final materials = provider.recipeFormItem.materialList;
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -161,12 +175,12 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
Icons.add,
|
Icons.add,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
onPressed: _addMaterial,
|
onPressed: () => provider.addRecipeMaterial(),
|
||||||
tooltip: '添加食材',
|
tooltip: '添加食材',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (_materials.isEmpty)
|
if (materials.isEmpty)
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
margin: EdgeInsets.only(bottom: 10),
|
margin: EdgeInsets.only(bottom: 10),
|
||||||
@@ -185,24 +199,28 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
else
|
else
|
||||||
Column(
|
Column(
|
||||||
children:
|
children:
|
||||||
_materials.asMap().entries.map((entry) {
|
materials.asMap().entries.map((entry) {
|
||||||
final index = entry.key;
|
final index = entry.key;
|
||||||
final material = entry.value;
|
final material = entry.value;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
_buildMaterialItem(index, material),
|
_buildMaterialItem(index, material, provider),
|
||||||
const SizedBox(height: 8)
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMaterialItem(int index, RecipeMaterial material) {
|
Widget _buildMaterialItem(
|
||||||
|
int index,
|
||||||
|
RecipeMaterial material,
|
||||||
|
FoodProvider provider,
|
||||||
|
) {
|
||||||
return Column(
|
return Column(
|
||||||
key: ValueKey(material.id),
|
key: ValueKey(material.id),
|
||||||
children: [
|
children: [
|
||||||
@@ -217,7 +235,7 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
prefixIcon: Icons.content_paste,
|
prefixIcon: Icons.content_paste,
|
||||||
),
|
),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_updateMaterial(index, name: value);
|
provider.updateRecipeMaterial(index, name: value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -231,13 +249,13 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
prefixIcon: Icons.scale,
|
prefixIcon: Icons.scale,
|
||||||
),
|
),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_updateMaterial(index, amount: value);
|
provider.updateRecipeMaterial(index, amount: value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.delete, color: Colors.red, size: 20),
|
icon: const Icon(Icons.delete, color: Colors.red, size: 20),
|
||||||
onPressed: () => _removeMaterial(index),
|
onPressed: () => provider.removeRecipeMaterial(index),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -245,44 +263,10 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
Widget _buildSteps(FoodProvider provider) {
|
||||||
|
final steps = provider.recipeFormItem.stepList;
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -296,12 +280,12 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
Icons.add,
|
Icons.add,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
onPressed: _addStep,
|
onPressed: () => provider.addRecipeStep(),
|
||||||
tooltip: '添加步骤',
|
tooltip: '添加步骤',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (_steps.isEmpty)
|
if (steps.isEmpty)
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
margin: EdgeInsets.only(bottom: 10),
|
margin: EdgeInsets.only(bottom: 10),
|
||||||
@@ -320,24 +304,24 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
else
|
else
|
||||||
Column(
|
Column(
|
||||||
children:
|
children:
|
||||||
_steps.asMap().entries.map((entry) {
|
steps.asMap().entries.map((entry) {
|
||||||
final index = entry.key;
|
final index = entry.key;
|
||||||
final step = entry.value;
|
final step = entry.value;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
_buildStepItem(index, step),
|
_buildStepItem(index, step, provider),
|
||||||
const SizedBox(height: 8)
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStepItem(int index, RecipeStep step) {
|
Widget _buildStepItem(int index, RecipeStep step, FoodProvider provider) {
|
||||||
return Row(
|
return Row(
|
||||||
key: ValueKey(step.id),
|
key: ValueKey(step.id),
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
@@ -369,81 +353,22 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
decoration: buildInputDecoration(
|
decoration: buildInputDecoration(
|
||||||
context: context,
|
context: context,
|
||||||
hintText: '请输入内容',
|
hintText: '请输入内容',
|
||||||
prefixIcon: Icons.content_paste
|
prefixIcon: Icons.content_paste,
|
||||||
),
|
),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_updateStep(index, content: value);
|
provider.updateRecipeStep(index, content: value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 删除按钮
|
// 删除按钮
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.delete, color: Colors.red),
|
icon: const Icon(Icons.delete, color: Colors.red),
|
||||||
onPressed: () => _removeStep(index),
|
onPressed: () => provider.removeRecipeStep(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() {
|
Widget _buildStepIndicator() {
|
||||||
return Container(
|
return Container(
|
||||||
@@ -545,7 +470,7 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导航按钮
|
// 导航按钮
|
||||||
Widget _buildNavigationButtons() {
|
Widget _buildNavigationButtons(FoodProvider provider) {
|
||||||
final isLastStep = _currentStep == 2;
|
final isLastStep = _currentStep == 2;
|
||||||
final isFirstStep = _currentStep == 0;
|
final isFirstStep = _currentStep == 0;
|
||||||
|
|
||||||
@@ -563,15 +488,15 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: buildPrimaryButton(
|
child: buildPrimaryButton(
|
||||||
context: context,
|
context: context,
|
||||||
text: isLastStep ? '完成创建' : '下一步',
|
text: isLastStep ? '提交' : '下一步',
|
||||||
onPressed: _nextStep,
|
onPressed: () => _nextStep(provider),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _nextStep() {
|
void _nextStep(FoodProvider provider) {
|
||||||
if (_currentStep < 2) {
|
if (_currentStep < 2) {
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -579,7 +504,7 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_submitForm();
|
_submitForm(provider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,112 +516,37 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _validateCurrentStep() {
|
void _submitForm(FoodProvider provider) async {
|
||||||
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 (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
if (_validateCurrentStep()) {
|
LoadingDialog.show(context, message: '提交中');
|
||||||
final formData = _formKey.currentState!.value;
|
final result = await provider.handleRecipe();
|
||||||
|
if (result == true) {
|
||||||
|
LoadingDialog.hide(context);
|
||||||
|
|
||||||
// 构建完整的RecipeDetail对象
|
if (provider.isEditing) {
|
||||||
// final recipe = RecipeDetail(
|
showSuccessTip(context, '更新菜谱成功');
|
||||||
// id: widget.initialData?.id ?? 0,
|
} else {
|
||||||
// name: formData[_nameField],
|
showSuccessTip(context, '新增菜谱成功');
|
||||||
// category: formData[_categoryField],
|
}
|
||||||
// recommendRate: formData[_recommendRateField] ?? 3.0,
|
|
||||||
// remark: formData[_remarkField] ?? '',
|
Future.delayed(Duration(milliseconds: 1500), () {
|
||||||
// isShare: formData[_isShareField] ?? false,
|
if (mounted) {
|
||||||
// userId: widget.initialData?.userId ?? 0,
|
Navigator.pop(context);
|
||||||
// username: widget.initialData?.username ?? '',
|
}
|
||||||
// avatar: widget.initialData?.avatar ?? '',
|
});
|
||||||
// materialList: _materials,
|
} else {
|
||||||
// stepList: _steps,
|
LoadingDialog.hide(context);
|
||||||
// recordList: widget.initialData?.recordList ?? [],
|
if (provider.isEditing) {
|
||||||
// likeList: widget.initialData?.likeList ?? [],
|
showErrorTip(context, '更新菜谱失败');
|
||||||
// favouriteList: widget.initialData?.favouriteList ?? [],
|
} else {
|
||||||
// commentList: widget.initialData?.commentList ?? [],
|
showErrorTip(context, '新增菜谱失败');
|
||||||
// );
|
}
|
||||||
//
|
|
||||||
// _saveRecipe(recipe);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ToastUtil.error('请检查表单填写是否正确');
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final provider = context.watch<FoodProvider>();
|
final provider = context.watch<FoodProvider>();
|
||||||
@@ -713,7 +563,7 @@ class _RecipeFormState extends State<RecipeForm> {
|
|||||||
children: [
|
children: [
|
||||||
_buildStepIndicator(),
|
_buildStepIndicator(),
|
||||||
FormBuilder(key: _formKey, child: _buildStepContent(provider)),
|
FormBuilder(key: _formKey, child: _buildStepContent(provider)),
|
||||||
_buildNavigationButtons(),
|
_buildNavigationButtons(provider),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_common/utils/date_utils.dart';
|
import 'package:flutter_common/utils/date_utils.dart';
|
||||||
import 'package:flutter_common/widget/common_widget.dart';
|
|
||||||
import 'package:food_hub_app/config/app_config.dart';
|
import 'package:food_hub_app/config/app_config.dart';
|
||||||
import 'package:food_hub_app/models/session.dart';
|
import 'package:food_hub_app/models/session.dart';
|
||||||
import 'package:food_hub_app/widgets/common/index.dart';
|
import 'package:food_hub_app/widgets/common/index.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user