Skip to content

Commit 6ce3776

Browse files
authored
[video_player_web] Fix blank first frame on iOS. (flutter#9025)
Fixes [First Frame of Video Once VideoPlayer Controller doesn't show in iOS Safari/Chrome](flutter/flutter#139107) This PR fixes the blank first frame issue by adding a `_videoElement.load()` call at the end of the `initialize` function. This call forces the browser to load media in preparation for playback (https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/load). Additionally, this PR removes `loadedmetadata` event listener, as the `canplay` event is now reliably emitted across all platforms. Keeping `loadedmetadata` listener could result in marking the video controller as initialized before the first frame is actually rendered. This causes the loader to disappear too early, leaving a blank space where the video should be until the browser displays the first frame. Relying on `canplay` ensures the first frame is ready before we proceed. > [!NOTE] > It turns out that this issue is iOS-specific and was reported not only in Flutter, but in some other packages and tools: > 1) [video-react/First frame not shown as the poster on iOS Safari](video-react/video-react#328) > 2) [WordPress/gutenberg/Video Block: First frame not shown as the poster on iOS Safari — Fix: Use Fragment URL](WordPress/gutenberg#51995) > [!TIP] > As a temporary workaround until this PR is merged, users can append the `#t=0.001` fragment to the asset or URL `src` to force the browser to load the first frame (e.g., `assets/butterfly.mp4#t=0.001`, `https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4#t=0.001`). The following repository was used for issue reproduction and testing: https://github.com/ksokolovskyi/video_player_blank_frame_bug | Before | After | | :---: | :---: | | https://video-player-blank-frame-bug.web.app | https://video-player-blank-frame-fix.web.app | | <video src="https://github.com/user-attachments/assets/4077fcc4-10cf-4dbd-8c59-2fd824b4e4c4" /> | <video src="https://github.com/user-attachments/assets/2951af36-1569-4a5c-90dd-1d52a5d1e7f2" /> | ## Pre-Review Checklist [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 909f062 commit 6ce3776

File tree

4 files changed

+45
-7
lines changed

4 files changed

+45
-7
lines changed

packages/video_player/video_player_web/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.3.5
2+
3+
* Fixes blank first frame on iOS.
4+
15
## 2.3.4
26

37
* Adjusts the code to the new platform interface.

packages/video_player/video_player_web/example/integration_test/video_player_test.dart

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:js_interop';
7+
import 'dart:js_interop_unsafe';
68

79
import 'package:flutter_test/flutter_test.dart';
810
import 'package:integration_test/integration_test.dart';
@@ -27,6 +29,18 @@ void main() {
2729
..playsInline = false;
2830
});
2931

32+
testWidgets('initialize() calls load', (WidgetTester _) async {
33+
bool loadCalled = false;
34+
35+
video['load'] = () {
36+
loadCalled = true;
37+
}.toJS;
38+
39+
VideoPlayer(videoElement: video).initialize();
40+
41+
expect(loadCalled, isTrue);
42+
});
43+
3044
testWidgets('fixes critical video element config', (WidgetTester _) async {
3145
VideoPlayer(videoElement: video).initialize();
3246

@@ -130,6 +144,11 @@ void main() {
130144
);
131145
});
132146

147+
tearDown(() {
148+
streamController.close();
149+
player.dispose();
150+
});
151+
133152
testWidgets('buffering dispatches only when it changes',
134153
(WidgetTester tester) async {
135154
// Take all the "buffering" events that we see during the next few seconds
@@ -222,8 +241,7 @@ void main() {
222241
expect(events[0].eventType, VideoEventType.initialized);
223242
});
224243

225-
// Issue: https://github.com/flutter/flutter/issues/137023
226-
testWidgets('loadedmetadata dispatches initialized',
244+
testWidgets('loadedmetadata does not dispatch initialized',
227245
(WidgetTester tester) async {
228246
video.dispatchEvent(web.Event('loadedmetadata'));
229247
video.dispatchEvent(web.Event('loadedmetadata'));
@@ -235,8 +253,22 @@ void main() {
235253

236254
final List<VideoEvent> events = await stream;
237255

238-
expect(events, hasLength(1));
239-
expect(events[0].eventType, VideoEventType.initialized);
256+
expect(events, isEmpty);
257+
});
258+
259+
testWidgets('loadeddata does not dispatch initialized',
260+
(WidgetTester tester) async {
261+
video.dispatchEvent(web.Event('loadeddata'));
262+
video.dispatchEvent(web.Event('loadeddata'));
263+
264+
final Future<List<VideoEvent>> stream = timedStream
265+
.where((VideoEvent event) =>
266+
event.eventType == VideoEventType.initialized)
267+
.toList();
268+
269+
final List<VideoEvent> events = await stream;
270+
271+
expect(events, isEmpty);
240272
});
241273

242274
// Issue: https://github.com/flutter/flutter/issues/105649

packages/video_player/video_player_web/lib/src/video_player.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ class VideoPlayer {
7777
..playsInline = true;
7878

7979
_videoElement.onCanPlay.listen(_onVideoElementInitialization);
80-
// Needed for Safari iOS 17, which may not send `canplay`.
81-
_videoElement.onLoadedMetadata.listen(_onVideoElementInitialization);
8280

8381
_videoElement.onCanPlayThrough.listen((dynamic _) {
8482
setBuffering(false);
@@ -131,6 +129,10 @@ class VideoPlayer {
131129
if (src != null) {
132130
_videoElement.src = src;
133131
}
132+
133+
// Explicitly triggers media loading in preparation for playback. Needed on
134+
// iOS to ensure the first frame becomes visible before playback begins.
135+
_videoElement.load();
134136
}
135137

136138
/// Attempts to play the video.

packages/video_player/video_player_web/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: video_player_web
22
description: Web platform implementation of video_player.
33
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
5-
version: 2.3.4
5+
version: 2.3.5
66

77
environment:
88
sdk: ^3.4.0

0 commit comments

Comments
 (0)