I have a flutter app in which I normally use http request post from a model to a go API like this.
if (dateRequired != null) {
data['date_required'] = dateRequired?.formatDbDate();
} else {
data['date_required'] = null;
}
However, I want to upload files and decode json data at the same time. The upload file as below works but the result I get from the code below produces either (2023-11-12)
or (null)
which throws a decode error in my api. I was expecting {"date_required": null}
or {"date_required": 2023-11-12}
.
How can I get to that? I notice that dart http uses Map<String, dynamic>
while dart MultipartRequest
uses Map<String, String>
so I can use null. This will require further parsing in my go api.
Future<dynamic> uploadFile(
BuildContext context, User user, String dateRequiredString) async {
final navigator = Navigator.of(context);
const storage = FlutterSecureStorage();
String? token = await storage.read(key: 'jwt');
Map<String, String> headers = {
"Authorization": "Bearer $token",
};
final bytes = file?.bytes;
if (file == null || bytes == null) return;
final multipartFile =
http.MultipartFile.fromBytes('file', bytes, filename: file?.name);
final request = http.MultipartRequest('POST', Uri.parse(user.fileUrl));
request.files.add(multipartFile);
request.headers.addAll(headers);
request.fields['date_required'] = dateRequiredString;
Dart code to produce the field, edited to in accordance with the answer from @mandy8055.
if (_formKey.currentState!.validate()) {
if (_formKey
.currentState
?.fields['date_required']
?.value !=
null) {
dateRequired = _formKey.currentState
?.fields['date_required']!.value;
}
Map<String, dynamic> data = {
'date_required': dateRequired != null
? dateRequired!.formatDbDate()
: null
};
print(data);
Map<String, String> dateRequiredString =
jsonEncode(data)
as Map<String, String>;
print(dateRequiredString);
This produces the following console output
{date_required: null}
Error: Expected a value of type 'Map<String, String>', but got one of type 'String'
further edit:
_formKey.currentState?.save();
if (_formKey.currentState!.validate()) {
if (_formKey
.currentState
?.fields['date_required']
?.value !=
null) {
dateRequired = _formKey.currentState
?.fields['date_required']!.value;
}
Map<String, dynamic> data = {
'date_required': dateRequired != null
? dateRequired!.formatDbDate()
: null
};
String jsonString = jsonEncode(data);
Map<String, dynamic> decodedMap =
jsonDecode(jsonString);
Map<String, String> dateRequiredString =
decodedMap.cast<String, String>();
print(data);
print(dateRequiredString);
uploadFile(
context, user, dateRequiredString);
}
this throws the error below (the date is meant to be null here). I see the key is not quoted but I think it should be quoted.
{date_required: null}
Error: Expected a value of type 'String', but got one of type 'Null'
Here is some working code to demonstrate the issue. I need to convert the Map<String, dynamic>
to Map<String, String>
and retain the quotes around the "date_required" key.
import 'dart:convert';
void main() {
Map<String, dynamic> data = {'date_required': null};
print(data);
String jsonString = jsonEncode(data);
print(jsonString);
Map<String, dynamic> decodedMap = jsonDecode(jsonString);
print(decodedMap);
Map<String, String> dateRequiredString = decodedMap.cast<String, String>();
print(dateRequiredString);
}
Console output:
{date_required: null}
{"date_required":null}
{date_required: null}
: TypeError: null: type 'JSNull' is not a subtype of type 'String'Error: TypeError: null: type 'JSNull' is not a subtype of type 'String'
2
Answers
The issue is due to the conversion of
date_required
value to a string using.toString()
function. The fix for this would be to use JSON encoding for thedate_required
field, which will properly handle thenull
value. You can use thedart:convert
library for the fix.EDIT:
I am not aware about your full code requirement. But I’ll try to provide the hint to get rid of the error which you posted in the comment. As per the docs, jsonEncode returns
string
and notMap
. The problem will be resolved by typecasting decoded string toMap
. A small sample snippet would be like:The key is to correctly transform and map the data types between Dart’s
Map<String, dynamic>
andMap<String, String>
(a bit as in this question).You want to:
date_required
) along with file upload.Map<String, dynamic>
andMap<String, String>
.Try and convert nullable
date_required
to a string representation. Then, make surenull
values are handled correctly.Use
jsonEncode
to convert theMap<String, dynamic>
to aString
. Assign this JSON string directly to a field inrequest.fields
.For the Go API, you will need to parse the ‘data’ field as a JSON string and handle it accordingly.