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
51 lines
1.4 KiB
Dart
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);
|
|
}
|
|
}
|
|
}
|