Conversation
sharjeelyunus
approved these changes
Sep 30, 2026
This branch has not been deployed
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.
Description
This PR reduces D-pad focus-navigation lag in the TV focus runtime and adds an opt-in
keepVisiblescroll mode for focus reveal.Focus navigation previously rebuilt its route/row/order view from every registered focus node on each D-pad press and resolved each target's
FocusTraversalGroupwith an ancestor walk. This PR caches that group at registration and maintains a route/row/order index so neighbour resolution is a direct lookup. It also addsstyles.tvOptions.scrollMode: keepVisible, which reveals the focused item with the minimum movement using each scrollable's own geometry, like Flutter's default traversal.Related Issue
N/A
Type of Change
What Has Changed
Focus navigation performance
TVFocusTargetnow captures its enclosingFocusTraversalGroupat registration, turningisInTraversalGroupinto an O(1) identity check instead of afindAncestorWidgetOfExactTypewalk per target per D-pad press.TVFocusRegistry, built on registration/unregistration withSplayTreeMaps so predecessor/successor (LEFT/RIGHT) and nearest-row (UP/DOWN) queries are logarithmic instead of full scans.rowTargets,cellTarget,nextRow,previousRow,rowValues,hasCell, andclearRoute.targetsmap is retained for backward compatibility with the legacy scan.includeDescendantScanargument toTVFocusOrderNode.collectInScopeso callers that know every focusable registers a target can skip the app-wide live focus-tree walk. It defaults totrue, so existing behavior is unchanged.scrollMode: keepVisibleTVScrollModeenum (legacydefault,keepVisible) parsed fromstyles.tvOptions.scrollMode.ensureWidgetVisible, which walks scrollable ancestors and applies each scrollable's ownScrollPosition.ensureVisiblewithkeepVisibleAtStart/keepVisibleAtEnd. This respects slivers, pinned/overlay headers, content padding, and nested scrollables, and scrolls only when the item is actually offscreen.includeHorizontal/includeVerticallet callers skip an axis managed elsewhere while continuing the ancestor walk._TapEnabledWrapperand_TVFocusOnlyWrapperfocus-scroll handlers.legacyremains the default.keepVisiblehonorsscrollAnimationDuration,scrollAnimationCurve, andresetScrollOnFocus; it ignoresfixedFocusScroll,fixedFocusOffset,verticalScrollPadding, andhorizontalScrollPadding, which staylegacy-only._resetScrollOnFocushelper and addedrememberActiveVerticalScrollableForContext.Tests and documentation
test/widget/tv_focus_registry_test.dartcovering the O(1) traversal-group check.test/widget/tv_focus_registry_index_test.dartcovering ordered row lookup, exact-cell lookup, row traversal, and index removal.test/widget/tv_focus_scroll_test.dartcovering thekeepVisiblereveal, the already-visible no-op, and excluded-axis behavior.scrollModeand thekeepVisiblesemantics indoc/tv_developer_guide.md.Usage
How to Test
From the repository root, run
melos bootstrap.From
modules/ensemble, run the focused tests:Run
flutter analyzeon the changed files:On a TV/emulator, set
styles.tvOptions.scrollMode: keepVisibleon a scrollable row and confirm D-pad focus reveals offscreen items with minimal movement, that nested scrollables and pinned headers behave correctly, and thatlegacyremains the default when unset.Screenshots / Videos
N/A
Checklist
flutter analyzeand addressed any new warningsflutter testand all tests pass