@@ -65,13 +65,17 @@ class VideoTrackRenderer extends StatefulWidget {
6565 final rtc.RTCVideoRenderer ? cachedRenderer;
6666 final bool autoDisposeRenderer;
6767
68+ /// wrap the video view in a Center widget (if [fit] is [VideoViewFit.contain] )
69+ final bool autoCenter;
70+
6871 const VideoTrackRenderer (
6972 this .track, {
7073 this .fit = VideoViewFit .contain,
7174 this .mirrorMode = VideoViewMirrorMode .auto,
7275 this .renderMode = VideoRenderMode .texture,
7376 this .autoDisposeRenderer = true ,
7477 this .cachedRenderer,
78+ this .autoCenter = true ,
7579 Key ? key,
7680 }) : super (key: key);
7781
@@ -83,6 +87,7 @@ class _VideoTrackRendererState extends State<VideoTrackRenderer> {
8387 rtc.VideoRenderer ? _renderer;
8488 // for flutter web only.
8589 bool _rendererReadyForWeb = false ;
90+ double ? _aspectRatio;
8691 EventsListener <TrackEvent >? _listener;
8792 // Used to compute visibility information
8893 late GlobalKey _internalKey;
@@ -121,6 +126,7 @@ class _VideoTrackRendererState extends State<VideoTrackRenderer> {
121126
122127 void disposeRenderer () {
123128 try {
129+ _renderer? .onResize = null ;
124130 _renderer? .srcObject = null ;
125131 _renderer? .dispose ();
126132 _renderer = null ;
@@ -167,6 +173,14 @@ class _VideoTrackRendererState extends State<VideoTrackRenderer> {
167173 // force recompute of mirror mode
168174 setState (() {});
169175 });
176+ _renderer? .onResize = () {
177+ if (mounted) {
178+ setState (() {
179+ _aspectRatio =
180+ (_renderer as rtc.RTCVideoRenderer ? )? .videoValue.aspectRatio;
181+ });
182+ }
183+ };
170184 }
171185
172186 @override
@@ -268,8 +282,52 @@ class _VideoTrackRendererState extends State<VideoTrackRenderer> {
268282 // FutureBuilder will cause flickering for flutter web. so using
269283 // different rendering methods for web and native.
270284 @override
271- Widget build (BuildContext context) =>
272- kIsWeb ? _videoViewForWeb () : _videoViewForNative ();
285+ Widget build (BuildContext context) {
286+ final child = kIsWeb ? _videoViewForWeb () : _videoViewForNative ();
287+
288+ if (widget.fit == VideoViewFit .cover) {
289+ return child;
290+ }
291+
292+ final videoView = LayoutBuilder (
293+ builder: (BuildContext context, BoxConstraints constraints) {
294+ if (! constraints.hasBoundedWidth && ! constraints.hasBoundedHeight) {
295+ return child;
296+ }
297+ if (_aspectRatio == null ) {
298+ return child;
299+ }
300+
301+ bool fixHeight;
302+ if (! constraints.hasBoundedWidth) {
303+ fixHeight = true ;
304+ } else if (! constraints.hasBoundedHeight) {
305+ fixHeight = false ;
306+ } else {
307+ // both width and height are bound, figure out which to fix based on aspect ratios
308+ final constraintsAspectRatio =
309+ constraints.maxWidth / constraints.maxHeight;
310+ fixHeight = constraintsAspectRatio > _aspectRatio! ;
311+ }
312+ final double width;
313+ final double height;
314+ if (fixHeight) {
315+ height = constraints.maxHeight;
316+ width = height * _aspectRatio! ;
317+ } else {
318+ width = constraints.maxWidth;
319+ height = width / _aspectRatio! ;
320+ }
321+ return SizedBox (width: width, height: height, child: child);
322+ },
323+ );
324+
325+ if (widget.autoCenter) {
326+ return Center (child: videoView);
327+ } else {
328+ return videoView;
329+ }
330+ }
273331
274332 bool _shouldMirror () {
275333 // off for screen share
0 commit comments