Flutter Upload Image | Save to Server | image_picker

Flutter Upload Image | Save to Server | image_picker

    We will learn how to upload image from local storage or albums to app for Flutter. And then how to upload the image to the database mysql server using restful api using both Laravel and NodeJS.

    We will show it both for Laravel and Nodejs server using restful api.

    For this purpose we will use a plugin called image_picker.

    Go head and run

    flutter add image_picker
    flutter pub get

    This will install the latest image_picker plugin. Unfortunately this plugin doesn't work well Mac M1 chip.

    This plugin doesn't need any special set up for Android but it requires special settings in iOS info.plist

    So put the code in info.plist

    <key>NSCameraUsageDescription</key>
    <string>This app requires access to the camera.</string>

    See the complete code below

    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        Get.lazyPut(()=>ImageController());
        return const MaterialApp(
          // Hide the debug banner
          debugShowCheckedModeBanner: false,
          title: 'Image upload',
          home: HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      const HomePage({Key? key}) : super(key: key);
    
      @override
      _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      File? _image;
      PickedFile? _pickedFile;
      final _picker = ImagePicker();
      // Implementing the image picker
      Future<void> _pickImage() async {
       _pickedFile=
        await _picker.getImage(source: ImageSource.gallery);
        if (_pickedFile != null) {
          setState(() {
            _image = File(_pickedFile!.path);
          });
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: const Text('Image upload'),
            ),
            body: SafeArea(
              child: Padding(
                padding: const EdgeInsets.all(35),
                child: Column(children: [
                  Center(
                    child: GestureDetector(
                      child: const Text('Select An Image'),
                      //onPressed: _openImagePicker,
                      //onTap:()=> Get.find<ImageController>().pickImage(),
                      onTap: ()=>_pickImage(),
                    ),
                  ),
                  const SizedBox(height: 35),
                   Container(
                      alignment: Alignment.center,
                      width: double.infinity,
                      height: 200,
                      color: Colors.grey[300],
                      child: _pickedFile != null
                          ? Image.file(
                        File(_pickedFile!
                            .path), width: 100, height: 100, fit: BoxFit.cover,
                      )
                          : const Text('Please select an image'),
                    )
                ]),
              ),
            ));
      }
    }

    Server upload

    We need two things for image upload to the server.

    1. image controller

    2. restful api code

    Image Controller

    First we will create a new dart file name image_controller.dart and put the below variables. This image controller depends on Getx. This tutorial assumes you you know getx.

    class ImageController extends GetxController{
     PickedFile? _pickedFile;
     PickedFile? get pickedFile=>pickedFile;
    
    }

    If you want to upload the image to the server. You need to create a button. On the button press, call the below method upload() in the image_controller.dart

      Future<void> upload() async {
       
        http.StreamedResponse response = await updateImage(_pickedFile);
        _isLoading = false;
        if (response.statusCode == 200) {
          Map map = jsonDecode(await response.stream.bytesToString());
          String message = map["message"];
          _imagePath=message;
         // _pickedFile = null;
          //await getUserInfo();
          print(message);
        } else {
          print("error posting the image");
        }
        update();
    
      }

    Now we need to work on updateImage() method. Let's define it like below

      Future<http.StreamedResponse> updateProfile(PickedFile? data) async {
        http.MultipartRequest request = http.MultipartRequest('POST', Uri.parse('http://127.0.0.1:8000/upload'));
       // request.headers.addAll(<String,String>{'Authorization': 'Bearer $token'});
        if(GetPlatform.isMobile && data != null) {
          File _file = File(data.path);
          request.files.add(http.MultipartFile('image', _file.readAsBytes().asStream(), _file.lengthSync(), filename: _file.path.split('/').last));
        }
        Map<String, String> _fields = Map();
        _fields.addAll(<String, String>{
          'f_name': userInfoModel.fName,  'email': userInfoModel.email
        });
        request.fields.addAll(_fields);
        http.StreamedResponse response = await request.send();
        return response;
      }

    The above should send a token from your device to the server for authentication. But for the simplicity of this dummy app, we will just send the image.

    For sending the image we are using http.MultipartRequest.

    It's a post request which takes your base url for the server, and end point. See here we mention the endpoint.

    You should use your own end point. This api, saves the image path to the mysql database server.

    Restful Api

    Laravel upload

            public function upload(Request $request){
             $dir="test/";
             $image = $request->file('image');
          
            if ($request->has('image')) {
                    $imageName = \Carbon\Carbon::now()->toDateString() . "-" . uniqid() . "." . "png";
                    if (!Storage::disk('public')->exists($dir)) {
                        Storage::disk('public')->makeDirectory($dir);
                    }
                    Storage::disk('public')->put($dir.$imageName, file_get_contents($image));
            }else{
                 return response()->json(['message' => trans('/storage/test/'.'def.png')], 200);
            } 
    
            $userDetails = [
            
                'image' => $imageName,
             
            ];
    
           // User::where(['id' => 27])->update($userDetails);
    
            return response()->json(['message' => trans('/storage/test/'.$imageName)], 200);
        }

    This restful api end point should be pointed from your flutter app. Here I am using laravel

    Nodejs server upload

    We used express, multer and body-parser to build a simple nodejs server. Create a nodejs app and a file name app.js and put the below code in the app.js

    const express = require('express');
    const bodyParser = require('body-parser');
    const app = express();
    const multer = require('multer');
    
    /*------------------------------------------
    --------------------------------------------
    parse application/json
    --------------------------------------------
    --------------------------------------------*/
    app.use(bodyParser.json());
      
    /*------------------------------------------
    --------------------------------------------
    image upload code using multer
    --------------------------------------------
    --------------------------------------------*/
    var storage = multer.diskStorage({
       destination: function (req, file, cb) {
          cb(null, 'test');
       },
       filename: function (req, file, cb) {
          cb(null, Date.now() + '-' + file.originalname);
       }
    });
    var upload = multer({ storage: storage });
    app.use(express.static(__dirname+'public/'));
    app.use('/test', express.static('test'));
       
    /**
     * Create New Item
     *
     * @return response()
     */
    app.post('/upload', upload.single('image'),(req, res) => {
    
        res.send(apiResponse({message:  req.file.path}));
    });
      
    /**
     * API Response
     *
     * @return response()
     */
    function apiResponse(results){
        return JSON.stringify({"status": 200, "error": null, "message": results["message"]});
    }
      
    /*------------------------------------------
    --------------------------------------------
    Server listening
    --------------------------------------------
    --------------------------------------------*/
    app.listen(3000,() =>{
      console.log('Server started on port 3000...');
    });

    The above code will store image in the test folder inside app folder.

    Update ui using Getx

                  GetBuilder<ImageController>(builder:(_) {
                    return Container(
                      alignment: Alignment.center,
                      width: double.infinity,
                      height: 200,
                      color: Colors.grey[300],
                      child: Get
                          .find<ImageController>()
                          .pickedFile != null
                          ? Image.file(
                        File(Get
                            .find<ImageController>()
                            .pickedFile!
                            .path), width: 100, height: 100, fit: BoxFit.cover,
                      )
                          : const Text('Please select an image'),
                    );
                  }
                  ),
                  const SizedBox(height: 35),
                  Center(
                    child: GestureDetector(
                      child: const Text('Server upload'),
                      //onPressed: _openImagePicker,
                      onTap:()=> Get.find<ImageController>().updateUserInfo(),
                    ),
                  ),
                  const SizedBox(height: 35),
                  GetBuilder<ImageController>(builder:(_) {
                    return Container(
                      alignment: Alignment.center,
                      width: double.infinity,
                      height: 180,
                      color: Colors.grey[300],
                      child: Get
                          .find<ImageController>()
                          .imagePath != null
                          ? Image.network(
                        "http://mvs.bslmeiyu.com"+Get
                            .find<ImageController>()
                            .imagePath!,
                         width: 100, height: 100, fit: BoxFit.cover,
                      )
                          : const Text('No image from server'),
                    );
                  }
                  ),
    

     

    Check out the complete e-commerce for image uploading

    Courses


    Recommended posts


    Recent posts