feat:更新api模块
This commit is contained in:
@@ -34,9 +34,9 @@ Future<bool> addMomentCommentApi(int id, String content) {
|
||||
}
|
||||
|
||||
Future<bool> addMomentLikeApi(int id) {
|
||||
return HttpUtil().post<bool>("/moment/$id/like");
|
||||
return HttpUtil().post<bool>("/food/moment/$id/like");
|
||||
}
|
||||
|
||||
Future<bool> deleteMomentLikeApi(int id) {
|
||||
return HttpUtil().delete<bool>("/moment/$id/like");
|
||||
return HttpUtil().delete<bool>("/food/moment/$id/like");
|
||||
}
|
||||
@@ -10,15 +10,15 @@ Future<RecipeDetail> queryRecipeByIdApi(int id) {
|
||||
}
|
||||
|
||||
Future<bool> addRecipeApi(Recipe recipe) {
|
||||
return HttpUtil().post<bool>("/food/food/recipe", data: recipe);
|
||||
return HttpUtil().post<bool>("/food/recipe", data: recipe);
|
||||
}
|
||||
|
||||
Future<bool> updateRecipeApi(int id, Recipe recipe) {
|
||||
return HttpUtil().put<bool>("/food/food/recipe/$id", data: recipe);
|
||||
return HttpUtil().put<bool>("/food/recipe/$id", data: recipe);
|
||||
}
|
||||
|
||||
Future<bool> deleteRecipeApi(int id) {
|
||||
return HttpUtil().delete<bool>("/food/food/recipe/$id");
|
||||
return HttpUtil().delete<bool>("/food/recipe/$id");
|
||||
}
|
||||
|
||||
Future<List<Recipe>> queryRecipeByUserApi(int id) {
|
||||
@@ -3,6 +3,7 @@ class AppConfig {
|
||||
// 网络配置
|
||||
// http://192.168.1.3:8100
|
||||
// http://14.103.235.151:81/food-service
|
||||
static const String baseApiUrl = "http://14.103.235.151:81/food-service";
|
||||
// static const String baseApiUrl = "http://172.29.101.108:8100";
|
||||
// static const String baseApiUrl = "http://14.103.235.151:81/food-service";
|
||||
// static const String baseApiUrl = "http://192.168.1.3:8100";
|
||||
static const String baseApiUrl = "https://cxx0822.iepose.cn/food-api";
|
||||
}
|
||||
@@ -106,20 +106,10 @@ List<StatelessWidget> homeActions(BuildContext context) {
|
||||
// 搜索功能
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.add, color: Colors.white),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, '/recordForm');
|
||||
},
|
||||
),
|
||||
// Builder(
|
||||
// builder: (BuildContext context) {
|
||||
// return IconButton(
|
||||
// icon: Icon(Icons.settings, color: Colors.white),
|
||||
// onPressed: () {
|
||||
// Scaffold.of(context).openEndDrawer();
|
||||
// },
|
||||
// );
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.add, color: Colors.white),
|
||||
// onPressed: () {
|
||||
// Navigator.pushNamed(context, '/recordForm');
|
||||
// },
|
||||
// ),
|
||||
];
|
||||
|
||||
@@ -56,6 +56,74 @@ class _HomePage extends State<HomePage> {
|
||||
|
||||
List<Widget> get tabPages => navItems.map((item) => item.page).toList();
|
||||
|
||||
// 显示底部弹窗
|
||||
void _showBottomSheet() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(16),
|
||||
),
|
||||
),
|
||||
builder: (context) => Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'请选择操作',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ListTile(
|
||||
leading: Icon(Icons.book, color: Theme.of(context).primaryColor),
|
||||
title: const Text('新增菜谱'),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
_handleAddRecipe();
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.note_add, color: Theme.of(context).primaryColor),
|
||||
title: const Text('新增记录'),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, "/recordForm");
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.group, color: Theme.of(context).primaryColor),
|
||||
title: const Text('发布朋友圈'),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
_handleAddRecord();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 处理添加菜谱
|
||||
void _handleAddRecipe() {
|
||||
// 这里添加跳转或处理添加菜谱的逻辑
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('添加菜谱功能')),
|
||||
);
|
||||
}
|
||||
|
||||
// 处理添加记录
|
||||
void _handleAddRecord() {
|
||||
// 这里添加跳转或处理添加记录的逻辑
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('添加记录功能')),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -77,6 +145,13 @@ class _HomePage extends State<HomePage> {
|
||||
backgroundColor: Color(0xFFF5F5F5),
|
||||
drawer: const SettingsDrawer(),
|
||||
body: tabPages[_currentIndex],
|
||||
floatingActionButton: FloatingActionButton(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
onPressed: _showBottomSheet,
|
||||
shape: const CircleBorder(),
|
||||
child: const Icon(Icons.add, color: Colors.white, size: 30),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||
bottomNavigationBar: NavBar(
|
||||
navItems: bottomNavItems,
|
||||
currentIndex: _currentIndex,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:food_hub_app/api/session.dart';
|
||||
import 'package:food_hub_app/apis/session.dart';
|
||||
import 'package:food_hub_app/models/session.dart';
|
||||
import 'package:food_hub_app/utils/sp_util.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/api/moment.dart';
|
||||
import 'package:food_hub_app/apis/moment.dart';
|
||||
import 'package:food_hub_app/models/moment.dart';
|
||||
import 'package:food_hub_app/widgets/moment/card.dart';
|
||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/api/recipe.dart';
|
||||
import 'package:food_hub_app/apis/recipe.dart';
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
|
||||
|
||||
@@ -1,205 +1,194 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:food_hub_app/utils/index.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
import 'package:food_hub_app/widgets/common/form.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'dart:io';
|
||||
|
||||
class RecordFormPage extends StatefulWidget {
|
||||
const RecordFormPage({super.key});
|
||||
|
||||
@override
|
||||
State<RecordFormPage> createState() => _RecordFormPage();
|
||||
State<RecordFormPage> createState() => _RecordFormPageState();
|
||||
}
|
||||
|
||||
class _RecordFormPage extends State<RecordFormPage> {
|
||||
class _RecordFormPageState extends State<RecordFormPage> {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
|
||||
List<TextEditingController> _controller = [];
|
||||
FormController _formController = FormController();
|
||||
|
||||
String _selected_1 = '';
|
||||
String _selected_2 = '';
|
||||
String? _initLocalData;
|
||||
final _focusNode = FocusNode();
|
||||
static const String _nameField = 'name';
|
||||
static const String _dateField = 'date';
|
||||
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
XFile? _selectedImage;
|
||||
|
||||
/// 整个表单存放的数据
|
||||
Map<String, dynamic> _formData = {"name": '', "date": '', "photo": ''};
|
||||
|
||||
/// 定义整个校验规则
|
||||
final Map<String, TDFormValidation> _validationRules = {
|
||||
'name': TDFormValidation(
|
||||
validate: (value) => value == null || value.isEmpty ? 'empty' : null,
|
||||
errorMessage: '输入不能为空',
|
||||
type: TDFormItemType.input,
|
||||
),
|
||||
"birth": TDFormValidation(
|
||||
validate: (value) => value == null || value.isEmpty ? 'empty' : null,
|
||||
errorMessage: '不能为空',
|
||||
type: TDFormItemType.dateTimePicker,
|
||||
),
|
||||
"photo": TDFormValidation(
|
||||
validate: (value) => value == null || value.isEmpty ? 'empty' : null,
|
||||
errorMessage: '不能为空',
|
||||
type: TDFormItemType.upLoadImg,
|
||||
),
|
||||
};
|
||||
|
||||
List<TDUploadFile> files = [];
|
||||
|
||||
List<TDUploadFile> _onValueChanged(
|
||||
List<TDUploadFile> fileList,
|
||||
List<TDUploadFile> value,
|
||||
TDUploadType event,
|
||||
) {
|
||||
switch (event) {
|
||||
case TDUploadType.add:
|
||||
fileList.addAll(value);
|
||||
break;
|
||||
case TDUploadType.remove:
|
||||
fileList.removeWhere((element) => element.key == value[0].key);
|
||||
break;
|
||||
case TDUploadType.replace:
|
||||
final firstReplaceFile = value.first;
|
||||
final index = fileList.indexWhere(
|
||||
(file) => file.key == firstReplaceFile.key,
|
||||
);
|
||||
if (index != -1) {
|
||||
fileList[index] = firstReplaceFile;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
File? imageUrl; // 改为单张图片变量
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _confirmClick(BuildContext context) {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
// showSuccessToast("新增记录成功");
|
||||
print(_formData);
|
||||
} else {
|
||||
TDMessage.showMessage(
|
||||
context: context,
|
||||
visible: true,
|
||||
icon: true,
|
||||
content: "请先输入信息",
|
||||
theme: MessageTheme.error,
|
||||
duration: 3000,
|
||||
);
|
||||
}
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
TDFormItem buildNameItem() {
|
||||
return TDFormItem(
|
||||
label: '菜谱名称',
|
||||
name: 'name',
|
||||
type: TDFormItemType.input,
|
||||
labelWidth: 82.0,
|
||||
showErrorMessage: true,
|
||||
requiredMark: true,
|
||||
child: TDInput(
|
||||
leftContentSpace: 0,
|
||||
inputDecoration: InputDecoration(
|
||||
hintText: "请输入菜谱名称",
|
||||
border: InputBorder.none,
|
||||
hintStyle: TextStyle(color: TDTheme.of(context).grayColor6),
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('新增记录', style: TextStyle(color: Colors.white)),
|
||||
backgroundColor: theme.primaryColor,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
child: Card(
|
||||
elevation: 0,
|
||||
color: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: _buildFormBuilder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
additionInfoColor: TDTheme.of(context).errorColor6,
|
||||
showBottomDivider: false,
|
||||
onChanged: (value) {
|
||||
_formData['name'] = value;
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TDFormItem buildDateItem() {
|
||||
return TDFormItem(
|
||||
label: '完成时间',
|
||||
name: 'date',
|
||||
labelWidth: 82.0,
|
||||
type: TDFormItemType.dateTimePicker,
|
||||
contentAlign: TextAlign.left,
|
||||
tipAlign: TextAlign.left,
|
||||
hintText: '请选择完成时间',
|
||||
select: _formData['date'],
|
||||
selectFn: (BuildContext context) {
|
||||
DateTime now = DateTime.now();
|
||||
TDPicker.showDatePicker(
|
||||
context,
|
||||
title: '选择时间',
|
||||
onConfirm: (selected) {
|
||||
setState(() {
|
||||
_formData['date'] = parseDatePickerSelected(selected);
|
||||
});
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
dateStart: [2000, 01, 01],
|
||||
dateEnd: [2100, 12, 31],
|
||||
initialDate: [now.year, now.month, now.day],
|
||||
);
|
||||
Widget _buildFormBuilder() {
|
||||
return FormBuilder(
|
||||
key: _formKey,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildFormLabel('菜谱名称', required: true),
|
||||
const SizedBox(height: 8),
|
||||
_buildNameTextField(),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
buildFormLabel('完成时间', required: true),
|
||||
const SizedBox(height: 8),
|
||||
_buildDateTextField(),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
buildFormLabel('上传图片', required: true),
|
||||
const SizedBox(height: 10),
|
||||
_buildImageUploadArea(),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
buildFormButtonGroup(context: context, onConfirm: () => _submitForm),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 菜谱名称输入框
|
||||
Widget _buildNameTextField() {
|
||||
return FormBuilderTextField(
|
||||
name: _nameField,
|
||||
focusNode: _focusNode,
|
||||
decoration: buildInputDecoration(context: context, hintText: '请输入菜谱名称'),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '请输入菜谱名称';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
TDFormItem buildImageItem() {
|
||||
return TDFormItem(
|
||||
label: '上传图片',
|
||||
name: 'photo',
|
||||
labelWidth: 82.0,
|
||||
type: TDFormItemType.upLoadImg,
|
||||
child: TDUpload(
|
||||
files: files,
|
||||
onError: print,
|
||||
onValidate: print,
|
||||
onChange: ((imgList, type) {
|
||||
files = _onValueChanged(files ?? [], imgList, type);
|
||||
List imgs = files.map((e) => e.remotePath ?? e.assetPath).toList();
|
||||
setState(() {});
|
||||
}),
|
||||
/// 完成时间选择框
|
||||
Widget _buildDateTextField() {
|
||||
return FormBuilderTextField(
|
||||
name: _dateField,
|
||||
readOnly: true,
|
||||
decoration: buildInputDecoration(
|
||||
context: context,
|
||||
hintText: '请选择完成时间',
|
||||
prefixIcon: const Icon(
|
||||
Icons.calendar_month,
|
||||
size: 20,
|
||||
color: Color(0xFF86909C),
|
||||
),
|
||||
),
|
||||
onTap: () => _onSelectDate(),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '请选择完成时间';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildFormBtnGroup() {
|
||||
/// 图片上传区域(预览+上传按钮)
|
||||
Widget _buildImageUploadArea() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (imageUrl != null)
|
||||
_buildImagePreviewItem()
|
||||
else
|
||||
_buildImageUploadButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImagePreviewItem() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: TDTheme.of(context).whiteColor1),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Stack(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TDButton(
|
||||
text: '取消',
|
||||
type: TDButtonType.fill,
|
||||
theme: TDButtonTheme.light,
|
||||
shape: TDButtonShape.rectangle,
|
||||
onTap: () => Navigator.pop(context),
|
||||
GestureDetector(
|
||||
onTap: () => _showImagePreview(),
|
||||
child: Image(
|
||||
image: FileImage(imageUrl!),
|
||||
width: 120,
|
||||
height: 120,
|
||||
fit: BoxFit.cover,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return Container(
|
||||
width: 120,
|
||||
height: 120,
|
||||
color: Colors.grey[100],
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: TDButton(
|
||||
text: '提交',
|
||||
type: TDButtonType.fill,
|
||||
theme: TDButtonTheme.primary,
|
||||
shape: TDButtonShape.rectangle,
|
||||
onTap: () => _confirmClick(context),
|
||||
|
||||
Positioned(
|
||||
top: 6,
|
||||
right: 6,
|
||||
child: GestureDetector(
|
||||
onTap: _removeImage,
|
||||
child: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.red,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 2,
|
||||
spreadRadius: 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Icon(Icons.close, color: Colors.white, size: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -208,36 +197,137 @@ class _RecordFormPage extends State<RecordFormPage> {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('新增记录', style: TextStyle(color: Colors.white)),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context)
|
||||
),
|
||||
),
|
||||
backgroundColor: Color(0xFFF5F5F5),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: SingleChildScrollView(
|
||||
child: TDForm(
|
||||
formController: _formController,
|
||||
data: _formData,
|
||||
rules: _validationRules,
|
||||
formContentAlign: TextAlign.left,
|
||||
formShowErrorMessage: true,
|
||||
onSubmit: () => {},
|
||||
items: [buildNameItem(), buildDateItem(), buildImageItem()],
|
||||
btnGroup: [buildFormBtnGroup()],
|
||||
void _showImagePreview() {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: Colors.black87, // 半透明黑色背景
|
||||
builder:
|
||||
(context) => Dialog(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
insetPadding: const EdgeInsets.all(16),
|
||||
child: GestureDetector(
|
||||
onTap: () => Navigator.pop(context), // 点击空白处关闭
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
// 预览图添加轻微阴影
|
||||
boxShadow: [BoxShadow(color: Colors.black38, blurRadius: 10)],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Image.file(
|
||||
imageUrl!,
|
||||
fit: BoxFit.contain, // 保持图片比例
|
||||
height: MediaQuery.of(context).size.height * 0.7, // 限制最大高度
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 图片上传按钮
|
||||
Widget _buildImageUploadButton() {
|
||||
return InkWell(
|
||||
onTap: _pickImage,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
width: 96,
|
||||
height: 96,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF2F3F5),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: const Color(0xFFDCDFE6), width: 1),
|
||||
),
|
||||
child: const Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add, color: Color(0xFF86909C), size: 24),
|
||||
SizedBox(height: 6),
|
||||
Text(
|
||||
'添加图片',
|
||||
style: TextStyle(color: Color(0xFF86909C), fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 选择日期
|
||||
Future<void> _onSelectDate() async {
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(2000),
|
||||
lastDate: DateTime(2100),
|
||||
builder:
|
||||
(context, child) => Theme(
|
||||
data: ThemeData.light().copyWith(
|
||||
primaryColor: Theme.of(context).primaryColor,
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: Theme.of(context).primaryColor,
|
||||
),
|
||||
buttonTheme: const ButtonThemeData(
|
||||
textTheme: ButtonTextTheme.primary,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
),
|
||||
);
|
||||
|
||||
if (picked != null) {
|
||||
_formKey.currentState?.fields[_dateField]?.didChange(
|
||||
DateFormat('yyyy-MM-dd').format(picked),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 选择图片(限制单张)
|
||||
Future<void> _pickImage() async {
|
||||
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
|
||||
if (image != null) {
|
||||
setState(() {
|
||||
imageUrl = File(image.path); // 直接覆盖现有图片
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 移除图片
|
||||
void _removeImage() {
|
||||
setState(() {
|
||||
imageUrl = null;
|
||||
});
|
||||
}
|
||||
|
||||
/// 提交表单
|
||||
void _submitForm() {
|
||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||
if (imageUrl == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('请上传图片'),
|
||||
backgroundColor: Colors.red,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final formData = {
|
||||
..._formKey.currentState!.value,
|
||||
'imageUrl': imageUrl?.path, // 单张图片路径
|
||||
};
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('提交成功!'),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
print('表单数据: $formData');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/api/stats.dart';
|
||||
import 'package:food_hub_app/apis/stats.dart';
|
||||
import 'package:food_hub_app/models/stats.dart';
|
||||
import 'package:food_hub_app/widgets/common/chart.dart';
|
||||
import 'package:food_hub_app/widgets/stats/card.dart';
|
||||
|
||||
102
lib/widgets/common/form.dart
Normal file
102
lib/widgets/common/form.dart
Normal file
@@ -0,0 +1,102 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 通用输入框样式
|
||||
InputDecoration buildInputDecoration({
|
||||
required BuildContext context,
|
||||
required String hintText,
|
||||
Widget? prefixIcon,
|
||||
}) {
|
||||
return InputDecoration(
|
||||
hintText: hintText,
|
||||
hintStyle: const TextStyle(
|
||||
color: Color(0xFF999999),
|
||||
fontSize: 15,
|
||||
height: 1.2,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: Color(0xFFE5E7EB)),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Theme.of(context).primaryColor, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: Colors.red, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 14),
|
||||
errorStyle: const TextStyle(fontSize: 12, height: 1, color: Colors.red),
|
||||
prefixIcon: prefixIcon,
|
||||
prefixIconConstraints: const BoxConstraints(minWidth: 40),
|
||||
isDense: true,
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildFormLabel(String text, {bool required = false}) {
|
||||
return RichText(
|
||||
text: TextSpan(
|
||||
text: text,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF1D2129),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: 'CustomFont',
|
||||
height: 1.2,
|
||||
),
|
||||
children: [
|
||||
if (required)
|
||||
const TextSpan(
|
||||
text: ' *',
|
||||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 表单底部按钮组组件
|
||||
Widget buildFormButtonGroup({
|
||||
required BuildContext context,
|
||||
required VoidCallback onConfirm,
|
||||
}) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(child: _buildCancelButton(context)),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(child: _buildSubmitButton(context, onConfirm)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 取消按钮
|
||||
Widget _buildCancelButton(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context), // 直接使用传入的context返回
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: const Color(0xFF4E5969),
|
||||
side: const BorderSide(color: Color(0xFFDCDFE6)),
|
||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
elevation: 0,
|
||||
),
|
||||
child: const Text('取消'),
|
||||
);
|
||||
}
|
||||
|
||||
/// 提交按钮
|
||||
Widget _buildSubmitButton(BuildContext context, VoidCallback onConfirm) {
|
||||
return ElevatedButton(
|
||||
onPressed: onConfirm,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).primaryColor, // 使用传入的context获取主题
|
||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
elevation: 0,
|
||||
),
|
||||
child: const Text('提交', style: TextStyle(color: Colors.white)),
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/api/recipe.dart';
|
||||
import 'package:food_hub_app/apis/recipe.dart';
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/utils/date_util.dart';
|
||||
import 'package:food_hub_app/utils/index.dart';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/api/recipe.dart';
|
||||
import 'package:food_hub_app/apis/recipe.dart';
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/widgets/recipe/card.dart';
|
||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:food_hub_app/api/recipe.dart';
|
||||
import 'package:food_hub_app/apis/recipe.dart';
|
||||
import 'package:food_hub_app/config/app_config.dart';
|
||||
import 'package:food_hub_app/models/recipe.dart';
|
||||
import 'package:food_hub_app/widgets/common/image.dart';
|
||||
import 'package:food_hub_app/widgets/common/index.dart';
|
||||
import 'package:food_hub_app/widgets/common/year_selector.dart';
|
||||
import 'package:tdesign_flutter/tdesign_flutter.dart';
|
||||
import 'package:timelines_plus/timelines_plus.dart';
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace {
|
||||
///
|
||||
/// Redefined in case the developer's machine has a Windows SDK older than
|
||||
/// version 10.0.22000.0.
|
||||
/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||
/// See: https://docs.microsoft.com/windows/win32/apis/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user