Skip to content
Open
Show file tree
Hide file tree
Changes from all 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.14.1]
* 🛠️ [#945](https://github.com/fluttercommunity/chewie/pull/945): Flutter 3.38 downgrade. Thanks [diegotori](https://github.com/diegotori).
* Library now supports Flutter and Dart versions `3.38.0` and `3.10` or higher respectively, restoring previous compatibility.
Expand Down
38 changes: 38 additions & 0 deletions lib/src/chewie_player.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';

import 'package:chewie/src/chewie_progress_colors.dart';
import 'web_fullscreen.dart';
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 +45,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 +197,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 +212,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 +351,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 +405,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 +469,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 +629,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
2 changes: 2 additions & 0 deletions lib/src/web_fullscreen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'web_fullscreen_stub.dart'
if (dart.library.js_interop) 'web_fullscreen_impl.dart';
30 changes: 30 additions & 0 deletions lib/src/web_fullscreen_impl.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.11.1
wakelock_plus: ^1.6.1
web: ^1.0.0

dev_dependencies:
flutter_test:
Expand Down