sendFormData method
Supports:
- Single-field multi-file uploads
- Multi-field multi-file uploads
- Multi-field single-file uploads
- Mixed upload modes
- Any combination of files + normal fields
- Automatic MIME type detection for every file
PARAMETERS:
singleFiles: List<File?> for a single fieldsingleFieldName: Field name for singleFilesfiles: Map<String, List<File?>> representing multi-field uploadsbody: additional text fields (Map<String, dynamic>)method: POST or PUT
The function automatically:
- Filters out null files
- Detects correct MIME types using
package:mime - Converts everything into MultipartFile
- Merges files + body into a single FormData object
- Sends with Dio POST/PUT
USAGE EXAMPLES
EXAMPLE 1 — Single-field multi-file
await sendFormData(
"/upload",
singleFiles: file1, file2,
singleFieldName: "images",
body: {"title": "My Gallery"},
);
EXAMPLE 2 — Multi-field single-file
await sendFormData(
"/upload",
files: {
"image_1": file1,
"image_2": file2,
},
body: {"user_id": 123},
);
EXAMPLE 3 — Multi-field multi-file
await sendFormData(
"/upload",
files: {
"documents": doc1, doc2,
"photos": p1, p2, p3,
},
);
EXAMPLE 4 — Mixed usage
await sendFormData(
"/upload-all",
singleFiles: avatar,
singleFieldName: "avatar",
files: {
"gallery": g1, g2, g3,
"attachments": a1, a2,
},
body: {"description": "Full upload"},
);
EXAMPLE 5 — Body only (no files)
await sendFormData( "/save", body: {"name": "John", "email": "john@mail.com"}, );
EXAMPLE 6 — PUT request
await sendFormData(
"/update",
method: FormDataMethod.put,
singleFiles: profileImage,
singleFieldName: "profile_image",
);
Returns: Dio Response
Implementation
Future<Response> sendFormData(
String endpoint, {
Map<String, List<File?>>? files, // multi-field multi-file
List<File?>? singleFiles, // single-field multi-file
String singleFieldName = 'image', // default name for singleFiles
Map<String, dynamic>? body, // normal fields
FormDataMethod method = FormDataMethod.post,
}) async {
final Map<String, dynamic> fileMap = {};
/// INTERNAL HELPER
/// Converts File -> MultipartFile with automatic MIME detection
Future<MultipartFile> toMultipart(File file) async {
final fileName = file.path.split('/').last;
// Detect MIME type (fallback to binary stream)
final mime = lookupMimeType(file.path) ?? 'application/octet-stream';
return MultipartFile.fromFile(
file.path,
filename: fileName,
contentType: MediaType.parse(mime),
);
}
// Handle single-field multi-file
if (singleFiles != null && singleFiles.isNotEmpty) {
final validFiles = singleFiles.where((f) => f != null).toList();
if (validFiles.isNotEmpty) {
final multipartFiles = await Future.wait(
validFiles.map((file) => toMultipart(file!)),
);
fileMap[singleFieldName] = multipartFiles;
}
}
// Handle multi-field multi-file
if (files != null && files.isNotEmpty) {
for (final entry in files.entries) {
final key = entry.key;
final validFiles = entry.value.where((f) => f != null).toList();
if (validFiles.isEmpty) continue;
final multipartFiles = await Future.wait(
validFiles.map((file) => toMultipart(file!)),
);
fileMap[key] = multipartFiles;
}
}
// Merge body + files into FormData
final formData = FormData.fromMap({
...?body,
...fileMap,
});
// Execute HTTP request
return method == FormDataMethod.post
? _dio.post(endpoint, data: formData)
: _dio.put(endpoint, data: formData);
}