diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3dd74238c..538f02c0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,9 +17,9 @@ env: # refer to https://docs.flutter.dev/development/tools/sdk/releases. # Note: The version below should be manually updated to the latest second most recent version # after a new stable version comes out. - # Current minimum is set to Flutter 3.32. Make the next version the new minimum once the next + # Current minimum is set to Flutter 3.38. Make the next version the new minimum once the next # stable version is released - FLUTTER_VERSION_MINIMUM_DEFAULT: "3.32.8" + FLUTTER_VERSION_MINIMUM_DEFAULT: "3.38.10" FLUTTER_VERSION_LATEST_STABLE_CHANNEL_DEFAULT: "3.x" jobs: @@ -64,7 +64,7 @@ jobs: fail-fast: false steps: - name: 📚 Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 🐦 Setup Flutter uses: subosito/flutter-action@v2 @@ -91,6 +91,6 @@ jobs: run: flutter test --no-pub --coverage --test-randomize-ordering-seed random - name: 📁 Upload coverage to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 # TODO: Remove the below once we have adequate tests for this library. continue-on-error: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 62c515482..f42e2e0c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## [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. + +## [1.14.0] +* 🛠️ [#944](https://github.com/fluttercommunity/chewie/pull/944): Flutter 3.41 Upgrade. Thanks [diegotori](https://github.com/diegotori). +* **BREAKING CHANGE**: Library now requires at least Flutter version `3.41.0` or higher due to + `wakelock_plus`'s upgrade to version `1.6.0`, which now requires this minimum Flutter version. + +## [1.13.1] +* 🛠️ [#940](https://github.com/fluttercommunity/chewie/pull/940): fix: add mounted check in _startHideTimer to prevent setState after dispose. Thanks [Cleparr](https://github.com/Cleparr). + ## [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. diff --git a/README.md b/README.md index a6bcb1e4b..0d7d0dc5d 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,9 @@ This library will at the very least make a solid effort to support the second mo of Flutter released. In other words, it will adopt `N-1` version support at the bare minimum. -However, this cannot be guaranteed due to major changes between Flutter versions. Should that occur, -future updates will be released as major or minor versions as needed. +However, this cannot be guaranteed either due to major changes between Flutter versions or +this library's dependencies. Should that occur, future updates will be released as major or minor +versions as needed. ## 🖼️ Preview diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 94307b760..50aa928eb 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,15 +4,15 @@ version: 1.0.0 publish_to: none environment: - sdk: '>=3.8.0 <4.0.0' - flutter: ">=3.32.0" + sdk: '>=3.10.0 <4.0.0' + flutter: ">=3.38.0" dependencies: chewie: path: ../ flutter: sdk: flutter - video_player: ^2.10.0 + video_player: ^2.11.1 dev_dependencies: flutter_test: diff --git a/lib/src/chewie_player.dart b/lib/src/chewie_player.dart index 7ffa295b2..9e02c4265 100644 --- a/lib/src/chewie_player.dart +++ b/lib/src/chewie_player.dart @@ -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'; @@ -44,16 +45,30 @@ class ChewieState extends State { 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(); @@ -182,6 +197,12 @@ class ChewieState extends State { 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, @@ -191,6 +212,11 @@ class ChewieState extends State { 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; @@ -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, @@ -378,6 +405,7 @@ class ChewieController extends ChangeNotifier { bool? allowMuting, bool? allowPlaybackSpeedChanging, bool? useRootNavigator, + bool? useNativeFullScreenOnWeb, Duration? hideControlsTimer, EdgeInsets? controlsSafeAreaMinimum, List? playbackSpeeds, @@ -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 ?? @@ -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; diff --git a/lib/src/material/material_controls.dart b/lib/src/material/material_controls.dart index 8d052dd75..3d43a1ab8 100644 --- a/lib/src/material/material_controls.dart +++ b/lib/src/material/material_controls.dart @@ -594,6 +594,7 @@ class _MaterialControlsState extends State ? ChewieController.defaultHideControlsTimer : chewieController.hideControlsTimer; _hideTimer = Timer(hideControlsTimer, () { + if (!mounted) return; setState(() { notifier.hideStuff = true; }); diff --git a/lib/src/web_fullscreen.dart b/lib/src/web_fullscreen.dart new file mode 100644 index 000000000..54e6d8961 --- /dev/null +++ b/lib/src/web_fullscreen.dart @@ -0,0 +1,2 @@ +export 'web_fullscreen_stub.dart' + if (dart.library.js_interop) 'web_fullscreen_impl.dart'; diff --git a/lib/src/web_fullscreen_impl.dart b/lib/src/web_fullscreen_impl.dart new file mode 100644 index 000000000..86dddc778 --- /dev/null +++ b/lib/src/web_fullscreen_impl.dart @@ -0,0 +1,30 @@ +import 'dart:js_interop'; + +import 'package:web/web.dart' as web; + +final _handlers = {}; + +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); + } +} diff --git a/lib/src/web_fullscreen_stub.dart b/lib/src/web_fullscreen_stub.dart new file mode 100644 index 000000000..47583a874 --- /dev/null +++ b/lib/src/web_fullscreen_stub.dart @@ -0,0 +1,5 @@ +void requestBrowserFullscreen() {} +void exitBrowserFullscreen() {} +bool get browserInFullscreen => false; +void addBrowserFullscreenChangeListener(void Function() callback) {} +void removeBrowserFullscreenChangeListener(void Function() callback) {} diff --git a/pubspec.yaml b/pubspec.yaml index 047272a27..b38e1b6a3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,19 +1,20 @@ name: chewie description: A video player for Flutter with Cupertino and Material play controls -version: 1.13.0 +version: 1.14.1 homepage: https://github.com/fluttercommunity/chewie environment: - sdk: '>=3.8.0 <4.0.0' - flutter: ">=3.32.0" + sdk: '>=3.10.0 <4.0.0' + flutter: ">=3.38.0" dependencies: - cupertino_icons: ^1.0.8 + cupertino_icons: ^1.0.9 flutter: sdk: flutter provider: ^6.1.5+1 - video_player: ^2.10.0 - wakelock_plus: ^1.3.2 + video_player: ^2.11.1 + wakelock_plus: ^1.6.1 + web: ^1.0.0 dev_dependencies: flutter_test: