mirror of
https://github.com/flutter/packages.git
synced 2025-06-29 22:33:11 +08:00
[video_player] Add optional web options [web] (#4551)
Web PR for Video Player Web Options (https://github.com/flutter/packages/pull/3259).
This commit is contained in:
@ -1,3 +1,7 @@
|
|||||||
|
## 2.1.0
|
||||||
|
|
||||||
|
* Adds web options to customize the control list and context menu display.
|
||||||
|
|
||||||
## 2.0.18
|
## 2.0.18
|
||||||
|
|
||||||
* Migrates to `dart:ui_web` APIs.
|
* Migrates to `dart:ui_web` APIs.
|
||||||
|
@ -213,5 +213,199 @@ void main() {
|
|||||||
expect(events[0].duration, equals(jsCompatibleTimeUnset));
|
expect(events[0].duration, equals(jsCompatibleTimeUnset));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('VideoPlayerWebOptions', () {
|
||||||
|
late VideoPlayer player;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
video = html.VideoElement();
|
||||||
|
player = VideoPlayer(videoElement: video)..initialize();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('VideoPlayerWebOptionsControls', () {
|
||||||
|
testWidgets('when disabled expect no controls',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
// ignore: avoid_redundant_argument_values
|
||||||
|
controls: VideoPlayerWebOptionsControls.disabled(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.controls, isFalse);
|
||||||
|
expect(video.controlsList, isNotNull);
|
||||||
|
expect(video.controlsList?.length, isZero);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('when enabled', () {
|
||||||
|
testWidgets('expect controls', (WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
controls: VideoPlayerWebOptionsControls.enabled(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.controls, isTrue);
|
||||||
|
expect(video.controlsList, isNotNull);
|
||||||
|
expect(video.controlsList?.length, isZero);
|
||||||
|
expect(video.controlsList?.contains('nodownload'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('nofullscreen'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
|
||||||
|
expect(video.getAttribute('disablePictureInPicture'), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('and no download expect correct controls',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
controls: VideoPlayerWebOptionsControls.enabled(
|
||||||
|
allowDownload: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.controls, isTrue);
|
||||||
|
expect(video.controlsList, isNotNull);
|
||||||
|
expect(video.controlsList?.length, 1);
|
||||||
|
expect(video.controlsList?.contains('nodownload'), isTrue);
|
||||||
|
expect(video.controlsList?.contains('nofullscreen'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
|
||||||
|
expect(video.getAttribute('disablePictureInPicture'), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('and no fullscreen expect correct controls',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
controls: VideoPlayerWebOptionsControls.enabled(
|
||||||
|
allowFullscreen: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.controls, isTrue);
|
||||||
|
expect(video.controlsList, isNotNull);
|
||||||
|
expect(video.controlsList?.length, 1);
|
||||||
|
expect(video.controlsList?.contains('nodownload'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('nofullscreen'), isTrue);
|
||||||
|
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
|
||||||
|
expect(video.getAttribute('disablePictureInPicture'), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('and no playback rate expect correct controls',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
controls: VideoPlayerWebOptionsControls.enabled(
|
||||||
|
allowPlaybackRate: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.controls, isTrue);
|
||||||
|
expect(video.controlsList, isNotNull);
|
||||||
|
expect(video.controlsList?.length, 1);
|
||||||
|
expect(video.controlsList?.contains('nodownload'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('nofullscreen'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('noplaybackrate'), isTrue);
|
||||||
|
expect(video.getAttribute('disablePictureInPicture'), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('and no picture in picture expect correct controls',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
controls: VideoPlayerWebOptionsControls.enabled(
|
||||||
|
allowPictureInPicture: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.controls, isTrue);
|
||||||
|
expect(video.controlsList, isNotNull);
|
||||||
|
expect(video.controlsList?.length, 0);
|
||||||
|
expect(video.controlsList?.contains('nodownload'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('nofullscreen'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
|
||||||
|
expect(video.getAttribute('disablePictureInPicture'), 'true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('allowRemotePlayback', () {
|
||||||
|
testWidgets('when enabled expect no attribute',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
// ignore: avoid_redundant_argument_values
|
||||||
|
allowRemotePlayback: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.getAttribute('disableRemotePlayback'), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('when disabled expect attribute',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
allowRemotePlayback: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.getAttribute('disableRemotePlayback'), 'true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('when called first time', () {
|
||||||
|
testWidgets('expect correct options', (WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
controls: VideoPlayerWebOptionsControls.enabled(
|
||||||
|
allowDownload: false,
|
||||||
|
allowFullscreen: false,
|
||||||
|
allowPlaybackRate: false,
|
||||||
|
allowPictureInPicture: false,
|
||||||
|
),
|
||||||
|
allowContextMenu: false,
|
||||||
|
allowRemotePlayback: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.controls, isTrue);
|
||||||
|
expect(video.controlsList, isNotNull);
|
||||||
|
expect(video.controlsList?.length, 3);
|
||||||
|
expect(video.controlsList?.contains('nodownload'), isTrue);
|
||||||
|
expect(video.controlsList?.contains('nofullscreen'), isTrue);
|
||||||
|
expect(video.controlsList?.contains('noplaybackrate'), isTrue);
|
||||||
|
expect(video.getAttribute('disablePictureInPicture'), 'true');
|
||||||
|
expect(video.getAttribute('disableRemotePlayback'), 'true');
|
||||||
|
});
|
||||||
|
|
||||||
|
group('when called once more', () {
|
||||||
|
testWidgets('expect correct options', (WidgetTester tester) async {
|
||||||
|
await player.setOptions(
|
||||||
|
const VideoPlayerWebOptions(
|
||||||
|
// ignore: avoid_redundant_argument_values
|
||||||
|
controls: VideoPlayerWebOptionsControls.disabled(),
|
||||||
|
// ignore: avoid_redundant_argument_values
|
||||||
|
allowContextMenu: true,
|
||||||
|
// ignore: avoid_redundant_argument_values
|
||||||
|
allowRemotePlayback: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(video.controls, isFalse);
|
||||||
|
expect(video.controlsList, isNotNull);
|
||||||
|
expect(video.controlsList?.length, 0);
|
||||||
|
expect(video.controlsList?.contains('nodownload'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('nofullscreen'), isFalse);
|
||||||
|
expect(video.controlsList?.contains('noplaybackrate'), isFalse);
|
||||||
|
expect(video.getAttribute('disablePictureInPicture'), isNull);
|
||||||
|
expect(video.getAttribute('disableRemotePlayback'), isNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -239,5 +239,15 @@ void main() {
|
|||||||
VideoEventType.bufferingEnd,
|
VideoEventType.bufferingEnd,
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('can set web options', (WidgetTester tester) async {
|
||||||
|
expect(
|
||||||
|
VideoPlayerPlatform.instance.setWebOptions(
|
||||||
|
await textureId,
|
||||||
|
const VideoPlayerWebOptions(),
|
||||||
|
),
|
||||||
|
completes,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ class VideoPlayer {
|
|||||||
|
|
||||||
final StreamController<VideoEvent> _eventController;
|
final StreamController<VideoEvent> _eventController;
|
||||||
final html.VideoElement _videoElement;
|
final html.VideoElement _videoElement;
|
||||||
|
void Function(html.Event)? _onContextMenu;
|
||||||
|
|
||||||
bool _isInitialized = false;
|
bool _isInitialized = false;
|
||||||
bool _isBuffering = false;
|
bool _isBuffering = false;
|
||||||
@ -202,9 +203,51 @@ class VideoPlayer {
|
|||||||
return Duration(milliseconds: (_videoElement.currentTime * 1000).round());
|
return Duration(milliseconds: (_videoElement.currentTime * 1000).round());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets options
|
||||||
|
Future<void> setOptions(VideoPlayerWebOptions options) async {
|
||||||
|
// In case this method is called multiple times, reset options.
|
||||||
|
_resetOptions();
|
||||||
|
|
||||||
|
if (options.controls.enabled) {
|
||||||
|
_videoElement.controls = true;
|
||||||
|
final String controlsList = options.controls.controlsList;
|
||||||
|
if (controlsList.isNotEmpty) {
|
||||||
|
_videoElement.setAttribute('controlsList', controlsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.controls.allowPictureInPicture) {
|
||||||
|
_videoElement.setAttribute('disablePictureInPicture', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.allowContextMenu) {
|
||||||
|
_onContextMenu = (html.Event event) => event.preventDefault();
|
||||||
|
_videoElement.addEventListener('contextmenu', _onContextMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.allowRemotePlayback) {
|
||||||
|
_videoElement.setAttribute('disableRemotePlayback', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resetOptions() {
|
||||||
|
_videoElement.controls = false;
|
||||||
|
_videoElement.removeAttribute('controlsList');
|
||||||
|
_videoElement.removeAttribute('disablePictureInPicture');
|
||||||
|
if (_onContextMenu != null) {
|
||||||
|
_videoElement.removeEventListener('contextmenu', _onContextMenu);
|
||||||
|
_onContextMenu = null;
|
||||||
|
}
|
||||||
|
_videoElement.removeAttribute('disableRemotePlayback');
|
||||||
|
}
|
||||||
|
|
||||||
/// Disposes of the current [html.VideoElement].
|
/// Disposes of the current [html.VideoElement].
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_videoElement.removeAttribute('src');
|
_videoElement.removeAttribute('src');
|
||||||
|
if (_onContextMenu != null) {
|
||||||
|
_videoElement.removeEventListener('contextmenu', _onContextMenu);
|
||||||
|
_onContextMenu = null;
|
||||||
|
}
|
||||||
_videoElement.load();
|
_videoElement.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +132,11 @@ class VideoPlayerPlugin extends VideoPlayerPlatform {
|
|||||||
return _player(textureId).events;
|
return _player(textureId).events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setWebOptions(int textureId, VideoPlayerWebOptions options) {
|
||||||
|
return _player(textureId).setOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieves a [VideoPlayer] by its internal `id`.
|
// Retrieves a [VideoPlayer] by its internal `id`.
|
||||||
// It must have been created earlier from the [create] method.
|
// It must have been created earlier from the [create] method.
|
||||||
VideoPlayer _player(int id) {
|
VideoPlayer _player(int id) {
|
||||||
|
@ -2,7 +2,7 @@ name: video_player_web
|
|||||||
description: Web platform implementation of video_player.
|
description: Web platform implementation of video_player.
|
||||||
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web
|
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web
|
||||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
|
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
|
||||||
version: 2.0.18
|
version: 2.1.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.1.0 <4.0.0"
|
sdk: ">=3.1.0 <4.0.0"
|
||||||
@ -21,7 +21,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_web_plugins:
|
flutter_web_plugins:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
video_player_platform_interface: ">=6.1.0 <7.0.0"
|
video_player_platform_interface: ^6.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user