feat:更新上传记录表单
This commit is contained in:
@@ -1,10 +1,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:food_hub_app/models/layout.dart';
|
import 'package:food_hub_app/models/layout.dart';
|
||||||
|
import 'package:food_hub_app/models/recipe.dart';
|
||||||
|
import 'package:food_hub_app/provider/food_provider.dart';
|
||||||
import 'package:food_hub_app/views/moment.dart';
|
import 'package:food_hub_app/views/moment.dart';
|
||||||
import 'package:food_hub_app/views/profile.dart';
|
import 'package:food_hub_app/views/profile.dart';
|
||||||
import 'package:food_hub_app/views/record.dart';
|
import 'package:food_hub_app/views/record.dart';
|
||||||
import 'package:food_hub_app/views/stats.dart';
|
import 'package:food_hub_app/views/stats.dart';
|
||||||
|
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
final List<PageInfo> pageInfos = [
|
final List<PageInfo> pageInfos = [
|
||||||
PageInfo(
|
PageInfo(
|
||||||
label: "记录",
|
label: "记录",
|
||||||
@@ -69,6 +73,7 @@ void buildBottomSheet(BuildContext context) {
|
|||||||
|
|
||||||
Widget buildBottomSheetBody(BuildContext context) {
|
Widget buildBottomSheetBody(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
final provider = context.watch<FoodProvider>();
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -91,6 +96,7 @@ Widget buildBottomSheetBody(BuildContext context) {
|
|||||||
leading: Icon(Icons.note_add, color: colors.primary),
|
leading: Icon(Icons.note_add, color: colors.primary),
|
||||||
title: const Text('新增记录'),
|
title: const Text('新增记录'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
provider.resetRecordForm();
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
Navigator.pushNamed(context, "/recordForm");
|
Navigator.pushNamed(context, "/recordForm");
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class MinIOHelper {
|
|||||||
|
|
||||||
final String rustfsIp = '14.103.235.151';
|
final String rustfsIp = '14.103.235.151';
|
||||||
final String rustfsFileUrl = 'http://14.103.235.151:9100';
|
final String rustfsFileUrl = 'http://14.103.235.151:9100';
|
||||||
final String bucketName = 'flisp';
|
final String bucketName = 'food';
|
||||||
|
|
||||||
MinIOHelper._internal() {
|
MinIOHelper._internal() {
|
||||||
_minio = Minio(
|
_minio = Minio(
|
||||||
@@ -26,6 +26,8 @@ class MinIOHelper {
|
|||||||
|
|
||||||
late Minio _minio;
|
late Minio _minio;
|
||||||
|
|
||||||
|
String get imageBaseUrl => '$rustfsFileUrl/$bucketName/';
|
||||||
|
|
||||||
Future<String> uploadFile({
|
Future<String> uploadFile({
|
||||||
required PlatformFile file,
|
required PlatformFile file,
|
||||||
Function(double)? onProgress,
|
Function(double)? onProgress,
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import 'package:food_hub_app/utils/minio_utils.dart';
|
|||||||
import 'package:food_hub_app/widgets/common/form.dart';
|
import 'package:food_hub_app/widgets/common/form.dart';
|
||||||
import 'package:food_hub_app/widgets/common/image.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/index.dart';
|
||||||
import 'package:food_hub_app/models/recipe.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
@@ -24,7 +22,6 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
final _focusNode = FocusNode();
|
final _focusNode = FocusNode();
|
||||||
static const String _nameField = 'name';
|
static const String _nameField = 'name';
|
||||||
static const String _dateField = 'date';
|
static const String _dateField = 'date';
|
||||||
File? imageFile;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -56,7 +53,7 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFormBuilder() {
|
Widget _buildFormBuilder() {
|
||||||
final foodProvider = context.watch<FoodProvider>();
|
final provider = context.watch<FoodProvider>();
|
||||||
|
|
||||||
return FormBuilder(
|
return FormBuilder(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
@@ -66,101 +63,85 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
children: [
|
children: [
|
||||||
buildFormLabel('菜谱名称', required: true),
|
buildFormLabel('菜谱名称', required: true),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_buildNameTextField(),
|
FormBuilderTextField(
|
||||||
|
name: _nameField,
|
||||||
|
focusNode: _focusNode,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
provider.recordFormItem.name = value!;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
decoration: buildInputDecoration(
|
||||||
|
context: context,
|
||||||
|
hintText: '请输入菜谱名称',
|
||||||
|
prefixIcon: const Icon(
|
||||||
|
Icons.title,
|
||||||
|
size: 20,
|
||||||
|
color: Color(0xFF86909C),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return '请输入菜谱名称';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
buildFormLabel('完成时间', required: true),
|
buildFormLabel('完成时间', required: true),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_buildDateTextField(),
|
FormBuilderTextField(
|
||||||
|
name: _dateField,
|
||||||
|
readOnly: true,
|
||||||
|
decoration: buildInputDecoration(
|
||||||
|
context: context,
|
||||||
|
hintText: '请选择完成时间',
|
||||||
|
prefixIcon: const Icon(
|
||||||
|
Icons.calendar_month,
|
||||||
|
size: 20,
|
||||||
|
color: Color(0xFF86909C),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => _onSelectDate(provider),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return '请选择完成时间';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
buildFormLabel('上传图片', required: true),
|
buildFormLabel('上传图片', required: true),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
_buildImageUploadArea(),
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (provider.recordFormItem.imageUrl.isEmpty)
|
||||||
|
buildImageUploadButton(onPickImage: () => _pickImage(provider))
|
||||||
|
else
|
||||||
|
buildImagePreviewItem(
|
||||||
|
context: context,
|
||||||
|
imageUrl:
|
||||||
|
'${MinIOHelper().imageBaseUrl}${provider.recordFormItem.imageUrl}',
|
||||||
|
onRemoveImage: () => _removeImage(provider),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
buildFormButtonGroup(
|
buildFormButtonGroup(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => _submitForm(),
|
onConfirm: () => _submitForm(provider),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 菜谱名称输入框
|
|
||||||
Widget _buildNameTextField() {
|
|
||||||
return FormBuilderTextField(
|
|
||||||
name: _nameField,
|
|
||||||
focusNode: _focusNode,
|
|
||||||
decoration: buildInputDecoration(
|
|
||||||
context: context,
|
|
||||||
hintText: '请输入菜谱名称',
|
|
||||||
prefixIcon: const Icon(Icons.title, size: 20, color: Color(0xFF86909C)),
|
|
||||||
),
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) {
|
|
||||||
return '请输入菜谱名称';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 完成时间选择框
|
|
||||||
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 _buildImageUploadArea() {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (imageFile != null)
|
|
||||||
_buildImagePreviewItem()
|
|
||||||
else
|
|
||||||
buildImageUploadButton(onPickImage: _pickImage),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildImagePreviewItem() {
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
buildFileImage(context, imageFile!),
|
|
||||||
buildDeleteImage(onRemoveImage: _removeImage),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 选择日期
|
/// 选择日期
|
||||||
Future<void> _onSelectDate() async {
|
Future<void> _onSelectDate(FoodProvider provider) async {
|
||||||
final DateTime? picked = await showDatePicker(
|
final DateTime? picked = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: DateTime.now(),
|
initialDate: DateTime.now(),
|
||||||
@@ -169,14 +150,16 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (picked != null) {
|
if (picked != null) {
|
||||||
_formKey.currentState?.fields[_dateField]?.didChange(
|
final String date = DateFormat('yyyy-MM-dd').format(picked);
|
||||||
DateFormat('yyyy-MM-dd').format(picked),
|
_formKey.currentState?.fields[_dateField]?.didChange(date);
|
||||||
);
|
setState(() {
|
||||||
|
provider.recordFormItem.date = date;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 选择图片(限制单张)
|
/// 选择图片(限制单张)
|
||||||
Future<void> _pickImage() async {
|
Future<void> _pickImage(FoodProvider provider) async {
|
||||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
allowedExtensions: ['jpg', 'jpeg', 'png'],
|
allowedExtensions: ['jpg', 'jpeg', 'png'],
|
||||||
@@ -187,22 +170,22 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
PlatformFile file = result.files.first;
|
PlatformFile file = result.files.first;
|
||||||
final fileName = await MinIOHelper().uploadFile(file: file);
|
final fileName = await MinIOHelper().uploadFile(file: file);
|
||||||
setState(() {
|
setState(() {
|
||||||
// imageFile = File(image.path);
|
provider.recordFormItem.imageUrl = fileName;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 移除图片
|
/// 移除图片
|
||||||
void _removeImage() {
|
void _removeImage(FoodProvider provider) {
|
||||||
setState(() {
|
setState(() {
|
||||||
imageFile = null;
|
provider.recordFormItem.imageUrl = '';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 提交表单
|
/// 提交表单
|
||||||
void _submitForm() {
|
void _submitForm(FoodProvider provider) {
|
||||||
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
if (_formKey.currentState?.saveAndValidate() ?? false) {
|
||||||
if (imageFile == null) {
|
if (provider.recordFormItem.imageUrl.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('请上传图片'),
|
content: Text('请上传图片'),
|
||||||
@@ -213,18 +196,7 @@ class _RecordFormPageState extends State<RecordFormPage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final formData = {
|
print(provider.recordFormItem.imageUrl);
|
||||||
..._formKey.currentState!.value,
|
|
||||||
'imageUrl': imageFile?.path,
|
|
||||||
};
|
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(
|
|
||||||
content: Text('提交成功!'),
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
print('表单数据: $formData');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,6 +126,25 @@ Widget buildNetworkImage(BuildContext context, String url) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildImagePreviewItem({
|
||||||
|
required BuildContext context,
|
||||||
|
required String imageUrl,
|
||||||
|
required VoidCallback onRemoveImage,
|
||||||
|
}) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
buildNetworkImage(context, imageUrl),
|
||||||
|
buildDeleteImage(onRemoveImage: onRemoveImage),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildImageLoadingIndicator(ImageChunkEvent? loadingProgress) {
|
Widget buildImageLoadingIndicator(ImageChunkEvent? loadingProgress) {
|
||||||
return Center(
|
return Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
|
|||||||
Reference in New Issue
Block a user