skip to Main Content

I’m working on a Flutter app where I’m trying to integrate an API to add restaurant items, including an image upload. The image upload works perfectly when tested with Postman, but it fails to upload the image from my Flutter app.

Here is the code I’m using in my Flutter app:

static Future<ApiGenericResponse> addRestaurantItem({
required String itemNameOrArabicName,
required String itemDescriptionOrArabicDescription,
required double price,
required File? image,
required bool isArabic,
required String status,
}) async {
final Map<String, dynamic> data = {
  if (isArabic)
    'item_arabic_name': itemNameOrArabicName
  else
    'item_name': itemNameOrArabicName,
  if (isArabic)
    'item_arabic_description': itemDescriptionOrArabicDescription
  else
    'item_description': itemDescriptionOrArabicDescription,
  'price': price,
  'status': status == LocaleKeys.active.tr() ? 1 : 0,
};

if (image != null) {
  data['image'] = await MultipartFile.fromFile(
    image.path,
  );
}

final form = await FormData.fromMap(data);

try {
  final response = await ApiHelper(apiService).callApi(
      endpoint: 'add-restaurant-item',
      requestType: RequestType.post,
      formData: form);

  if (response.success) {
    return ApiGenericResponse(
        success: true, data: response.data, message: response.message);
  } else {
    return ApiGenericResponse(success: false, message: response.message);
  }
} catch (e) {
  return ApiGenericResponse(success: false, message: e.toString());
   }
 }

In my backend logs, I see that the payload is received without the image:

[2024-07-25 07:30:19] local.INFO: array (
'item_name' => 'Log test',
'item_description' => 'log',
'price' => '11.0',
'status' => '0',
'lang' => 'en',
   )

Additional Information:

I have ensured that the Content-Type header is set to multipart/form-data. I have tried running the app on both an emulator and a physical device, but the issue persists. The image is accessible and its path is correctly passed to the MultipartFile.fromFile method.

2

Answers


  1. Chosen as BEST ANSWER

    I was using dio package. For some reason, dio's Multipart isn't uploading image to the server which is weird. When I tried calling the same api with http package, image upload worked. Here's my http api call code:

    static Future<ApiGenericResponse> addRestaurantItemHttp({
    required String itemNameOrArabicName,
    required String itemDescriptionOrArabicDescription,
    required double price,
    required File? image,
    required bool isArabic,
    required String status,
     }) async {
      var uri = Uri.parse('https://test6.accrualdev.com/api/add-     restaurant-   item');
    var request = http.MultipartRequest('POST', uri);
    
    // Adding fields to the request
    request.fields.addAll({
      if (isArabic)
        'item_arabic_name': itemNameOrArabicName
      else
        'item_name': itemNameOrArabicName,
      if (isArabic)
        'item_arabic_description': itemDescriptionOrArabicDescription
      else
        'item_description': itemDescriptionOrArabicDescription,
      'price': price.toString(),
      'status': status == LocaleKeys.active.tr() ? '1' : '0',
      'lang': isArabic ? 'en' : 'ar'
    });
    
    // Adding the image file if it exists
    if (image != null) {
      request.files.add(await http.MultipartFile.fromPath(
        'image',
        image.path,
      ));
    }
    final String? accessToken = SessionManager.instance.accessToken;
    
    request.headers.addAll({
      'Authorization': 'Bearer $accessToken',
    });
    
    try {
      // Sending the request
      var streamedResponse = await request.send();
    
      // Getting the response
      var response = await http.Response.fromStream(streamedResponse);
    
      // Checking the status code
      if (response.statusCode == 200) {
        var responseData = json.decode(response.body);
        if (responseData["status"] == true) {
          return ApiGenericResponse(
            success: true,
            data: responseData['data'],
            message: responseData['msg'],
          );
        } else
          return ApiGenericResponse(
              success: false, message: responseData["error"]);
      } else {
        var errorData = json.decode(response.body);
        return ApiGenericResponse(
          success: false,
          message: errorData['error'] ?? 'Unknown error occurred',
        );
      }
    } catch (e) {
      return ApiGenericResponse(
        success: false,
        message: e.toString(),
      );
      }
     }
    

  2. When you create a FormData object from a Map, it doesn’t automatically convert the MultipartFile object to a file field in the form data.

    Try this code, hope it works fine

    static Future<ApiGenericResponse> addRestaurantItem({
      required String itemNameOrArabicName,
      required String itemDescriptionOrArabicDescription,
      required double price,
      required File? image,
      required bool isArabic,
      required String status,
    }) async {
      final Map<String, dynamic> data = {
        if (isArabic)
          'item_arabic_name': itemNameOrArabicName
        else
          'item_name': itemNameOrArabicName,
        if (isArabic)
          'item_arabic_description': itemDescriptionOrArabicDescription
        else
          'item_description': itemDescriptionOrArabicDescription,
        'price': price,
        'tatus': status == LocaleKeys.active.tr()? 1 : 0,
      };
    
      final form = FormData();
    
      data.forEach((key, value) {
        form.fields.add(MapEntry(key, value.toString()));
      });
    
      if (image!= null) {
        form.files.add(MapEntry(
          'image',
          await MultipartFile.fromFile(
            image.path,
            filename: image.path.split('/').last,
          ),
        ));
      }
    
      try {
        final response = await ApiHelper(apiService).callApi(
          endpoint: 'add-restaurant-item',
          requestType: RequestType.post,
          formData: form,
        );
    
        if (response.success) {
          return ApiGenericResponse(
            success: true,
            data: response.data,
            message: response.message,
          );
        } else {
          return ApiGenericResponse(success: false, message: response.message);
        }
      } catch (e) {
        return ApiGenericResponse(success: false, message: e.toString());
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search