feat: show the controls when play/pause comes from outside the controls - #957
Open
Ortes wants to merge 10 commits into
Open
feat: show the controls when play/pause comes from outside the controls#957Ortes wants to merge 10 commits into
Ortes wants to merge 10 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #957 +/- ##
==========================================
+ Coverage 51.21% 56.62% +5.40%
==========================================
Files 25 25
Lines 1728 1759 +31
==========================================
+ Hits 885 996 +111
+ Misses 843 763 -80 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
diegotori
requested changes
Aug 11, 2026
diegotori
requested changes
Aug 11, 2026
Ortes
added a commit
to Ortes/chewie
that referenced
this pull request
Aug 12, 2026
Ortes
force-pushed
the
feat/external-playpause-controls
branch
from
August 12, 2026 07:25
f89d717 to
4bfbe00
Compare
Hardware media keys (play/pause handled natively by the browser) and MediaSession toggle the video element directly: playback changes but the controls give no visual feedback. Watch for isPlaying flips in _updateState and mirror _playPause: reveal-and-hold on pause, reveal-then-autohide on resume.
Compare against _latestValue (now initialized from the controller) instead of a dedicated field, and drop the pause special-case: the controls reveal and auto-hide the same way for both directions.
_latestValue no longer carries a field initializer that reads the late `controller`, which is only resolved in didChangeDependencies. The storage field is nullable and seeded where `controller` is resolved, behind a non-null getter that mirrors the existing _chewieController/chewieController pattern in this class.
VideoPlayerController inherits a public `value` setter from ValueNotifier, so an external play/pause can be simulated without subclassing it. The auto-hide waits now derive from ChewieController.defaultHideControlsTimer instead of a magic 4s.
_updateState treated every isPlaying flip alike and restarted the hide timer, so a pause auto-hid the controls after hideControlsTimer. That included pauses coming from the controls themselves: _playPause and the bottom-bar button deliberately cancel the timer so a paused video keeps its controls up. External play/pause now mirrors _playPause per direction: reveal and re-arm the timer on play, reveal and hold on pause.
MaterialControls and CupertinoControls carried the same gap as the desktop controls: play/pause reaching the VideoPlayerController from outside them — headset buttons, notification and Control Center controls, or the embedder driving the controller — changed playback with no visual feedback. Both now mirror _playPause per direction, with _latestValue given the same nullable-plus-seeded treatment so the comparison has a previous value to read on the first update. The tests run the whole matrix against all three control implementations.
The declaration goes back to master's `late VideoPlayerValue _latestValue;` and the field is assigned one line after `controller` is resolved, so neither the nullable storage field nor the non-null getter is needed and the existing read sites stay untouched. Write-before-read is what master already relies on: its first write happens in _updateState, reached from _initialize, itself called from didChangeDependencies. Assigning at the top of didChangeDependencies moves that first write earlier on every path, so the window in which the field is valid only grows.
Ortes
force-pushed
the
feat/external-playpause-controls
branch
from
August 15, 2026 11:13
4bfbe00 to
b117ffd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
When playback is toggled from outside the controls — hardware media keys (▶⏸/F8, handled natively by the browser on web), Android headset/notification controls, the MediaSession API, or the embedding app driving the
VideoPlayerController— the video starts/stops butMaterialDesktopControlsgives no visual feedback. Pressing space (which goes through the controls' own_playPause) reveals the controls; the media key does not.With this change any
isPlayingflip reveals the controls and restarts the hide timer, so the state change is always visible and the controls auto-hide again afterhideControlsTimer.How
_updateState(already registered as a listener on theVideoPlayerController) compares the incomingcontroller.value.isPlayingagainst the previous snapshot_latestValue— which now gets an initializer so its first read can't throw — and calls the existing_cancelAndRestartTimer()on a flip. No new state, no platform-specific code: every platform implementation funnels external play/pause intovalue.isPlaying(isPlayingStateUpdate), so this covers web media keys, ExoPlayer'sonIsPlayingChanged, iOS Control Center, etc.Tests
test/external_playpause_test.dartdrives an externally-mutated controller and covers: reveal + auto-hide on external play, reveal + auto-hide on external pause, and no reveal on position-only updates.dart format,dart analyze lib test, and the fullflutter testsuite are clean.