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();
}
}