From 613e50b0da6f5e1f0172dd5b7b955bd74f1e7b2e Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Sun, 28 Jun 2026 23:17:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9B=B4=E6=96=B0=E8=8F=9C=E8=B0=B1?= =?UTF-8?q?=E8=A1=A8=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 2 +- lib/models/recipe.dart | 23 ++ lib/provider/food_provider.dart | 11 + lib/views/recipe_form.dart | 498 ++++++++++++++------------------ lib/views/record.dart | 71 ++--- 5 files changed, 275 insertions(+), 330 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index d23dc0d..0cecc69 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -120,7 +120,7 @@ class MyApp extends StatelessWidget { routes: { '/home': (context) => HomePage(), '/recordForm': (context) => RecordForm(), - '/recipeForm': (context) => RecipeFormPage(), + '/recipeForm': (context) => RecipeForm(), '/recipeDetail': (context) => RecipeDetailPage(), '/momentForm': (context) => MomentForm(), '/moment': (context) => MomentPage(), diff --git a/lib/models/recipe.dart b/lib/models/recipe.dart index 7ba6a29..3e0957f 100644 --- a/lib/models/recipe.dart +++ b/lib/models/recipe.dart @@ -161,6 +161,29 @@ class Recipe { factory Recipe.fromJson(Map json) => _$RecipeFromJson(json); Map 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() diff --git a/lib/provider/food_provider.dart b/lib/provider/food_provider.dart index 8fb3d6d..4e2452b 100644 --- a/lib/provider/food_provider.dart +++ b/lib/provider/food_provider.dart @@ -13,6 +13,7 @@ import 'package:food_hub_app/views/record.dart'; class FoodProvider with ChangeNotifier { late RecordTab currentRecordTab = RecordTab.recipe; late FoodRecord recordFormItem; + late Recipe recipeFormItem; late Moment momentFormItem; late bool isEditing; @@ -55,6 +56,16 @@ class FoodProvider with ChangeNotifier { notifyListeners(); } + void resetRecipeForm() { + recipeFormItem = Recipe.getEmpty(); + notifyListeners(); + } + + void initRecipeForm(Recipe recipe) { + recipeFormItem = recipe; + notifyListeners(); + } + void updateRecordFormItem({String? name, String? date, String? imageUrl}) { if (name != null) recordFormItem.name = name; if (date != null) recordFormItem.date = date; diff --git a/lib/views/recipe_form.dart b/lib/views/recipe_form.dart index 04470d9..4dc14db 100644 --- a/lib/views/recipe_form.dart +++ b/lib/views/recipe_form.dart @@ -1,26 +1,29 @@ import 'package:flutter/material.dart'; -import 'package:flutter_common/widget/common_widget.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 RecipeFormPage extends StatefulWidget { - final RecipeDetail? initialData; - - const RecipeFormPage({super.key, this.initialData}); +class RecipeForm extends StatefulWidget { + const RecipeForm({super.key}); @override - State createState() => _RecipeFormPageState(); + State createState() => _RecipeFormState(); } -class _RecipeFormPageState extends State { +class _RecipeFormState extends State { final _formKey = GlobalKey(); int _currentStep = 0; List _materials = []; + List _materialZhuliaos = []; + List _materialFuliaos = []; List _steps = []; // 表单字段名称常量 @@ -45,32 +48,24 @@ class _RecipeFormPageState extends State { @override void 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) { case 0: - return _buildBasicInfoStep(); + return _buildBasicInfoStep(provider); case 1: return _buildMaterialsStep(); case 2: return _buildStepsStep(); default: - return _buildBasicInfoStep(); + return _buildBasicInfoStep(provider); } } // 第一步:基本信息 - Widget _buildBasicInfoStep() { + Widget _buildBasicInfoStep(FoodProvider provider) { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -79,7 +74,7 @@ class _RecipeFormPageState extends State { const SizedBox(height: 8), FormBuilderTextField( name: _nameField, - initialValue: widget.initialData?.name ?? '', + initialValue: provider.recipeFormItem.name, decoration: buildInputDecoration( context: context, hintText: '请输入菜谱名称', @@ -96,7 +91,7 @@ class _RecipeFormPageState extends State { const SizedBox(height: 8), FormBuilderDropdown( name: _categoryField, - initialValue: widget.initialData?.category ?? '家常菜', + initialValue: provider.recipeFormItem.category, decoration: buildInputDecoration( context: context, hintText: '请选择分类', @@ -119,11 +114,11 @@ class _RecipeFormPageState extends State { const SizedBox(height: 8), FormBuilderSlider( name: _recommendRateField, - initialValue: widget.initialData?.recommendRate ?? 3.0, - min: 0, + initialValue: provider.recipeFormItem.recommendRate, + min: 1, max: 5, - divisions: 10, - activeColor: Colors.orange, + divisions: 4, + activeColor: Theme.of(context).colorScheme.primary, decoration: const InputDecoration(border: InputBorder.none), ), const SizedBox(height: 16), @@ -132,18 +127,18 @@ class _RecipeFormPageState extends State { const SizedBox(height: 8), FormBuilderTextField( name: _remarkField, - initialValue: widget.initialData?.remark ?? '', + initialValue: provider.recipeFormItem.remark, maxLines: 3, decoration: buildInputDecoration( context: context, - hintText: '请输入菜谱的特别说明或小贴士' - ) + hintText: '请输入菜谱的特别说明或小贴士', + ), ), const SizedBox(height: 16), FormBuilderCheckbox( name: _isShareField, - initialValue: widget.initialData?.isShare ?? false, + initialValue: provider.recipeFormItem.isShare, title: const Text('公开分享此菜谱'), ), ], @@ -159,20 +154,23 @@ class _RecipeFormPageState extends State { children: [ Row( children: [ - Text('食材清单', style: Theme.of(context).textTheme.headlineSmall), + Text('食材清单', style: Theme.of(context).textTheme.titleMedium), const Spacer(), IconButton( - icon: const Icon(Icons.add, color: Colors.blue), + icon: Icon( + Icons.add, + color: Theme.of(context).colorScheme.primary, + ), onPressed: _addMaterial, tooltip: '添加食材', ), ], ), - const SizedBox(height: 16), - if (_materials.isEmpty) 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( children: [ Icon(Icons.inventory_2, size: 48, color: Colors.grey[400]), @@ -185,8 +183,19 @@ class _RecipeFormPageState extends State { ), ) else - ..._materials.asMap().entries.map( - (entry) => _buildMaterialItem(entry.key, entry.value), + 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(), ), ], ), @@ -194,89 +203,45 @@ class _RecipeFormPageState extends State { } Widget _buildMaterialItem(int index, RecipeMaterial material) { - return Card( + return Column( key: ValueKey(material.id), - margin: const EdgeInsets.only(bottom: 12), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( + children: [ + Row( 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, - ), - ), + Expanded( + child: TextFormField( + initialValue: material.name, + decoration: buildInputDecoration( + context: context, + hintText: '请输入名称', + prefixIcon: Icons.content_paste, ), - const Spacer(), - IconButton( - icon: const Icon(Icons.delete, color: Colors.red, size: 20), - onPressed: () => _removeMaterial(index), - ), - ], + onChanged: (value) { + _updateMaterial(index, name: value); + }, + ), ), - const SizedBox(height: 12), - Row( - children: [ - Expanded( - child: TextFormField( - initialValue: material.type, - decoration: const InputDecoration( - labelText: '类型', - border: OutlineInputBorder(), - hintText: '如:主料、辅料', - ), - onChanged: (value) { - _updateMaterial(index, type: value); - }, - ), + const SizedBox(width: 12), + Expanded( + child: TextFormField( + initialValue: material.amount, + decoration: buildInputDecoration( + context: context, + hintText: '请输入用量', + prefixIcon: Icons.scale, ), - const SizedBox(width: 12), - Expanded( - flex: 2, - child: TextFormField( - initialValue: material.name, - decoration: const InputDecoration( - labelText: '食材名称', - border: OutlineInputBorder(), - hintText: '如:鸡蛋、西红柿', - ), - onChanged: (value) { - _updateMaterial(index, name: value); - }, - ), - ), - const SizedBox(width: 12), - Expanded( - child: TextFormField( - initialValue: material.amount, - decoration: const InputDecoration( - labelText: '用量', - border: OutlineInputBorder(), - hintText: '如:2个、100g', - ), - onChanged: (value) { - _updateMaterial(index, amount: value); - }, - ), - ), - ], + onChanged: (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 { children: [ Row( children: [ - Text('制作步骤', style: Theme.of(context).textTheme.headlineSmall), + Text('制作步骤', style: Theme.of(context).textTheme.titleMedium), const Spacer(), IconButton( - icon: const Icon(Icons.add, color: Colors.blue), + icon: Icon( + Icons.add, + color: Theme.of(context).colorScheme.primary, + ), onPressed: _addStep, tooltip: '添加步骤', ), ], ), - const SizedBox(height: 16), - if (_steps.isEmpty) 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( children: [ Icon(Icons.list_alt, size: 48, color: Colors.grey[400]), @@ -350,14 +318,19 @@ class _RecipeFormPageState extends State { ), ) else - ReorderableListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: _steps.length, - itemBuilder: - (context, index) => _buildStepItem(index, _steps[index]), - onReorder: - (oldIndex, newIndex) => _reorderStep(oldIndex, newIndex), + 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(), ), ], ), @@ -365,68 +338,50 @@ class _RecipeFormPageState extends State { } Widget _buildStepItem(int index, RecipeStep step) { - return Card( - key: Key('step_$index'), - margin: const EdgeInsets.only(bottom: 12), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - decoration: BoxDecoration( - color: Colors.green.shade50, - borderRadius: BorderRadius.circular(12), - ), - child: Text( - '步骤 ${index + 1}', - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.green, - ), - ), - ), - const Spacer(), - IconButton( - icon: const Icon(Icons.delete, color: Colors.red), - onPressed: () => _removeStep(index), - ), - ], + 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(height: 12), - TextFormField( - initialValue: step.content, - maxLines: 3, - decoration: const InputDecoration( - labelText: '步骤说明', - border: OutlineInputBorder(), - hintText: '详细描述此步骤的操作方法', - ), - onChanged: (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); - }, - ), - ], + ), ), - ), + 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), + ), + ], ); } @@ -434,7 +389,7 @@ class _RecipeFormPageState extends State { setState(() { _steps.add( RecipeStep( - id: DateTime.now().microsecond, + id: DateTime.now().microsecondsSinceEpoch, sort: _steps.length, content: '', imageUrl: '', @@ -446,15 +401,15 @@ class _RecipeFormPageState extends State { 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, - ); - } + // // 重新排序 + // 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, + // ); + // } }); } @@ -481,7 +436,7 @@ class _RecipeFormPageState extends State { setState(() { final step = _steps[index]; _steps[index] = RecipeStep( - id: DateTime.now().microsecond, + id: step.id, sort: step.sort, content: content ?? step.content, imageUrl: imageUrl ?? step.imageUrl, @@ -500,9 +455,13 @@ class _RecipeFormPageState extends State { 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), ], ), @@ -513,9 +472,9 @@ class _RecipeFormPageState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ _buildStepTitle(0, '基本信息'), - SizedBox(width: 25), + SizedBox(width: 40), _buildStepTitle(1, '食材清单'), - SizedBox(width: 25), + SizedBox(width: 40), _buildStepTitle(2, '制作步骤'), ], ), @@ -529,27 +488,26 @@ class _RecipeFormPageState extends State { Widget _buildStepCircle(int stepIndex) { final isActive = _currentStep == stepIndex; final isCompleted = _currentStep > stepIndex; - final boxColor = isActive || isCompleted - ? Theme.of(context).colorScheme.primary - : Colors.grey; + final boxColor = + isActive || isCompleted + ? Theme.of(context).colorScheme.primary + : Colors.grey; return Container( width: 32, height: 32, - decoration: BoxDecoration( - color: boxColor, - shape: BoxShape.circle, - ), + 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: isActive ? Colors.white : Colors.grey.shade700, - fontWeight: FontWeight.bold, - ), - ), + child: + isCompleted + ? const Icon(Icons.check, color: Colors.white, size: 16) + : Text( + '${stepIndex + 1}', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), ), ); } @@ -570,9 +528,10 @@ class _RecipeFormPageState extends State { 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; + final textColor = + isActive || isCompleted + ? Theme.of(context).colorScheme.primary + : Colors.grey; return Text( title, @@ -594,16 +553,10 @@ class _RecipeFormPageState extends State { children: [ if (!isFirstStep) Expanded( - child: OutlinedButton( + child: buildInfoButton( + context: context, + text: '上一步', 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), @@ -700,25 +653,25 @@ class _RecipeFormPageState extends State { 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); + // 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('请检查表单填写是否正确'); @@ -729,50 +682,37 @@ class _RecipeFormPageState extends State { 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); - } - }); - } - }); + // 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 colors = Theme.of(context).colorScheme; - final title = widget.initialData == null ? '创建新菜谱' : '编辑菜谱'; + final provider = context.watch(); - return Scaffold( - appBar: AppBar( - 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( + return SingleChildScrollView( + child: Padding( padding: EdgeInsets.all(10), - child: CommonCard( + child: GlassCard( + useOwnLayer: true, + settings: LiquidGlassSettings( + glassColor: Theme.of(context).scaffoldBackgroundColor, + ), child: Column( children: [ _buildStepIndicator(), - Expanded( - child: FormBuilder( - key: _formKey, - child: Padding( - padding: const EdgeInsets.all(16), - child: _buildStepContent(), - ), - ), - ), + FormBuilder(key: _formKey, child: _buildStepContent(provider)), _buildNavigationButtons(), ], ), diff --git a/lib/views/record.dart b/lib/views/record.dart index e16cddd..c255be9 100644 --- a/lib/views/record.dart +++ b/lib/views/record.dart @@ -1,10 +1,8 @@ -import 'package:easy_refresh/easy_refresh.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/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/widgets/common/easy_refresh.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/list.dart'; @@ -29,12 +27,6 @@ class RecordPage extends StatefulWidget { } class _RecordPageState extends State { - final EasyRefreshController _freshController = EasyRefreshController( - controlFinishRefresh: true, - controlFinishLoad: true, - ); - final ScrollController _scrollController = ScrollController(); - bool _showScrollToTop = false; final List _tabPages = [ RecipeList(userId: 0), RecipeCalendar(), @@ -44,53 +36,13 @@ class _RecordPageState extends State { @override void initState() { super.initState(); - _scrollController.addListener(_onScroll); } @override void dispose() { - _scrollController.removeListener(_onScroll); - _freshController.dispose(); - _scrollController.dispose(); super.dispose(); } - // 下拉刷新 - Future _onRefresh() async { - _freshController.finishRefresh(); - } - - // 上拉加载 - Future _onLoad() async { - final provider = context.read(); - 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({ required BuildContext context, required RecordTab currentTab, @@ -129,6 +81,25 @@ class _RecordPageState extends State { ); } + 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) { provider.resetMomentForm(); provider.isEditing = false; @@ -170,7 +141,7 @@ class _RecordPageState extends State { '新增菜谱', style: Theme.of(context).textTheme.bodyLarge, ), - onTap: () => {}, + onTap: () => _handleAddRecipe(provider), ), ListTile( leading: Icon(