Description
Is there an existing issue for this?
- I have searched the existing issues
Current Behavior
Observed a strange behavior in iOS, where when I played the vimeo video, it was played. Then I changed the orientation between portrait and landscape, it was getting played. When I came back of the player, it showed black screen on video player, though audio was still coming.
iOS device - iPhone SE (OS version - 15.7.3)
Expected Behavior
It should show the video and audio in sync
Steps with code example to reproduce
String? _vimeoIframeData = '';
String? _videoId = '';
Widget _vimeoPlayerWidget = WaitScreen();
@OverRide
void initState() {
super.initState();
_getVimeoVideoData();
// _loadVimeoIframeData();
}
Widget _videoPlayerMobile({
String? iFrameCode,
String? videoId,
Key? key,
}) {
String? srcValue;
try {
var document = parse(iFrameCode ?? widget.videoUrl);
var element = document.getElementsByTagName('iframe').elementAtOrNull(0);
srcValue = element?.attributes['src'] ?? '';
} catch (e) {
print('Error while fetching IFrame src from vimeo video embeded code');
srcValue = null;
}
final isLoading = true.obs;
return ObxValue(
(RxBool state) {
return Stack(
children: [
AspectRatio(
aspectRatio: 16 /
((srcValue != null && srcValue.isNotEmpty)
? 10
: 11), // if there is direct player url available, then using aspectRatio as 16/10 , else using 16/11
child: Stack(
children: [
state.value
? WaitScreen(
bgColor: Colors.black.withValues(alpha: .3),
)
: const SizedBox(),
Container(
child: InAppWebView(
key: key,
initialSettings: InAppWebViewSettings(
mediaPlaybackRequiresUserGesture: false,
allowsInlineMediaPlayback: true,
useHybridComposition: true,
),
initialData: InAppWebViewInitialData(
data: _buildHtmlContent(videoId),
baseUrl: WebUri("https://player.vimeo.com"),
),
onConsoleMessage: (controller, consoleMessage) {
final message = consoleMessage.message;
if (message.startsWith('vimeo:')) {
_manageVimeoPlayerEvent(message.substring(6));
}
},
onWebViewCreated: (controller) {
widget.webViewController = controller;
widget.onInAppWebViewCreated?.call(controller);
},
onLoadStart: widget.onInAppWebViewLoadStart,
onLoadStop: (controller, url) {
isLoading.value = false;
widget.onInAppWebViewLoadStop?.call(controller, url);
},
onReceivedError: (controller, request, error) {
isLoading.value = false;
// onReceivedError will be called also for subframes
// which cause the dialog to display when there is no error
// in playing the video (Fix for MIN-11697)
if (!(request.isForMainFrame ?? true)) return;
},
shouldOverrideUrlLoading:
(controller, navigationAction) async {
var uri = navigationAction.request.url;
// Allow Vimeo links to load
if (uri != null &&
uri.toString().contains("player.vimeo.com")) {
return NavigationActionPolicy.ALLOW;
}
// Block other external navigation (e.g., ads, redirects)
return NavigationActionPolicy.CANCEL;
},
onEnterFullscreen: widget.onEnterFullscreen,
onExitFullscreen: widget.onExitFullscreen,
),
),
],
),
),
],
);
},
isLoading,
);
}
Future _getVimeoVideoData() async {
// fetch vimeo video id
String? vimeoVideoId;
if (widget.videoUrl.isNotEmpty && widget.videoUrl.contains('vimeo.com/')) {
var arr = widget.videoUrl.split('/');
if (arr.length > 1) {
vimeoVideoId = arr.elementAtOrNull(arr.length - 1);
}
}
// fetch vimeo IFrameData
var vimeoIframeData =
await UtilityMethods().getVimeoPlayerIFrame(widget.videoUrl);
_setVideoData(
vimeoVideoId,
vimeoIframeData,
);
}
void _setVideoData(String? vimeoVideoId, String? vimeoIframeData) {
setState(() {
_videoId = vimeoVideoId ?? '';
_vimeoIframeData = vimeoIframeData;
_setVimeoPlayerWidget();
});
}
void _setVimeoPlayerWidget() {
_vimeoPlayerWidget = (_videoId ?? '').isNotEmpty
? _videoPlayerMobile(
iFrameCode: _vimeoIframeData,
videoId: videoId,
key: Key(
'embedCodeVideoPlayerMobileWidget${base64Encode(utf8.encode(widget.videoUrl))}',
),
)
: WaitScreen();
}
String _buildHtmlContent(String? videoId) {
return '''
<style>
body {
margin: 0;
padding: 0;
background-color: #000000;
}
.video-container {
position: relative;
width: 100%;
height: 100vh;
}
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
<script src="https://player.vimeo.com/api/player.js"></script>
<iframe id="player" src="${_buildIframeUrl(videoId ?? '')}" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen webkitallowfullscreen mozallowfullscreen>
</iframe>
<script>
const player = new Vimeo.Player('player');
player.ready().then(() => console.log('vimeo:onReady'));
player.on('play', () => console.log('vimeo:onPlay'));
player.on('pause', () => console.log('vimeo:onPause'));
player.on('ended', () => console.log('vimeo:onFinish'));
player.on('seeked', () => console.log('vimeo:onSeek'));
</script>
''';
}
/// Builds the iframe URL
String _buildIframeUrl(String videoId) {
var iFrameURL = 'https://player.vimeo.com/video/$videoId?'
'autoplay=${widget.isAutoPlay}'
'&loop=${widget.isLooping}'
'&muted=${widget.isMuted}'
'&title=${widget.showTitle}'
'&byline=${widget.showByline}'
'&controls=${widget.showControls}'
'&dnt=${widget.enableDNT}';
return iFrameURL;
}
/// Manage vimeo player events received from the WebView
void _manageVimeoPlayerEvent(String event) {
debugPrint('Vimeo event: $event');
switch (event) {
case 'onReady':
widget.onReady?.call();
break;
case 'onPlay':
widget.onPlay?.call();
break;
case 'onPause':
widget.onPause?.call();
break;
case 'onFinish':
widget.onFinish?.call();
break;
case 'onSeek':
widget.onSeek?.call();
break;
}
}
Future _cleanupAndGoBack() async {
await widget.webViewController?.evaluateJavascript(source: '''
var iframe = document.querySelector("iframe");
if (iframe && iframe.contentWindow) {
iframe.contentWindow.postMessage('{"method":"pause"}', '*');
}
''');
await widget.webViewController?.loadData(
data: "<html></html>",
baseUrl: WebUri("about:blank"),
);
await Future.delayed(Duration(milliseconds: 100));
widget.webViewController?.dispose();
Get.back();
}
@OverRide
Widget build(BuildContext context) {
return PopScope(
canPop: true,
onPopInvokedWithResult: (bool didPop, Object? result) {
if (didPop) {
_cleanupAndGoBack();
}
},
child: _videoId == null
? (widget.errorWidget ?? UIUtil().renderVideoErrorWidget())
: ((_videoId ?? '').isNotEmpty ? _vimeoPlayerWidget : WaitScreen()),
);
}
Stacktrace/Logs
flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.27.4, on Microsoft Windows [Version 10.0.22621.4890], locale en-IN)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
[√] Chrome - develop for the web
[X] Visual Studio - develop Windows apps
X Visual Studio not installed; this is necessary to develop Windows apps.
Download at https://visualstudio.microsoft.com/downloads/.
Please install the "Desktop development with C++" workload, including all of its default components
[√] Android Studio (version 2024.1)
[√] VS Code (version 1.98.2)
[√] Connected device (3 available)
[√] Network resources
! Doctor found issues in 1 category.
Flutter version
3.27.4
Operating System, Device-specific and/or Tool
iOS device - iPhone SE (OS version - 15.7.3)
Plugin version
6.1.5
Additional information
No response
Self grab
- I'm ready to work on this issue!