Search code examples
flutterdartfile-permissionsflutter-web

Unable to select image using Image Picker on Flutter Web (MissingPluginException)


Below I'm sharing my flutter code that simply selects an Image and displays it on screen after taking the required permissions. The code, however, runs fine on Android but gives a MissingPluginException(No implementation found for method requestPermissions on channel flutter.baseflow.com/permissions/methods) exception when I try to upload image on web.

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Project2',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  File _file = File("zz");

  uploadImage() async {
    final ImagePicker _picker = ImagePicker();
    final XFile? image;

    var permissionStatus = requestPermissions();
    if (await permissionStatus.isGranted) {
      image = await _picker.pickImage(source: ImageSource.gallery);
      var selected = File(image!.path);

      setState(() {
        _file = selected;
      });
    } else {
      showToast("Permission not granted");
    }
  }

  Future<PermissionStatus> requestPermissions() async {
    await Permission.photos.request();
    return Permission.photos.status;
  }

  void showToast(String message) {
    Fluttertoast.showToast(
      msg: message,
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.BOTTOM,
      timeInSecForIosWeb: 1,
      backgroundColor: Colors.red,
      textColor: Colors.white,
      fontSize: 16.0,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Upload Image"),
      ),
      body: Column(
        children: [
          (_file.path != "zz")
              ? Image.file(_file)
              : Image.asset("assets/img/images.jpeg"),
          SizedBox(
            height: 20,
            width: double.infinity,
          ),
          ElevatedButton(
            onPressed: () => uploadImage(),
            child: Text("Upload"),
          )
        ],
      ),
    );
  }
}

Following is the generated stacktrace on pressing upload button:

Error: MissingPluginException(No implementation found for method requestPermissions on channel flutter.baseflow.com/permissions/methods)
    at Object.throw_ [as throw] (http://localhost:64931/dart_sdk.js:5041:11)
    at MethodChannel._invokeMethod (http://localhost:64931/packages/flutter/src/services/system_channels.dart.lib.js:943:21)
    at _invokeMethod.next (<anonymous>)
    at http://localhost:64931/dart_sdk.js:37403:33
    at _RootZone.runUnary (http://localhost:64931/dart_sdk.js:37274:59)
    at _FutureListener.thenAwait.handleValue (http://localhost:64931/dart_sdk.js:32530:29)
    at handleValueCallback (http://localhost:64931/dart_sdk.js:33057:49)
    at Function._propagateToListeners (http://localhost:64931/dart_sdk.js:33095:17)
    at _Future.new.[_completeWithValue] (http://localhost:64931/dart_sdk.js:32943:23)
    at async._AsyncCallbackEntry.new.callback (http://localhost:64931/dart_sdk.js:32964:35)
    at Object._microtaskLoop (http://localhost:64931/dart_sdk.js:37526:13)
    at _startMicrotaskLoop (http://localhost:64931/dart_sdk.js:37532:13)
    at http://localhost:64931/dart_sdk.js:33303:9

pubspec.yaml file:

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  image_picker: ^0.8.3+3
  permission_handler: ^8.1.4+2
  fluttertoast: ^8.0.8

  cupertino_icons: ^1.0.2

dev_dependencies:
  flutter_test:
    sdk: flutter


flutter:
  uses-material-design: true

  assets:
    - assets/img/images.jpeg

PS: flutter clean and flutter run isn't working for Web version.


Solution

  • The problem is with the permissions handler package in Web. Permissions Handler package is built only for Android and IOS, and no permission is required to upload image on Web Platform, so using it on Web gives MissingPluginException.

    The problem was resolved by adding conditional statements and separate implementations for Web And Mobile Platforms.

    To check if the platform is web, you first need to add:

    import 'package:flutter/foundation.dart' show kIsWeb;
    

    Change the uploadImage() method as:

      uploadImage() async {
        var permissionStatus = requestPermissions();
    
        // MOBILE
        if (!kIsWeb && await permissionStatus.isGranted) {
          final ImagePicker _picker = ImagePicker();
          XFile? image = await _picker.pickImage(source: ImageSource.gallery);
    
          if (image != null) {
            var selected = File(image.path);
    
            setState(() {
              _file = selected;
            });
          } else {
            showToast("No file selected");
          }
        }
        // WEB
        else if (kIsWeb) {
          final ImagePicker _picker = ImagePicker();
          XFile? image = await _picker.pickImage(source: ImageSource.gallery);
          if (image != null) {
            var f = await image.readAsBytes();
            setState(() {
              _file = File("a");
              webImage = f;
            });
          } else {
            showToast("No file selected");
          }
        } else {
          showToast("Permission not granted");
        }
      }
    

    And then finally add seperate implementations for Web and Android Image in build() method:

      File _file = File("zz");
      Uint8List webImage = Uint8List(10);
    @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Upload Image"),
          ),
          body: Column(
            children: [
              (_file.path == "zz")
                  ? Image.asset("assets/img/images.jpeg")
                  : (kIsWeb)
                      ? Image.memory(webImage)
                      : Image.file(_file),
              SizedBox(
                height: 20,
                width: double.infinity,
              ),
              ElevatedButton(
                onPressed: () => uploadImage(),
                child: Text("Upload"),
              )
            ],
          ),
        );
      }