Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [Unreleased]
* ✨ Web: enter the browser's native (OS-level) fullscreen via the Fullscreen API instead of only expanding the Flutter view inside the browser window. Pressing Escape to leave browser fullscreen also exits Chewie's fullscreen. Controlled by the new `ChewieController.useNativeFullScreenOnWeb` flag (defaults to `true`; no effect on non-web platforms).

## [1.13.0]
* 🛠️ [#927](https://github.com/fluttercommunity/chewie/pull/927): Fix(web): Keep playback when entering/exiting fullscreen on web. Thanks [victorspringer](https://github.com/victorspringer).
* **BREAKING CHANGE**: Library now requires at least Flutter version `3.32.0` or higher.
Expand Down
39 changes: 39 additions & 0 deletions lib/src/chewie_player.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'dart:async';

import 'package:chewie/src/chewie_progress_colors.dart';
// ignore: uri_does_not_exist
Comment thread
Ortes marked this conversation as resolved.
Outdated
import 'web_fullscreen_stub.dart' if (dart.library.html) 'web_fullscreen.dart';
Comment thread
Ortes marked this conversation as resolved.
Outdated
import 'package:chewie/src/models/option_item.dart';
import 'package:chewie/src/models/options_translation.dart';
import 'package:chewie/src/models/subtitle_model.dart';
Expand Down Expand Up @@ -44,16 +46,30 @@ class ChewieState extends State<Chewie> {

bool get isControllerFullScreen => widget.controller.isFullScreen;
late PlayerNotifier notifier;
late final void Function() _browserFsExitHandler;

@override
void initState() {
super.initState();
widget.controller.addListener(listener);
notifier = PlayerNotifier.init();
// When the user presses Escape, the browser exits its native fullscreen
// without Chewie knowing. Detect this and collapse the fullscreen route.
_browserFsExitHandler = () {
if (!browserInFullscreen && _isFullScreen) {
widget.controller.exitFullScreen();
}
};
if (widget.controller.useNativeFullScreenOnWeb) {
addBrowserFullscreenChangeListener(_browserFsExitHandler);
}
}

@override
void dispose() {
if (widget.controller.useNativeFullScreenOnWeb) {
removeBrowserFullscreenChangeListener(_browserFsExitHandler);
}
widget.controller.removeListener(listener);
notifier.dispose();
super.dispose();
Expand Down Expand Up @@ -182,6 +198,12 @@ class ChewieState extends State<Chewie> {
WakelockPlus.enable();
}

// Ask the browser to enter its native fullscreen. Must be called before the
// first await so we are still inside the user-gesture event handler.
if (widget.controller.useNativeFullScreenOnWeb) {
requestBrowserFullscreen();
}

await Navigator.of(
context,
rootNavigator: widget.controller.useRootNavigator,
Expand All @@ -191,6 +213,11 @@ class ChewieState extends State<Chewie> {

if (kIsWeb) {
await _reInitializeControllers(wasPlaying);
// Exit native browser fullscreen when the Chewie route pops (e.g. user
// clicked the fullscreen button again). No-op if Escape was already used.
if (widget.controller.useNativeFullScreenOnWeb) {
exitBrowserFullscreen();
}
}

_isFullScreen = false;
Expand Down Expand Up @@ -325,6 +352,7 @@ class ChewieController extends ChangeNotifier {
this.allowMuting = true,
this.allowPlaybackSpeedChanging = true,
this.useRootNavigator = true,
this.useNativeFullScreenOnWeb = true,
this.playbackSpeeds = const [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],
this.systemOverlaysOnEnterFullScreen,
this.deviceOrientationsOnEnterFullScreen,
Expand Down Expand Up @@ -378,6 +406,7 @@ class ChewieController extends ChangeNotifier {
bool? allowMuting,
bool? allowPlaybackSpeedChanging,
bool? useRootNavigator,
bool? useNativeFullScreenOnWeb,
Duration? hideControlsTimer,
EdgeInsets? controlsSafeAreaMinimum,
List<double>? playbackSpeeds,
Expand Down Expand Up @@ -441,6 +470,8 @@ class ChewieController extends ChangeNotifier {
allowPlaybackSpeedChanging:
allowPlaybackSpeedChanging ?? this.allowPlaybackSpeedChanging,
useRootNavigator: useRootNavigator ?? this.useRootNavigator,
useNativeFullScreenOnWeb:
useNativeFullScreenOnWeb ?? this.useNativeFullScreenOnWeb,
playbackSpeeds: playbackSpeeds ?? this.playbackSpeeds,
systemOverlaysOnEnterFullScreen:
systemOverlaysOnEnterFullScreen ??
Expand Down Expand Up @@ -599,6 +630,14 @@ class ChewieController extends ChangeNotifier {
/// Defines if push/pop navigations use the rootNavigator
final bool useRootNavigator;

/// On Flutter Web, also enter the browser's native fullscreen (via the
/// Fullscreen API) when going fullscreen, instead of only expanding the
/// Flutter view inside the browser window. Pressing Escape to leave the
/// browser fullscreen also exits Chewie's fullscreen.
///
/// Has no effect on non-web platforms.
final bool useNativeFullScreenOnWeb;

/// Defines the [Duration] before the video controls are hidden. By default, this is set to three seconds.
final Duration hideControlsTimer;

Expand Down
30 changes: 30 additions & 0 deletions lib/src/web_fullscreen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'dart:js_interop';

import 'package:web/web.dart' as web;

final _handlers = <void Function(), JSFunction>{};

void requestBrowserFullscreen() {
web.document.documentElement?.requestFullscreen();
}

void exitBrowserFullscreen() {
if (web.document.fullscreenElement != null) {
web.document.exitFullscreen();
}
}

bool get browserInFullscreen => web.document.fullscreenElement != null;

void addBrowserFullscreenChangeListener(void Function() callback) {
final jsHandler = ((JSAny? _) => callback()).toJS;
_handlers[callback] = jsHandler;
web.document.addEventListener('fullscreenchange', jsHandler);
}

void removeBrowserFullscreenChangeListener(void Function() callback) {
final jsHandler = _handlers.remove(callback);
if (jsHandler != null) {
web.document.removeEventListener('fullscreenchange', jsHandler);
}
}
5 changes: 5 additions & 0 deletions lib/src/web_fullscreen_stub.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
void requestBrowserFullscreen() {}
void exitBrowserFullscreen() {}
bool get browserInFullscreen => false;
void addBrowserFullscreenChangeListener(void Function() callback) {}
void removeBrowserFullscreenChangeListener(void Function() callback) {}
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies:
provider: ^6.1.5+1
video_player: ^2.10.0
wakelock_plus: ^1.3.2
web: ^1.0.0

dev_dependencies:
flutter_test:
Expand Down