diff --git a/lib/layout/app_actions.dart b/lib/layout/app_actions.dart index 1ae15e1..f024fb2 100644 --- a/lib/layout/app_actions.dart +++ b/lib/layout/app_actions.dart @@ -65,8 +65,8 @@ class AppActionsState extends State { void _handleAddRecipe(FoodProvider provider) { // provider.resetRecordForm(); - // Navigator.pop(context); - // Navigator.pushNamed(context, "/recordForm"); + Navigator.pop(context); + Navigator.pushNamed(context, "/recipeForm"); } void _handleAddRecord(FoodProvider provider) { diff --git a/lib/main.dart b/lib/main.dart index 585790f..29e1cd3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,6 +12,7 @@ import 'package:food_hub_app/views/moment.dart'; import 'package:food_hub_app/views/moment_form.dart'; import 'package:food_hub_app/views/moment_user.dart'; import 'package:food_hub_app/views/profile_user.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/recipe_detail.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; @@ -71,6 +72,7 @@ class MyApp extends StatelessWidget { routes: { '/home': (context) => HomePage(), '/recordForm': (context) => RecordFormPage(), + '/recipeForm': (context) => RecipeFormPage(), '/recipeDetail': (context) => RecipeDetailPage(), '/momentForm': (context) => MomentFormPage(), '/moment': (context) => MomentPage(), diff --git a/lib/models/recipe.dart b/lib/models/recipe.dart index d927714..6678390 100644 --- a/lib/models/recipe.dart +++ b/lib/models/recipe.dart @@ -5,11 +5,13 @@ part 'recipe.g.dart'; /// 食材信息 @JsonSerializable() class RecipeMaterial { + int id; String type; String name; String amount; RecipeMaterial({ + required this.id, required this.type, required this.name, required this.amount, @@ -24,11 +26,13 @@ class RecipeMaterial { /// 步骤信息 @JsonSerializable() class RecipeStep { + int id; int sort; String content; String imageUrl; RecipeStep({ + required this.id, required this.sort, required this.content, required this.imageUrl, diff --git a/lib/models/recipe.g.dart b/lib/models/recipe.g.dart index 539c885..04a2051 100644 --- a/lib/models/recipe.g.dart +++ b/lib/models/recipe.g.dart @@ -8,6 +8,7 @@ part of 'recipe.dart'; RecipeMaterial _$RecipeMaterialFromJson(Map json) => RecipeMaterial( + id: (json['id'] as num).toInt(), type: json['type'] as String, name: json['name'] as String, amount: json['amount'] as String, @@ -15,12 +16,14 @@ RecipeMaterial _$RecipeMaterialFromJson(Map json) => Map _$RecipeMaterialToJson(RecipeMaterial instance) => { + 'id': instance.id, 'type': instance.type, 'name': instance.name, 'amount': instance.amount, }; RecipeStep _$RecipeStepFromJson(Map json) => RecipeStep( + id: (json['id'] as num).toInt(), sort: (json['sort'] as num).toInt(), content: json['content'] as String, imageUrl: json['imageUrl'] as String, @@ -28,6 +31,7 @@ RecipeStep _$RecipeStepFromJson(Map json) => RecipeStep( Map _$RecipeStepToJson(RecipeStep instance) => { + 'id': instance.id, 'sort': instance.sort, 'content': instance.content, 'imageUrl': instance.imageUrl, diff --git a/lib/views/recipe_form.dart b/lib/views/recipe_form.dart new file mode 100644 index 0000000..04470d9 --- /dev/null +++ b/lib/views/recipe_form.dart @@ -0,0 +1,783 @@ +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/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'; + +class RecipeFormPage extends StatefulWidget { + final RecipeDetail? initialData; + + const RecipeFormPage({super.key, this.initialData}); + + @override + State createState() => _RecipeFormPageState(); +} + +class _RecipeFormPageState extends State { + final _formKey = GlobalKey(); + int _currentStep = 0; + List _materials = []; + List _steps = []; + + // 表单字段名称常量 + static const String _nameField = 'name'; + static const String _categoryField = 'category'; + static const String _recommendRateField = 'recommendRate'; + static const String _remarkField = 'remark'; + static const String _isShareField = 'isShare'; + + // 分类选项 + final List _categoryOptions = [ + '家常菜', + '川菜', + '粤菜', + '湘菜', + '西餐', + '甜品', + '汤羹', + '其他', + ]; + + @override + void initState() { + super.initState(); + _initializeFormData(); + } + + void _initializeFormData() { + if (widget.initialData != null) { + _materials = List.from(widget.initialData!.materialList); + _steps = List.from(widget.initialData!.stepList); + } + } + + // 构建步骤内容 + Widget _buildStepContent() { + switch (_currentStep) { + case 0: + return _buildBasicInfoStep(); + case 1: + return _buildMaterialsStep(); + case 2: + return _buildStepsStep(); + default: + return _buildBasicInfoStep(); + } + } + + // 第一步:基本信息 + Widget _buildBasicInfoStep() { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildFormLabel(context: context, text: '菜谱名称', isRequired: true), + const SizedBox(height: 8), + FormBuilderTextField( + name: _nameField, + initialValue: widget.initialData?.name ?? '', + decoration: buildInputDecoration( + context: context, + hintText: '请输入菜谱名称', + prefixIcon: Icons.restaurant_menu, + ), + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required(errorText: '菜谱名称不能为空'), + FormBuilderValidators.maxLength(50, errorText: '名称不能超过50个字符'), + ]), + ), + const SizedBox(height: 16), + + buildFormLabel(context: context, text: '分类', isRequired: true), + const SizedBox(height: 8), + FormBuilderDropdown( + name: _categoryField, + initialValue: widget.initialData?.category ?? '家常菜', + decoration: buildInputDecoration( + context: context, + hintText: '请选择分类', + prefixIcon: Icons.widgets, + ), + items: + _categoryOptions + .map( + (category) => DropdownMenuItem( + value: category, + child: Text(category), + ), + ) + .toList(), + validator: FormBuilderValidators.required(errorText: '请选择分类'), + ), + const SizedBox(height: 16), + + buildFormLabel(context: context, text: '推荐评分', isRequired: false), + const SizedBox(height: 8), + FormBuilderSlider( + name: _recommendRateField, + initialValue: widget.initialData?.recommendRate ?? 3.0, + min: 0, + max: 5, + divisions: 10, + activeColor: Colors.orange, + decoration: const InputDecoration(border: InputBorder.none), + ), + const SizedBox(height: 16), + + buildFormLabel(context: context, text: '备注', isRequired: false), + const SizedBox(height: 8), + FormBuilderTextField( + name: _remarkField, + initialValue: widget.initialData?.remark ?? '', + maxLines: 3, + decoration: buildInputDecoration( + context: context, + hintText: '请输入菜谱的特别说明或小贴士' + ) + ), + const SizedBox(height: 16), + + FormBuilderCheckbox( + name: _isShareField, + initialValue: widget.initialData?.isShare ?? false, + title: const Text('公开分享此菜谱'), + ), + ], + ), + ); + } + + // 第二步:食材清单 + Widget _buildMaterialsStep() { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text('食材清单', style: Theme.of(context).textTheme.headlineSmall), + const Spacer(), + IconButton( + icon: const Icon(Icons.add, color: Colors.blue), + onPressed: _addMaterial, + tooltip: '添加食材', + ), + ], + ), + const SizedBox(height: 16), + + if (_materials.isEmpty) + Container( + padding: const EdgeInsets.symmetric(vertical: 40), + child: Column( + children: [ + Icon(Icons.inventory_2, size: 48, color: Colors.grey[400]), + const SizedBox(height: 16), + Text( + '暂无食材,请点击添加按钮添加食材', + style: TextStyle(color: Colors.grey[600]), + ), + ], + ), + ) + else + ..._materials.asMap().entries.map( + (entry) => _buildMaterialItem(entry.key, entry.value), + ), + ], + ), + ); + } + + Widget _buildMaterialItem(int index, RecipeMaterial material) { + return Card( + key: ValueKey(material.id), + 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: 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( + 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( + 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); + }, + ), + ), + ], + ), + ], + ), + ), + ); + } + + void _addMaterial() { + setState(() { + _materials.add( + RecipeMaterial( + id: DateTime.now().microsecondsSinceEpoch, + type: '主料', + name: '', + amount: '', + ), + ); + }); + } + + void _removeMaterial(int index) { + setState(() { + _materials.removeAt(index); + }); + } + + void _updateMaterial( + int index, { + String? type, + String? name, + String? amount, + }) { + setState(() { + final material = _materials[index]; + _materials[index] = RecipeMaterial( + id: material.id, + type: type ?? material.type, + name: name ?? material.name, + amount: amount ?? material.amount, + ); + }); + } + + // 第三步:制作步骤 + Widget _buildStepsStep() { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text('制作步骤', style: Theme.of(context).textTheme.headlineSmall), + const Spacer(), + IconButton( + icon: const Icon(Icons.add, color: Colors.blue), + onPressed: _addStep, + tooltip: '添加步骤', + ), + ], + ), + const SizedBox(height: 16), + + if (_steps.isEmpty) + Container( + padding: const EdgeInsets.symmetric(vertical: 40), + child: Column( + children: [ + Icon(Icons.list_alt, size: 48, color: Colors.grey[400]), + const SizedBox(height: 16), + Text( + '暂无步骤,请点击添加按钮添加制作步骤', + style: TextStyle(color: Colors.grey[600]), + ), + ], + ), + ) + else + ReorderableListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: _steps.length, + itemBuilder: + (context, index) => _buildStepItem(index, _steps[index]), + onReorder: + (oldIndex, newIndex) => _reorderStep(oldIndex, newIndex), + ), + ], + ), + ); + } + + 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), + ), + ], + ), + 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); + }, + ), + ], + ), + ), + ); + } + + void _addStep() { + setState(() { + _steps.add( + RecipeStep( + id: DateTime.now().microsecond, + 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: DateTime.now().microsecond, + sort: step.sort, + content: content ?? step.content, + imageUrl: imageUrl ?? step.imageUrl, + ); + }); + } + + // 步骤指示器 + Widget _buildStepIndicator() { + return Container( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildStepCircle(0), + _buildStepConnector(0), + _buildStepCircle(1), + _buildStepConnector(1), + _buildStepCircle(2), + ], + ), + Container( + margin: const EdgeInsets.only(top: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildStepTitle(0, '基本信息'), + SizedBox(width: 25), + _buildStepTitle(1, '食材清单'), + SizedBox(width: 25), + _buildStepTitle(2, '制作步骤'), + ], + ), + ), + ], + ), + ); + } + + // 只构建圆圈,不包含标题文字 + Widget _buildStepCircle(int stepIndex) { + final isActive = _currentStep == stepIndex; + final isCompleted = _currentStep > stepIndex; + final boxColor = isActive || isCompleted + ? Theme.of(context).colorScheme.primary + : Colors.grey; + + return Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: boxColor, + shape: BoxShape.circle, + ), + child: Center( + child: isCompleted + ? const Icon(Icons.check, color: Colors.white, size: 16) + : Text( + '${stepIndex + 1}', + style: TextStyle( + color: isActive ? Colors.white : Colors.grey.shade700, + fontWeight: FontWeight.bold, + ), + ), + ), + ); + } + + // 箭头连接器 + Widget _buildStepConnector(int stepIndex) { + final isActive = _currentStep > stepIndex; + return SizedBox( + width: 40, + child: Icon( + Icons.arrow_forward, + size: 20, + color: isActive ? Theme.of(context).colorScheme.primary : Colors.grey, + ), + ); + } + + Widget _buildStepTitle(int stepIndex, String title) { + final isActive = _currentStep == stepIndex; + final isCompleted = _currentStep > stepIndex; + final textColor = isActive || isCompleted + ? Theme.of(context).colorScheme.primary + : Colors.grey; + + return Text( + title, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: isActive ? FontWeight.bold : FontWeight.normal, + color: textColor, + fontSize: 12, + ), + ); + } + + // 导航按钮 + Widget _buildNavigationButtons() { + final isLastStep = _currentStep == 2; + final isFirstStep = _currentStep == 0; + + return Row( + children: [ + if (!isFirstStep) + Expanded( + child: OutlinedButton( + 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), + Expanded( + child: buildPrimaryButton( + context: context, + text: isLastStep ? '完成创建' : '下一步', + onPressed: _nextStep, + ), + ), + ], + ); + } + + void _nextStep() { + if (_currentStep < 2) { + if (_formKey.currentState?.saveAndValidate() ?? false) { + setState(() { + _currentStep++; + }); + } + } else { + _submitForm(); + } + } + + void _previousStep() { + if (_currentStep > 0) { + setState(() { + _currentStep--; + }); + } + } + + bool _validateCurrentStep() { + return true; + switch (_currentStep) { + case 0: + // 验证基本信息 + final name = + _formKey.currentState?.fields[_nameField]?.value?.toString() ?? ''; + final category = + _formKey.currentState?.fields[_categoryField]?.value?.toString() ?? + ''; + + if (name.isEmpty) { + ToastUtil.error('请输入菜谱名称'); + return false; + } + if (category.isEmpty) { + ToastUtil.error('请选择分类'); + return false; + } + return true; + + case 1: + // 验证食材清单 + if (_materials.isEmpty) { + ToastUtil.error('请至少添加一个食材'); + return false; + } + + for (var material in _materials) { + if (material.name.isEmpty || material.amount.isEmpty) { + ToastUtil.error('请完善食材信息'); + return false; + } + } + return true; + + case 2: + // 验证制作步骤 + if (_steps.isEmpty) { + ToastUtil.error('请至少添加一个制作步骤'); + return false; + } + + for (var step in _steps) { + if (step.content.isEmpty) { + ToastUtil.error('请完善步骤说明'); + return false; + } + } + return true; + + default: + return true; + } + } + + void _submitForm() { + if (_formKey.currentState?.saveAndValidate() ?? false) { + if (_validateCurrentStep()) { + final formData = _formKey.currentState!.value; + + // 构建完整的RecipeDetail对象 + final recipe = RecipeDetail( + id: widget.initialData?.id ?? 0, + name: formData[_nameField], + category: formData[_categoryField], + recommendRate: formData[_recommendRateField] ?? 3.0, + remark: formData[_remarkField] ?? '', + isShare: formData[_isShareField] ?? false, + userId: widget.initialData?.userId ?? 0, + username: widget.initialData?.username ?? '', + avatar: widget.initialData?.avatar ?? '', + materialList: _materials, + stepList: _steps, + recordList: widget.initialData?.recordList ?? [], + likeList: widget.initialData?.likeList ?? [], + favouriteList: widget.initialData?.favouriteList ?? [], + commentList: widget.initialData?.commentList ?? [], + ); + + _saveRecipe(recipe); + } + } else { + ToastUtil.error('请检查表单填写是否正确'); + } + } + + void _saveRecipe(RecipeDetail recipe) { + LoadingDialog.show(context, message: '提交中'); + + // 模拟保存操作 + Future.delayed(const Duration(seconds: 2), () { + LoadingDialog.hide(context); + + if (mounted) { + ToastUtil.success(widget.initialData == null ? '菜谱创建成功!' : '菜谱更新成功!'); + + Future.delayed(const Duration(milliseconds: 1500), () { + if (mounted) { + Navigator.pop(context); + } + }); + } + }); + } + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + final title = widget.initialData == null ? '创建新菜谱' : '编辑菜谱'; + + 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( + padding: EdgeInsets.all(10), + child: CommonCard( + child: Column( + children: [ + _buildStepIndicator(), + Expanded( + child: FormBuilder( + key: _formKey, + child: Padding( + padding: const EdgeInsets.all(16), + child: _buildStepContent(), + ), + ), + ), + _buildNavigationButtons(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/common/form.dart b/lib/widgets/common/form.dart index 9f63c30..eb3da4f 100644 --- a/lib/widgets/common/form.dart +++ b/lib/widgets/common/form.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'index.dart'; + /// 通用输入框样式 InputDecoration buildInputDecoration({ required BuildContext context, @@ -87,40 +89,19 @@ Widget buildFormButtonGroup({ /// 取消按钮 Widget _buildCancelButton(BuildContext context) { - return ElevatedButton( - onPressed: () => Navigator.pop(context), // 直接使用传入的context返回 - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.surface, - foregroundColor: const Color(0xFF4E5969), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), - elevation: 0, - ), - child: const Text('取消'), + return buildInfoButton( + context: context, + text: '取消', + onPressed: () => Navigator.pop(context), ); } /// 提交按钮 Widget _buildSubmitButton(BuildContext context, VoidCallback onConfirm) { - return ElevatedButton( - onPressed: () => onConfirm(), - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.primary, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), - elevation: 0, - ), - child: const Text('提交', style: TextStyle(color: Colors.white)), - ); + return buildPrimaryButton(context: context, text: '提交', onPressed: onConfirm); } /// 删除按钮 Widget _buildDeleteButton(BuildContext context, VoidCallback onDelete) { - return ElevatedButton( - onPressed: () => onDelete(), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), - elevation: 0, - ), - child: const Text('删除', style: TextStyle(color: Colors.white)), - ); + return buildErrorButton(text: '删除', onPressed: onDelete); } diff --git a/lib/widgets/common/index.dart b/lib/widgets/common/index.dart index d7a9934..3bffd1d 100644 --- a/lib/widgets/common/index.dart +++ b/lib/widgets/common/index.dart @@ -92,13 +92,50 @@ Widget buildTag(BuildContext context, String title) { ); } -/// 确认按钮样式 -ButtonStyle primaryButtonStyle() { - return ButtonStyle( - backgroundColor: WidgetStateProperty.all(Colors.green), - shape: WidgetStateProperty.all( - RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), +Widget buildPrimaryButton({ + required BuildContext context, + required String text, + required VoidCallback onPressed, +}) { + return ElevatedButton( + onPressed: () => onPressed(), + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + elevation: 0, ), + child: Text(text, style: TextStyle(color: Colors.white)), + ); +} + +Widget buildErrorButton({ + required String text, + required VoidCallback onPressed, +}) { + return ElevatedButton( + onPressed: () => onPressed(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + elevation: 0, + ), + child: Text(text, style: TextStyle(color: Colors.white)), + ); +} + +Widget buildInfoButton({ + required BuildContext context, + required String text, + required VoidCallback onPressed, +}) { + return ElevatedButton( + onPressed: () => onPressed(), + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.surface, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + elevation: 0, + ), + child: Text(text, style: TextStyle(color: Colors.white)), ); }