1

I am trying to scale (zoom) a video to full screen, so there are no white borders while maintaining the aspect ratio of the video. I want it to overflow, I don't mind that some of the height or width is clipped.

But I struggle to produce code that works across all kind of different devices.

This is the code of which I expect it should have the intended behavior:

  double getScale() {
    double videoHeight = _controller.value.size.height;
    double videoWidth = _controller.value.size.width;

    double physicalHeight = window.physicalSize.height;
    double physicalWidth = window.physicalSize.width;

    double xScaleNeeded = physicalWidth / videoWidth;
    double yScaleNeeded = physicalHeight / videoHeight;

    double scale=1.0;
    // Take the axis that needs the biggest scale increase to fill the screen in that axis
    if(xScaleNeeded > yScaleNeeded){
      scale = xScaleNeeded;
    } else{
      scale = yScaleNeeded;
    }
    return scale;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: _controller.value.isInitialized
            ? Transform.scale(
                scale: getScale(),
                child: AspectRatio(
                  aspectRatio: _controller.value.aspectRatio,
                  child: GestureDetector(onTap: tappedVideo, child: VideoPlayer(_controller)),
                ),
              )
            : errorPlayback
                ? Center(
                    child: Column(
                    children: const [
                      Icon(Icons.error),
                      Text("Sorry, can't load the video :("),
                    ],
                  ))
                : const Center(child: CircularProgressIndicator()),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: playerControls(),
    );
  }

This code produces:

Nexus 10 emulator: overflow on x and y axis Nexus 7 emulator: no overflow but white bars on left and right side Pixel 6 Pro emulator: overflow in both x and y axis Samsung Galaxy S22 physical device: white bars on left and right side

The video and physical screen sizes are in pixels and seem to be right for the different devices. I use the VideoPlayer package from pub.dev, version 2.6.0

I also tried using ClipRect to cut a bit of the video in the axis that is to big but I run into a lot of different problems in that approach as well.

All help is welcome :) thanks

Harmen
  • 841
  • 8
  • 17

1 Answers1

1

Demo video

I suggest a setup using a ScaleTransition with a GestureDetector wrapped around it. You will need these properties in your widget.

late AnimationController _scaleVideoAnimationController;
Animation<double> _scaleVideoAnimation = const AlwaysStoppedAnimation<double>(1.0);

double _lastZoomGestureScale = 1.0;

Initialize the animation controller.

void setScale() { // You can call this as soon as video and screen dimensions are known.
    final newTargetScale = screenSize.width /
          (videoSize.width * screenSize.height / videoSize.height);

    _scaleVideoAnimation =
        Tween<double>(begin: 1.0, end: newValue).animate(CurvedAnimation(
      parent: _scaleVideoAnimationController,
      curve: Curves.easeInOut,
    ));
}

And wrap your VideoController inside a GestureController.

GestureDetector(
    onTap: () {}, // Trigger play/pause
    onScaleUpdate: (details) {
        _lastZoomGestureScale = details.scale;
    },
    onScaleEnd: (details) {
        if (_lastZoomGestureScale > 1.0) {
            setState(() {
                // Zoom in
                _scaleVideoAnimationController.forward();
            });
        } else if (_lastZoomGestureScale < 1.0) {
            setState(() {
                // Zoom out
                _scaleVideoAnimationController.reverse();
            });
        }
        _lastZoomGestureScale = 1.0;
    },
    child: ScaleTransition(
        scale: _scaleAnimation,
        child: AspectRatio(aspectRatio: 16 / 9, child: <your_videoPlayer>)
    )
);

You can find a sample of this here on Github, where I implemented something similar with a VLCPlayer. The mechanism should work for any video playing widget though.

jraufeisen
  • 3,005
  • 7
  • 27
  • 43
  • Thanks for posting such a comprehensive answer! I am still a bit hesitant to implement it. I am not looking for interactive zoom, but to have the video just fullscreen. I do not understand why this transition animation should be used. – Harmen Apr 14 '23 at 11:35
  • Thanks, then I misunderstood your question. If you have no need for such an animation, you can just remove it. It's not necessary by any means – jraufeisen Apr 14 '23 at 12:52