Skip to content

feat: YouTube-style tap gestures (double-tap seek zones on mobile, double-click fullscreen on desktop) - #964

Merged
diegotori merged 2 commits into
fluttercommunity:masterfrom
Fullphysio:feat/yt-tap-gestures
Sep 16, 2026
Merged

diegotori merged 2 commits into
fluttercommunity:masterfrom
Fullphysio:feat/yt-tap-gestures

Conversation

@Ortes

@Ortes Ortes commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Stacked on #953 — this branch includes #953's commits (the keyboard seek indicator). Only the last 2 commits are new here; it will be rebased once #953 lands.
Supersedes #932.

What

YouTube-style tap gestures for the Material controls:

Platform Gesture Behavior
Mobile (MaterialControls) Double-tap left / right ~40% of the video Seek −10s / +10s with the accumulating indicator
Mobile Double-tap middle ~20% Nothing (dead zone, like YouTube)
Mobile Single tap in the same zone while the indicator is visible (~0.9s) Keeps seeking: 10s → 20s → 30s
Desktop (MaterialDesktopControls) Double-click anywhere on the video Toggle fullscreen (like the YouTube desktop player)

New ChewieController options, all default-on:

  • allowDoubleTapSeek (default true) — mobile double-tap seek zones.
  • doubleTapSeekDuration (default 10s) — the per-tap seek step.
  • allowDoubleTapToggleFullScreen (default true) — desktop double-click fullscreen; only honored when allowFullScreen is true.

Double-tap seek is disabled on live streams and when playback is finished, and seeks are clamped to [0, duration] via the existing _seekRelative.

How

  • The keyboard seek indicator state that feat: YouTube-style seek indicator for keyboard seeking #953 added inline to _MaterialDesktopControlsState moves into a shared SeekIndicatorStateMixin (lib/src/helpers/seek_indicator_mixin.dart), so both control sets drive the same SeekIndicator widget and share the accumulate/reset/auto-hide logic.
  • MaterialControls._buildHitArea wraps its GestureDetector in a LayoutBuilder; onDoubleTapDown records the tap's dx and onDoubleTap dispatches by zone (< 0.4 back, > 0.6 forward, middle dead). The double-tap recognizers are only attached while double-tap seek is applicable, so single-tap latency is unchanged when the feature is off or the stream is live.
  • Tap-to-repeat: the single-tap handler first checks whether the indicator is visible and the tap landed in the active zone; otherwise it falls through to the existing show/hide-controls / play-pause logic untouched.
  • Desktop gets onDoubleTap → _onExpandCollapse on its hit area. Desktop single-click behavior is deliberately unchanged — pauseOnBackgroundTap already lets apps opt into click-to-pause.

Notes

  • Double-tap seek is credited to @punit1111's Added seek forward and rewind buttons #717, which first explored it — this PR implements it on top of current master with clamping, live/finished gating, the dead zone, and the shared indicator.
  • Known limitation: while the controls are hidden, the first tap reveals them and only then do double-taps seek (the hit area sits under the controls' AbsorbPointer). Matching YouTube's double-tap-while-hidden would need the recognizer above the absorber, which would add tap latency to every control button — out of scope here.

Tests

test/double_tap_seek_test.dart adds a fake VideoPlayerPlatform (modeled on video_player's own test fake, hence the video_player_platform_interface dev dependency) so the controller really initializes and positions can be asserted. Covers: both zones seek and clamp, the configured step, the dead zone, live/flag gating, tap-to-repeat (same zone accumulates, opposite zone falls through, faded indicator does not repeat), single-tap regression, desktop double-click fullscreen on/off, and option defaults + copyWith round-trips.

dart format, flutter analyze lib test, and the full flutter test suite (105 tests) are clean.

@codecov

codecov Bot commented Aug 28, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.47%. Comparing base (00d4b3a) to head (62d9d8f).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #964      +/-   ##
==========================================
+ Coverage   77.70%   79.47%   +1.76%     
==========================================
  Files          43       44       +1     
  Lines        2687     2738      +51     
==========================================
+ Hits         2088     2176      +88     
+ Misses        599      562      -37     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@diegotori

Copy link
Copy Markdown
Collaborator

@Ortes you still have changelog conflicts in this PR. Thanks.

@Ortes
Ortes force-pushed the feat/yt-tap-gestures branch 2 times, most recently from a73ab57 to 219aace Compare September 1, 2026 15:56
Ortes added a commit to Fullphysio/chewie that referenced this pull request Sep 9, 2026
@Ortes
Ortes force-pushed the feat/yt-tap-gestures branch from 219aace to 015352e Compare September 9, 2026 09:34
@diegotori

Copy link
Copy Markdown
Collaborator

@Ortes looks like every time I merge your PRs, it always conflicts with the changelog entries that you make ahead of time. Please re-sync them. Thanks.

@Ortes

Ortes commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

@diegotori The conflicts are structural rather than stale branches: every PR inserts at line 1 of CHANGELOG.md, so any two of them conflict by definition, and the ## [Unreleased] → ## [1.16.x] rename at release conflicts on that line too. With 8 PRs open it re-triggers on every merge.

Since you re-file the entries under the release version anyway (#959 → 1.16.2, #966 → 1.16.0, #970 → 1.16.3), would you rather I drop the CHANGELOG hunk from my PRs entirely and put the proposed entry in the PR description instead? That removes the conflict class completely. Happy to keep writing them if you prefer — just say which.

Meanwhile I'll re-sync all the open ones now.

@diegotori

Copy link
Copy Markdown
Collaborator

@Ortes, usually I have Gemini create the changelog entries before publishing. Ideally, I usually take point on releasing new versions to pub.dev.

If you want, you can omit those messages from your PR so that I can update it when I publish it.

@Ortes
Ortes force-pushed the feat/yt-tap-gestures branch 3 times, most recently from bad3c7b to 425e1cf Compare September 15, 2026 06:13
@Ortes

Ortes commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

@diegotori Unrelated to this PR, and easy to say no to: would you consider adding me as a collaborator on chewie? I asked in #953 and #946 but it probably got lost in the PR noise.

Where I'm at: 17 PRs merged, 7 of them in the last two days. This week I re-synced the whole open stack onto the 1.17.x cast refactor, and dropped the CHANGELOG entries from my PRs as you asked — so that particular friction is gone from your side.

What I'd use it for: triaging incoming issues and PRs (labels, duplicates, asking for reproductions) and reviewing other people's PRs, so the queue moves without everything waiting on you. Releases and pub.dev publishing stay yours — I'm not asking for those. Merge rights only if and when you'd want that.

If it needs the fluttercommunity org rather than you, tell me the process and I'll do the legwork. And if the answer is no, or not now, just say so and I'll drop it.

@diegotori diegotori left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to add some test coverage before we can move forward with this.

Thanks.

@diegotori

Copy link
Copy Markdown
Collaborator

@diegotori Unrelated to this PR, and easy to say no to: would you consider adding me as a collaborator on chewie? I asked in #953 and #946 but it probably got lost in the PR noise.

Where I'm at: 17 PRs merged, 7 of them in the last two days. This week I re-synced the whole open stack onto the 1.17.x cast refactor, and dropped the CHANGELOG entries from my PRs as you asked — so that particular friction is gone from your side.

What I'd use it for: triaging incoming issues and PRs (labels, duplicates, asking for reproductions) and reviewing other people's PRs, so the queue moves without everything waiting on you. Releases and pub.dev publishing stay yours — I'm not asking for those. Merge rights only if and when you'd want that.

If it needs the fluttercommunity org rather than you, tell me the process and I'll do the legwork. And if the answer is no, or not now, just say so and I'll drop it.

I'll have to think about this. You'll have to go through them, and we'll need to make sure not to step on each other's toes.

Once they speak to me about it, then we'll see what happens.

@Ortes

Ortes commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

@diegotori Added test/double_tap_seek_test.dart coverage for the branches codecov flagged as missing in material_controls.dart (the 8 uncovered lines in the patch): pauseOnBackgroundTap on the hit area, the display-tapped controls-hide toggle, and the backward direction of the tap-to-repeat zone check. flutter analyze and the full flutter test suite (197 tests) are clean.

Mobile (MaterialControls):
- Double-tapping the left/right side of the video seeks backward/forward,
  flashing the accumulating seek indicator. The middle band is a dead zone.
- While the indicator is visible, further single taps in the same zone keep
  seeking (10s -> 20s -> 30s), like YouTube.
- Gated by ChewieController.allowDoubleTapSeek (default true) with the step
  set by ChewieController.doubleTapSeekDuration (default 10s). Disabled on
  live streams and when playback is finished.

Desktop (MaterialDesktopControls):
- Double-clicking the video toggles fullscreen, like the YouTube desktop
  player. Gated by ChewieController.allowDoubleTapToggleFullScreen (default
  true) and allowFullScreen.

The keyboard seek indicator state introduced by fluttercommunity#953 moves from
_MaterialDesktopControlsState into a shared SeekIndicatorStateMixin so both
control sets drive the same SeekIndicator.

Double-tap-to-seek was first explored by punit1111 in fluttercommunity#717.
pauseOnBackgroundTap, the display-tapped hide toggle, and the backward
tap-to-repeat zone check were the 8 lines codecov flagged as uncovered
in material_controls.dart's patch.
@diegotori

Copy link
Copy Markdown
Collaborator

@Ortes looks like you still have conflicts to deal with. Thanks.

@Ortes
Ortes force-pushed the feat/yt-tap-gestures branch from f8ba2c0 to 62d9d8f Compare September 16, 2026 20:37

@diegotori diegotori left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@diegotori
diegotori merged commit 3ce7dd9 into fluttercommunity:master Sep 16, 2026
5 checks passed
diegotori added a commit that referenced this pull request Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants