import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_common/utils/log_utils.dart'; import 'package:flutter_common/utils/minio_utils.dart'; import 'package:flutter_common/utils/toast_util.dart'; import 'package:flutter_common/widget/common_widget.dart'; import 'package:flutter_common/widget/dialog_widget.dart'; import 'package:flutter_common/widget/loading_widget.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:food_hub_app/config/app_config.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/image.dart'; import 'package:provider/provider.dart'; class MomentFormPage extends StatefulWidget { const MomentFormPage({super.key}); @override State createState() => _MomentFormPageState(); } class _MomentFormPageState extends State { final _formKey = GlobalKey(); final _focusNode = FocusNode(); static const String _contentField = 'content'; static const int maxContentLength = 200; static const int maxImageCount = 9; // 最大图片数量 Widget _buildContentField(FoodProvider provider) { final colors = Theme.of(context).colorScheme; return FormBuilderTextField( name: _contentField, initialValue: provider.momentFormItem.content, focusNode: _focusNode, maxLines: 5, minLines: 3, maxLength: maxContentLength, buildCounter: ( context, { required currentLength, required isFocused, maxLength, }) { return Text( '$currentLength/$maxContentLength', style: TextStyle( color: currentLength > maxLength! ? Colors.red : colors.primary, ), ); }, onChanged: (value) { provider.updateMomentFormItem(content: value); }, decoration: buildInputDecoration(context: context, hintText: '请输入朋友圈内容'), validator: (value) { if (value == null || value.isEmpty) { return '请输入朋友圈内容'; } if (value.length > maxContentLength) { return '内容不能超过$maxContentLength字'; } return null; }, ); } Widget _buildImageField(FoodProvider provider) { final imageUrls = provider.momentFormItem.imageList; final totalItems = imageUrls.length + (imageUrls.length < maxImageCount ? 1 : 0); return GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, childAspectRatio: 4/3, ), itemCount: totalItems, itemBuilder: (context, index) { if (index < imageUrls.length) { return ImagePreview( imageUrls: imageUrls, index: index, onRemoveImage: () => _removeImage(provider, index), ); } // 上传按钮(最后一个) else { return buildImageUploadButton( context: context, onTap: () => _pickImages(provider), ); } }, ); } Widget _buildFormBuilder(FoodProvider provider) { return FormBuilder( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildFormLabel(context: context, text: '朋友圈内容', isRequired: true), const SizedBox(height: 8), _buildContentField(provider), const SizedBox(height: 8), buildFormLabel(context: context, text: '上传图片(最多9张)', isRequired: false), const SizedBox(height: 8), _buildImageField(provider), const SizedBox(height: 8), buildFormButtonGroup( context: context, isShowDelete: provider.isEditing, onConfirm: () => _submitForm(provider), onDelete: () {}, ), ], ), ); } /// 选择多张图片 Future _pickImages(FoodProvider provider) async { final currentCount = provider.momentFormItem.imageList.length; final remainingCount = maxImageCount - currentCount; if (remainingCount <= 0) { ToastUtil.error('最多只能上传$maxImageCount张图片'); return; } FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['jpg', 'jpeg', 'png'], allowMultiple: true, ); if (result != null) { // 限制选择数量 final filesToUpload = result.files.take(remainingCount).toList(); LoadingDialog.show(context, message: '上传中'); final newImageUrls = []; // 逐个上传图片 for (final file in filesToUpload) { final fileName = await MinIOHelper().uploadFile( bucketName: AppConfig.bucketName, file: file, ); logger.i('图片名称:$fileName'); newImageUrls.add(fileName); } // 更新图片列表 LoadingDialog.hide(context); provider.addMomentFormImage(newImageUrls); } } /// 移除单张图片 void _removeImage(FoodProvider provider, int index) { provider.removeMomentFormImage(index); } /// 提交表单 void _submitForm(FoodProvider provider) async{ if (_formKey.currentState?.saveAndValidate() ?? false) { LoadingDialog.show(context, message: '提交中'); final result = await provider.handleMoment(); if (result == true) { await provider.refreshMomentList(); LoadingDialog.hide(context); if (provider.isEditing) { showSuccessTip(context, '更新朋友圈成功'); } else { showSuccessTip(context, '新增朋友圈成功'); } Future.delayed(Duration(milliseconds: 1500), () { if (mounted) { Navigator.pop(context); } }); } else { LoadingDialog.hide(context); if (provider.isEditing) { showErrorTip(context, '更新朋友圈失败'); } else { showErrorTip(context, '新增朋友圈失败'); } } } } @override Widget build(BuildContext context) { final colors = Theme.of(context).colorScheme; final provider = context.watch(); final String title = provider.isEditing ? '编辑朋友圈' : '新增朋友圈'; 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: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(10), child: CommonCard(child: _buildFormBuilder(provider)), ), ), ); } }