Files
plezy/lib/focus/focusable_tile_mixin.dart
edde746 4522f42bbf fix(focus): reveal focused items only during keyboard/D-pad sessions
Closing an episode context menu (or its Rate / File Info sheets) on a
show opened from a Home section scrolled the detail page back to the
top, and on shows with many seasons jumped the season selector back to
the entered-from season. The Home path parks invisible focus on the
initial episode/season target; dismissing the menu restored focus to
that parked node, whose focus-gain auto-scroll then yanked the viewport.

Focus chrome is already keyboard-mode-only, so make the focus-gain
reveal match: FocusableWrapper, FocusableChipStateMixin, and
FocusableTileStateMixin now scroll into view only during keyboard/D-pad
sessions. The gate reads the tracker's live state (new
InputModeTracker.currentMode) because the inherited provider is one
frame stale on the first navigation key of a session. The touch OSK
search submit keeps its jump-to-results via an explicit reveal, matching
the existing pointer-mode convention of pairing requestFocus with an
explicit scroll.

close #2031
2026-08-19 22:38:18 +02:00

51 lines
1.4 KiB
Dart

import 'package:flutter/material.dart';
import '../utils/scroll_utils.dart';
import 'input_mode_tracker.dart';
import 'owned_focus_node_binding.dart';
/// Manages the internal/external FocusNode lifecycle for list-tile widgets and
/// auto-scrolls the tile into view when it gains focus.
mixin FocusableTileStateMixin<T extends StatefulWidget> on State<T> {
final _focusNodeBinding = OwnedFocusNodeBinding();
FocusNode? _boundExternalNode;
FocusNode? get widgetFocusNode;
FocusNode get effectiveFocusNode => _focusNodeBinding.node;
@override
void initState() {
super.initState();
_bindFocusNode();
}
@override
void didUpdateWidget(T oldWidget) {
super.didUpdateWidget(oldWidget);
if (_boundExternalNode != widgetFocusNode) {
_bindFocusNode();
}
}
@override
void dispose() {
_focusNodeBinding.dispose();
super.dispose();
}
void _bindFocusNode() {
_boundExternalNode = widgetFocusNode;
_focusNodeBinding.bind(externalNode: widgetFocusNode, listener: _onFocusChange);
}
void _onFocusChange() {
// Reveal on focus is keyboard/D-pad-only: pointer-mode focus is
// programmatic and invisible, so revealing it would yank the scroll view
// (issue #2031).
if (effectiveFocusNode.hasFocus && InputModeTracker.currentMode == InputMode.keyboard) {
scrollContextToCenter(context);
}
}
}