Skip to content

fix: keep the deck out of focus traversal - #204

Merged
mkobuolys merged 1 commit into
mkobuolys:mainfrom
spydon:fix/focus-after-first-frame
Sep 5, 2026
Merged

fix: keep the deck out of focus traversal#204
mkobuolys merged 1 commit into
mkobuolys:mainfrom
spydon:fix/focus-after-first-frame

Conversation

@spydon

@spydon spydon commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Description

On the web, starting any deck throws RenderBox was not laid out once at startup:

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞══════════════════════════════════════
The following assertion was thrown while dispatching notifications for
WidgetsBindingObserver.didChangeViewFocus:
Assertion failed: hasSize
"RenderBox was not laid out: RenderSemanticsAnnotations#53775 NEEDS-LAYOUT
NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE"

FlutterDeckControlsListener wraps the deck in Focus(autofocus: true). The autofocus is applied from a microtask before the first layout, and on the web that synchronously round trips through the engine:

FocusManager.applyFocusChangesIfNeeded
  -> View._scopeFocusChangeListener
  -> PlatformDispatcher.requestViewFocusChange
  -> DomElement.focusWithoutScroll
  -> WidgetsBinding.handleViewFocusChanged
  -> View.didChangeViewFocus
  -> FocusTraversalPolicy.findFirstFocus
  -> _sortAllDescendants

_sortAllDescendants reads FocusNode.rect, which reads RenderBox.size and asserts hasSize. No layout has run at that point, so the assertion fires.

Fix

The node exists so the deck receives key events, not so it can be a tab stop, so it is marked skipTraversal. It is then left out of the traversal sort and never has its rect read, while it still holds focus and still receives shortcuts.

Testing

  • Added should focus the deck without joining focus traversal, which fails without the fix and passes with it.
  • The full flutter_deck suite passes (209 tests). Removing the focus entirely breaks 3 of the existing keyboard tests, so the deck genuinely still receives keyboard input.
  • Verified in a real deck on Flutter 3.47.0 stable with flutter run -d chrome: 1 exception at startup before the change, 0 after.
  • Also verified with a focusable widget (an ElevatedButton) on the first slide, to check the fix does not depend on the deck node being the only focus node: 1 exception before the change, 0 after.

@spydon
spydon force-pushed the fix/focus-after-first-frame branch from d36260f to f9ee5e2 Compare September 5, 2026 11:57
On the web, starting a deck throws "RenderBox was not laid out" once at
startup:

    EXCEPTION CAUGHT BY WIDGETS LIBRARY
    The following assertion was thrown while dispatching notifications for
    WidgetsBindingObserver.didChangeViewFocus:
    RenderBox was not laid out: RenderSemanticsAnnotations#53775 NEEDS-LAYOUT

FlutterDeckControlsListener wraps the deck in Focus(autofocus: true). The
autofocus is applied from a microtask before the first layout, and on the
web that synchronously round trips through the engine:

    FocusManager.applyFocusChangesIfNeeded
      -> View._scopeFocusChangeListener
      -> PlatformDispatcher.requestViewFocusChange
      -> DomElement.focusWithoutScroll
      -> WidgetsBinding.handleViewFocusChanged
      -> View.didChangeViewFocus
      -> FocusTraversalPolicy.findFirstFocus
      -> _sortAllDescendants

Sorting reads FocusNode.rect, which reads RenderBox.size and asserts
hasSize. No layout has run at that point, so the assertion fires.

The node exists to receive key events, not to be a tab stop, so mark it
skipTraversal. It is then left out of the traversal sort and never has its
rect read, while it still holds focus and still receives shortcuts.

Verified against Flutter 3.47.0 stable: the exception is gone and keyboard
shortcuts still work.
@spydon
spydon force-pushed the fix/focus-after-first-frame branch from f9ee5e2 to 04849c9 Compare September 5, 2026 12:10
@spydon spydon changed the title fix: focus the deck after the first frame instead of autofocusing fix: keep the deck out of focus traversal Sep 5, 2026
@mkobuolys
mkobuolys merged commit 236b15a into mkobuolys:main Sep 5, 2026
@mkobuolys

Copy link
Copy Markdown
Owner

@spydon ❤️

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