skip to Main Content

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


  1. 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 the date_required field, which will properly handle the null value. You can use the dart:convert library for the fix.

    import 'dart:convert';
    // Rest of code
    String dateRequiredString = json.encode({
      'date_required': dateRequired != null
          ? dateRequired!.formatDbDate()
          : null
    });
    // ...rest of code
    request.fields.addAll(json.decode(dateRequiredString) as Map<String, String>);
                            // ^^^^^^ Do not forget to decode
    

    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 not Map. The problem will be resolved by typecasting decoded string to Map. A small sample snippet would be like:

    import 'dart:convert';
    
    void main() {
      Map<String, dynamic> data = {'date_required': null};
      
      String jsonString = jsonEncode(data);
      Map<String, dynamic> decodedMap = jsonDecode(jsonString);
      Map<String, String> dateRequiredString = decodedMap.cast<String, String>(); // <--- Notice this
      
      print(dateRequiredString);
    }
    
    Login or Signup to reply.
  2. The key is to correctly transform and map the data types between Dart’s Map<String, dynamic> and Map<String, String> (a bit as in this question).
    You want to:

    • send nullable data (date_required) along with file upload.
    • maintain compatibility between Map<String, dynamic> and Map<String, String>.

    Try and convert nullable date_required to a string representation. Then, make sure null values are handled correctly.

    Future<dynamic> uploadFile(BuildContext context, User user, String? dateRequired) async {
        // existing setup code
    
        // Convert nullable date to a string representation
        String dateRequiredStr = dateRequired != null ? jsonEncode({'date_required': dateRequired}) : jsonEncode({'date_required': null});
    
        // Adding fields to the request
        request.fields['data'] = dateRequiredStr;
    
        // remaining code for file upload
    }
    

    Use jsonEncode to convert the Map<String, dynamic> to a String. Assign this JSON string directly to a field in request.fields.


    For the Go API, you will need to parse the ‘data’ field as a JSON string and handle it accordingly.

    import 'dart:convert';
    
    void main() {
      Map<String, dynamic> data = {'date_required': null};
    
      String jsonString = jsonEncode(data); // Convert map to JSON string
    
      print(jsonString); // {"date_required":null}
    
      // Directly use jsonString in your request.fields
      // Example: request.fields['data'] = jsonString;
    }
    
    // Note: There is no need to convert back to Map<String, String> in Dart.
    // Handle the JSON parsing in your Go API.
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search