0

I want to make a product image upload when I select image from gallery using image picker it will send using REST API. I'm looking up how to do so and I still don't get it, I tried different methods but always getting an errors occur. So if anyone knows whats best way to send image as multipart data to API. My goal is to connect flutter is frontend and laravel is backend. Thanks in advance

Flutter:

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

import 'package:image_picker/image_picker.dart';
import 'package:myapp/utils/api.dart';
import 'package:path/path.dart';
import 'package:flutter/foundation.dart' show kIsWeb;



class AddProduct extends StatefulWidget {
const AddProduct({Key? key}) : super(key: key);

@override
State<AddProduct> createState() => _AddProductState();
}

class _AddProductState extends State<AddProduct> {
bool _isLoading = false;
File? _image;

final _picker = ImagePicker();
// Implementing the image picker
Future<void> _openImagePicker() async {
final XFile? pickedImage =
    await _picker.pickImage(source: ImageSource.gallery);
if (pickedImage != null) {
  setState(() {
    _image = File(pickedImage.path);
  });
}
}

@override
Widget build(BuildContext context) {

return Scaffold(
  body: Container(
    child: _isLoading ? const Center(child: CircularProgressIndicator()) : ListView(
      children: <Widget>[
        textSection(),
        imageProduct(),
        buttonSection(context) 

      ],
    ),
  ),
  appBar: AppBar(
      title: const Text('Add Product')
  ),      
);

}

Container buttonSection(BuildContext context) {
return Container(
  width: MediaQuery.of(context).size.width,
  height: 40.0,
  padding: const EdgeInsets.symmetric(horizontal: 15.0),
  margin: const EdgeInsets.only(top: 15.0),
  child: ElevatedButton(
    onPressed: () {
      setState(() {
        _isLoading = true;
      });          
      addProducts(context, File(_image!.path));
    },
    child: const Text("Create", style: TextStyle(color: Colors.white)),
  ),
);
}

final TextEditingController _productName = TextEditingController();
final TextEditingController _description = TextEditingController();
final TextEditingController _price = TextEditingController();

Container textSection() {
return Container(
  padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
  child: Column(
    children: <Widget>[
      TextFormField(
        controller: _productName,
        cursorColor: Colors.black,

        style: const TextStyle(color: Colors.black),
        decoration: const InputDecoration(
          hintText: "Product name",
          border: OutlineInputBorder(),
          hintStyle: TextStyle(color: Colors.black54),
        ),
      ),
      TextFormField(
        controller: _description,
        cursorColor: Colors.black,
        maxLines: 10,

        style: const TextStyle(color: Colors.black),
        decoration: const InputDecoration(
          hintText: "Description",
          border: OutlineInputBorder(),
          hintStyle: TextStyle(color: Colors.black54),
        ),
      ),
      TextFormField(
        controller: _price,
        cursorColor: Colors.black,

        style: const TextStyle(color: Colors.black),
        decoration: const InputDecoration(
          hintText: "Price",
          border: OutlineInputBorder(),
          hintStyle: TextStyle(color: Colors.black54),
        ),
      ), 
    ] 
  ),      
);
}

Widget imageProduct() {
return Center(
  child: Stack(children: <Widget>[
    SizedBox(
      width: 100,
      height: 100,
      child: _image != null ? (kIsWeb)
                ? Image.network(_image!.path,  fit: BoxFit.cover)
                : Image.file(_image!, fit: BoxFit.cover) : const Text('Please select an image'),
    ),        
    Positioned(
      bottom: 20.0,
      right: 20.0,
      child: InkWell(
        onTap: () {
          _openImagePicker();              
        },
        child: const Icon(
          Icons.camera_alt,
          color: Colors.teal,
          size: 28.0,
        ),
      ),
    ),
  ]),
);
}

Future addProducts(BuildContext context, File imageFile) async {

var token = 'abc';
 var request = http.MultipartRequest(
   'POST',
   Uri.parse(Api.baseUrl + 'product/new'),
);

Map<String, String> headers = {
  "Accept": "application/json",
  "Authorization": "Bearer " + token
};   
 request.files.add(await http.MultipartFile.fromPath('image', imageFile.path));
 request.headers.addAll(headers); 
 request.fields['product_name'] = _productName.text;
 request.fields['description'] = _description.text;
 request.fields['price'] = _price.text;
 
 var response = await request.send();
} 

}
dev100
  • 11
  • 3
  • Does this answer your question? [How to upload images and file to a server in Flutter?](https://stackoverflow.com/questions/49125191/how-to-upload-images-and-file-to-a-server-in-flutter) – Moaz El-sawaf Jun 02 '22 at 15:31
  • Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. – Community Jun 03 '22 at 02:17

1 Answers1

0

I convert image to base64, I send base64 in the json to the API. In the API a convert base64 to image.

In flutter :

Future<String?> convertImgToBase64() async {
    try {
      File img = File(_imageFile!.path);
      final splitted = _imageFile!.path.split('.');
      final ext = splitted.last;
      final response = await img.readAsBytes();
      return "data:image/$ext;base64,${base64Encode(response)}";
    } catch (e) {
      //print(e.toString());
      return null;
    }
  }

In laravel :

public function convertImage(Request $request, $id) {
        if($request->has('avatar')) {
            $data = $request->avatar;
            if (preg_match('/^data:image\/(\w+);base64,/', $data, $type)) {
                $data = substr($data, strpos($data, ',') + 1);
                $type = strtolower($type[1]);
                $name = "avatar_{$id}.{$type}" ;
                if (!in_array($type, [ 'jpg', 'jpeg', 'png' ])) {
                    throw new \Exception('Invalid image type');
                }
                $data = str_replace( ' ', '+', $data );
                $data = base64_decode($data);
                if ($data === false) {
                    throw new \Exception('Base64_decode failed');
                }
            } else {
                throw new \Exception('Did not match data URI with image data');
            }
            Storage::disk('local')->put("gallery/avatars/{$name}", $data);
            return $name;
            /*/if (file_put_contents($destination_path.$name, $data) != false) {
                return $name;
            } else {
                throw new \Exception('Unable to save image');
            }*/

        }
    }
}
mollusk
  • 139
  • 1
  • 8
  • Hi Thanks for sharing, I'm new with flutter here how to post to REST api with form elements with image single or multiple to upload? – dev100 Jun 03 '22 at 10:22
  • You can use dio or http package. Construct your json and pass`Future create(dynamic data) async { bool res = false; final response = await client.post('${url}family', data: data); if (response.statusCode == 200) { res = true; } }` – mollusk Jun 03 '22 at 10:50
  • Hi, still unable to resolve, after select and create uploading image have an issue, I shared code in my question section. Thanks `Future addProducts(BuildContext context, File imageFile) async { ..... request.fields['image'] = convertImgToBase64( imageFile) as String; var response = await request.send(); if(response.statusCode == 200) { print("succsss")}` – dev100 Jun 03 '22 at 13:17
  • Hi, with your code you can dialogue with api without send image ? For one image you can past the base64 directly in json without multipart for test. – mollusk Jun 04 '22 at 11:01
  • Hi, api without image is working, fine. I tried only image for test endpoint `Future uploadImage(File imageFile) async { var token = 'abc'; Map data = { 'image': convertImgToBase64( imageFile), }; var response = await http.post(Uri.parse(Api.baseUrl + 'product/new'), body: data, headers: { "Accept": "application/json", "Authorization": "Bearer " + token }); }` got Error: Expected a value of type 'String', but got one of type '_Future' – dev100 Jun 06 '22 at 13:19
  • Add await before convertImgToBase64() because it's a async function. – mollusk Jun 08 '22 at 11:12