Search code examples
flutterdartflutter-dependencies

How to implement Tap To Focus in Flutter Camera Plugin?


A very simple question: how to implement tap to focus functionality for the Flutter camera plugin?

I’ve searched the entire World Wide Web about solutions, but I found nothing.

Does anyone have an idea?


Solution

  • You have to use the camera controller method to set the focus manually :

    controller.setFocusPoint(offset)
    

    See the official Api documention here for learn about method and camera functions.

    The method need a point to focus. The x,y point have to be > 0 and < 1 where 0 is top left point of the camera and 1 the bottom right.

    Then you have to retrieve your camera size and create proportions.

    Here is a full sample code that you can copy and run

    import 'package:camera/camera.dart';
    import 'package:flutter/material.dart';
    
    late List<CameraDescription> cameras;
    
    Future<void> main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      cameras = await availableCameras();
      runApp(const MaterialApp(home: CameraApp()));
    }
    
    class CameraApp extends StatefulWidget {
      const CameraApp({Key? key}) : super(key: key);
    
      @override
      _CameraAppState createState() => _CameraAppState();
    }
    
    class _CameraAppState extends State<CameraApp> {
      late CameraController controller;
      bool showFocusCircle = false;
      double x = 0;
      double y = 0;
    
      @override
      void initState() {
        super.initState();
        controller = CameraController(cameras[0], ResolutionPreset.max);
        controller.initialize().then((_) {
          if (!mounted) {
            return;
          }
          setState(() {});
        });
      }
    
      @override
      void dispose() {
        controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        if (!controller.value.isInitialized) {
          return Container();
        }
        return  GestureDetector(
          onTapUp: (details) {
            _onTap(details);
          },
            child: Stack(
              children: [
                Center(
                    child: CameraPreview(controller)
                ),
                if(showFocusCircle) Positioned(
                    top: y-20,
                    left: x-20,
                    child: Container(
                  height: 40,
                  width: 40,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    border: Border.all(color: Colors.white,width: 1.5)
                  ),
                ))
              ],
            )
        );
      }
    
    
      Future<void> _onTap(TapUpDetails details) async {
        if(controller.value.isInitialized) {
          showFocusCircle = true;
          x = details.localPosition.dx;
          y = details.localPosition.dy;
    
          double fullWidth = MediaQuery.of(context).size.width;
          double cameraHeight = fullWidth * controller.value.aspectRatio;
    
          double xp = x / fullWidth;
          double yp = y / cameraHeight;
    
          Offset point = Offset(xp,yp);
          print("point : $point");
    
          // Manually focus
          await controller.setFocusPoint(point);
          
          // Manually set light exposure
          //controller.setExposurePoint(point);
          
          setState(() {
            Future.delayed(const Duration(seconds: 2)).whenComplete(() {
              setState(() {
                showFocusCircle = false;
              });
            });
          });
        }
      }
    }
    

    Be aware to add bounds for the x,y point value to avoid crash if the tap is beyond the camera preview :

    if (point != null &&
          (point.dx < 0 || point.dx > 1 || point.dy < 0 || point.dy > 1)) {
        throw ArgumentError(
            'The values of point should be anywhere between (0,0) and (1,1).');
      }
    

    Besides, the focus point is not available for the IOS camera front as far as I know, so allow the focus tap only on back camera lens.