Flutter camera appears stretched

UPDATE: version v0.7.0 of the camera package introduced a breaking
change for calculating camera aspectRatio and it seems they add the AspectRatio widget by default

Well, inspired by previous posts, I got it to work on 2 screens with different screen aspect ratios. What I did was print the values and look for differences (not quite what you’d call an educated approach). What came out of that was that one screen was taller than the other, although the same width so the screen aspect ratio is different.

Disclaimer: This was tested in an app locked to portrait orientation, no guaranty that this will work in landscape orientation. Also, it seems that this does put some of the camera preview pixels outside the screen, but at the moment I have no remedy for that.

No matter which camera resolution was selected, the camera controller aspect ratio stayed the same, but the screen aspect ratio differed so the calculations mentioned in older posts than this one worked for one (shorter) screen but not the other (taller).

After some long calculations it became apparent that, to fill the screen, the scale factor has to be greater than 1. So, in the end, to get the scale to work on both screens, there needs to be a check if one ratio is greater than the other and switch operands, to get a scale factor greater than 1, accordingly.

TL;DR

Here is what was changed and end result:

Calculation

1. Camera package version v0.7.0 and above

  Widget cameraWidget(context) {
    var camera = _cameraController.value;
    // fetch screen size
    final size = MediaQuery.of(context).size;
        
    // calculate scale depending on screen and camera ratios
    // this is actually size.aspectRatio / (1 / camera.aspectRatio)
    // because camera preview size is received as landscape
    // but we're calculating for portrait orientation
    var scale = size.aspectRatio * camera.aspectRatio;

    // to prevent scaling down, invert the value
    if (scale < 1) scale = 1 / scale;

    return Transform.scale(
      scale: scale,
      child: Center(
        child: CameraPreview(_cameraController),
      ),
  );
}

2. Older versions – v0.6.6 and below

  Widget cameraWidget(context) {
    // get screen size
    final size = MediaQuery.of(context).size;

    // calculate scale for aspect ratio widget
    var scale = _cameraController.value.aspectRatio / size.aspectRatio;

    // check if adjustments are needed...
    if (_cameraController.value.aspectRatio < size.aspectRatio) {
      scale = 1 / scale;
    }

    return Transform.scale(
      scale: scale,
      child: Center(
        child: AspectRatio(
          aspectRatio: _cameraController.value.aspectRatio,
          child: CameraPreview(_cameraController),
        ),
      ),
    );
  }

Results

NOTE: results below are valid only for versions below 0.7.0 but the concept is the same

Notice that the scale has to be greater than 1 to fill the whole screen.

  1. taller screen with above calculation
Screen size (w/h): Size(411.4, 797.7)
Screen size aspect ratio: 0.515759312320917
Controller preview size: Size(1280.0, 720.0)
Controller aspect ratio: 0.5625
Scale: 1.0906249999999997
  1. shorter screen with above calculation
Screen size (w/h): Size(411.4, 683.4)
Screen size aspect ratio: 0.6020066889632107
Controller preview size: Size(1280.0, 720.0)
Controller aspect ratio: 0.5625
Scale: 1.0702341137123745
  1. shorter screen without above calculation
Screen size (w/h): Size(411.4, 683.4)
Screen size aspect ratio: 0.6020066889632107
Controller preview size: Size(1280.0, 720.0)
Controller aspect ratio: 0.5625
Scale: 0.934375

Leave a Comment