Flutter:如何将视频按比例缩放以填满屏幕?

huangapple go评论103阅读模式
英文:

Flutter: How to scale a video to fill screen while keeping aspect ratio?

问题

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 too big, but I run into a lot of different problems in that approach as well.

All help is welcome Flutter:如何将视频按比例缩放以填满屏幕? thanks

英文:

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 Flutter:如何将视频按比例缩放以填满屏幕? thanks

答案1

得分: 1

Flutter:如何将视频按比例缩放以填满屏幕?

我建议使用ScaleTransition和包裹在其周围的GestureDetector来设置。您的小部件中需要这些属性。

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

double _lastZoomGestureScale = 1.0;

初始化动画控制器。

void setScale() { // 一旦知道视频和屏幕尺寸,可以调用此函数。
    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,
    ));
}

然后将您的VideoController包裹在GestureController中。

GestureDetector(
    onTap: () {}, // 触发播放/暂停
    onScaleUpdate: (details) {
        _lastZoomGestureScale = details.scale;
    },
    onScaleEnd: (details) {
        if (_lastZoomGestureScale > 1.0) {
            setState(() {
                // 放大
                _scaleVideoAnimationController.forward();
            });
        } else if (_lastZoomGestureScale < 1.0) {
            setState(() {
                // 缩小
                _scaleVideoAnimationController.reverse();
            });
        }
        _lastZoomGestureScale = 1.0;
    },
    child: ScaleTransition(
        scale: _scaleAnimation,
        child: AspectRatio(aspectRatio: 16 / 9, child: <your_videoPlayer>)
    )
);

您可以在这里的Github上找到此示例,我在那里使用VLCPlayer实现了类似的功能。不过,该机制对于任何视频播放小部件都应该适用。

英文:

Flutter:如何将视频按比例缩放以填满屏幕?

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&lt;double&gt; _scaleVideoAnimation = const AlwaysStoppedAnimation&lt;double&gt;(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&lt;double&gt;(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 &gt; 1.0) {
            setState(() {
                // Zoom in
                _scaleVideoAnimationController.forward();
            });
        } else if (_lastZoomGestureScale &lt; 1.0) {
            setState(() {
                // Zoom out
                _scaleVideoAnimationController.reverse();
            });
        }
        _lastZoomGestureScale = 1.0;
    },
    child: ScaleTransition(
        scale: _scaleAnimation,
        child: AspectRatio(aspectRatio: 16 / 9, child: &lt;your_videoPlayer&gt;)
    )
);

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.

huangapple
  • 本文由 发表于 2023年4月4日 04:41:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/75923610.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定