Kennedy Mwenda
Kennedy Mwenda

Reputation: 33

Flutter MethodChannel: How to open the full featured camera

I opted to use Flutter MethodChannel to access the phone camera since the Flutter camera plugin seems to be limited for our use case.

Consider the code snippets below:

Flutter/Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Native Camera Test',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Native Camera Test'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const platform = MethodChannel('samples.flutter.dev/camera');

  Future<void> openCamera() async {
    // Check and request camera permission
    bool isCameraGranted = await _requestPermission(Permission.camera);

    if (isCameraGranted) {
      try {
        final String imagePath = await platform.invokeMethod('openCamera');
        print('Image Path: $imagePath');
      } on PlatformException catch (e) {
        print("Failed to open camera: '${e.message}'.");
      }
    } else {
      print('Permission denied');
    }
  }

  Future<bool> _requestPermission(Permission permission) async {
    // Check if the permission is already granted
    if (await permission.isGranted) {
      return true;
    }

    // Request permission if not already granted
    var status = await permission.request();
    return status == PermissionStatus.granted;
  }

  Future<void> imagePath(String imagePath) async {
    print("Hit from Kotlin");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(

Native/Kotlin

package com.example.nativecameratest

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import androidx.core.content.FileProvider
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.MethodChannel
import java.io.File
import java.io.IOException
import java.util.*

import io.flutter.embedding.engine.FlutterEngine
import androidx.annotation.NonNull

class MainActivity: FlutterActivity(){
    private val CHANNEL = "samples.flutter.dev/camera"
    private val REQUEST_IMAGE_CAPTURE = 1
    private lateinit var currentPhotoPath: String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Set up the method channel on the binaryMessenger
        MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "openCamera") {
                dispatchTakePictureIntent(result)
            } else {
                result.notImplemented()
            }
        }
    }

    private fun dispatchTakePictureIntent(result: MethodChannel.Result) {
        val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        if (takePictureIntent.resolveActivity(packageManager) != null) {
            val photoFile: File? = try {
                createImageFile()
            } catch (ex: IOException) {
                result.error("FILE_CREATION_FAILED", "Error occurred while creating the file", null)
                null
            }
            if (photoFile != null) {
                val photoURI = FileProvider.getUriForFile(this, "com.example.nativecameratest.fileprovider", photoFile)
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
                startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
            }
        }
    }

    private fun createImageFile(): File {
        val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        return File.createTempFile(
            "JPEG_${System.currentTimeMillis()}_", /* prefix */
            ".jpg", /* suffix */
            storageDir /* directory */
        ).apply {
            // Save a file path for use with ACTION_VIEW intents
            currentPhotoPath = absolutePath
            Log.d("MainActivity", "Image file created at: $currentPhotoPath")
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
            // Return the file path to Flutter
            MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL).invokeMethod("imagePath", currentPhotoPath)
        }
    }
}

The camera opens as shown on the image below: enter image description here

So here is what I intend to achieve:

  1. Open full featured camera as shown below: enter image description here
  1. Ability to take multiple photos before closing camera (Similar to when taking photos with WhatsApp) Which can be achieved if can be able access the full camera. If No.1 is not possible how can I go about it?

  2. Send photos (paths of the photos) back to Flutter/Dart

Upvotes: 1

Views: 159

Answers (0)

Related Questions