Search code examples
flutterdartflutter-plugin

Flutter can't set CameraPreview widget to match screen size


How to set Flutter CameraPreview Size "Fullscreen"

Problem is same with this link however the solution isn't working on my phone (LG G5). Leaving a black margin around the camera preview for back camera however works fine for front camera but this time recorded video will cover a larger area than the preview video. I tried on other phones front and back camera preview works just fine but the actual recorded video covering more area problem still persists.

Edit: Problem originates from the Camera package itself not the framework for anyone out there who has a similar problem.

Further investigation and playing around with the package revealed that aspect ratio value that is returned from the package is not guaranteed to match the device's aspect ratio however works fine most of the cases.

 final size = MediaQuery.of(context).size;
    final deviceRatio = size.width / size.height;
    return Scaffold(
      key: _scaffoldKey,
      body: Stack(
        children: <Widget>[
          controller != null
              ? Center(
                  child: Transform.scale(
                    scale: controller.value.aspectRatio / deviceRatio,
                    child: new AspectRatio(
                      aspectRatio: controller.value.aspectRatio,
                      child: new CameraPreview(controller),
                    ),
                  ),
                )
              : Container(),
void onNewCameraSelected(CameraDescription cameraDescription) async {
    if (controller != null) {
      await controller.dispose();
    }
    controller = CameraController(
      cameraDescription,
      ResolutionPreset.high,
      enableAudio: enableAudio,
    );

    // If the controller is updated then update the UI.
    controller.addListener(() {
      if (mounted) setState(() {});
      if (controller.value.hasError) {
        showInSnackBar('Camera error ${controller.value.errorDescription}');
      }
    });

    try {
      await controller.initialize();
    } on CameraException catch (e) {
      _showCameraException(e);
    }

    if (mounted) {
      setState(() {
        print("controller inited");
      });
    }
  }

Solution

  • this is the same exact code from the docs and no use of ratio or scale found, and it works fine on every device I have including the emulator

    import 'dart:async';
    import 'dart:io';
    
    import 'package:camera/camera.dart';
    import 'package:flutter/material.dart';
    import 'package:path/path.dart' show join;
    import 'package:path_provider/path_provider.dart';
    
    Future<void> main() async {
      // Obtain a list of the available cameras on the device.
      final cameras = await availableCameras();
    
      // Get a specific camera from the list of available cameras.
      final firstCamera = cameras.first;
    
      runApp(
        MaterialApp(
          theme: ThemeData.dark(),
          home: TakePictureScreen(
            // Pass the appropriate camera to the TakePictureScreen widget.
            camera: firstCamera,
          ),
        ),
      );
    }
    
    // A screen that allows users to take a picture using a given camera.
    class TakePictureScreen extends StatefulWidget {
      final CameraDescription camera;
    
      const TakePictureScreen({
        Key key,
        @required this.camera,
      }) : super(key: key);
    
      @override
      TakePictureScreenState createState() => TakePictureScreenState();
    }
    
    class TakePictureScreenState extends State<TakePictureScreen> {
      CameraController _controller;
      Future<void> _initializeControllerFuture;
    
      @override
      void initState() {
        super.initState();
        // To display the current output from the Camera,
        // create a CameraController.
        _controller = CameraController(
          // Get a specific camera from the list of available cameras.
          widget.camera,
          // Define the resolution to use.
          ResolutionPreset.medium,
        );
    
        // Next, initialize the controller. This returns a Future.
        _initializeControllerFuture = _controller.initialize();
      }
    
      @override
      void dispose() {
        // Dispose of the controller when the widget is disposed.
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text('Take a picture')),
          // Wait until the controller is initialized before displaying the
          // camera preview. Use a FutureBuilder to display a loading spinner
          // until the controller has finished initializing.
          body: FutureBuilder<void>(
            future: _initializeControllerFuture,
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                // If the Future is complete, display the preview.
                return CameraPreview(_controller);
              } else {
                // Otherwise, display a loading indicator.
                return Center(child: CircularProgressIndicator());
              }
            },
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.camera_alt),
            // Provide an onPressed callback.
            onPressed: () async {
              // Take the Picture in a try / catch block. If anything goes wrong,
              // catch the error.
              try {
                // Ensure that the camera is initialized.
                await _initializeControllerFuture;
    
                // Construct the path where the image should be saved using the
                // pattern package.
                final path = join(
                  // Store the picture in the temp directory.
                  // Find the temp directory using the `path_provider` plugin.
                  (await getTemporaryDirectory()).path,
                  '${DateTime.now()}.png',
                );
    
                // Attempt to take a picture and log where it's been saved.
                await _controller.takePicture(path);
    
                // If the picture was taken, display it on a new screen.
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => DisplayPictureScreen(imagePath: path),
                  ),
                );
              } catch (e) {
                // If an error occurs, log the error to the console.
                print(e);
              }
            },
          ),
        );
      }
    }
    
    // A widget that displays the picture taken by the user.
    class DisplayPictureScreen extends StatelessWidget {
      final String imagePath;
    
      const DisplayPictureScreen({Key key, this.imagePath}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text('Display the Picture')),
          // The image is stored as a file on the device. Use the `Image.file`
          // constructor with the given path to display the image.
          body: Image.file(File(imagePath)),
        );
      }
    }