Commit b2cc9701 authored by 伯言's avatar 伯言 Committed by Greg Spencer

Fix FocusTraversalPolicy makes focus lost (#34153) (#34712)

FocusTraversalPolicy keep the previously visited node to avoid hysteresis. But even if the visited focus has been disposed, FocusTraversalPolicy will still use it to requestFocus, which will cause FocusManger to get an abandoned node to get the focus.
parent 7992e324
......@@ -311,6 +311,15 @@ mixin DirectionalFocusTraversalPolicyMixin on FocusTraversalPolicy {
bool _popPolicyDataIfNeeded(TraversalDirection direction, FocusScopeNode nearestScope, FocusNode focusedChild) {
final _DirectionalPolicyData policyData = _policyData[nearestScope];
if (policyData != null && policyData.history.isNotEmpty && policyData.history.first.direction != direction) {
if (policyData.history.last.node.parent == null) {
// If a node has been removed from the tree, then we should stop
// referencing it and reset the scope data so that we don't try and
// request focus on it. This can happen in slivers where the rendered node
// has been unmounted. This has the side effect that hysteresis might not
// be avoided when items that go off screen get unmounted.
invalidateScopeData(nearestScope);
return false;
}
switch (direction) {
case TraversalDirection.down:
case TraversalDirection.up:
......
......@@ -799,5 +799,65 @@ void main() {
expect(policy.findFirstFocusInDirection(scope, TraversalDirection.left), equals(upperRightNode));
expect(policy.findFirstFocusInDirection(scope, TraversalDirection.right), equals(upperLeftNode));
});
testWidgets('Can find focus when policy data dirty', (WidgetTester tester) async {
final FocusNode focusTop = FocusNode(debugLabel: 'top');
final FocusNode focusCenter = FocusNode(debugLabel: 'center');
final FocusNode focusBottom = FocusNode(debugLabel: 'bottom');
final FocusTraversalPolicy policy = ReadingOrderTraversalPolicy();
await tester.pumpWidget(DefaultFocusTraversal(
policy: policy,
child: FocusScope(
debugLabel: 'Scope',
child: Column(
children: <Widget>[
Focus(
focusNode: focusTop,
child: Container(width: 100, height: 100)),
Focus(
focusNode: focusCenter,
child: Container(width: 100, height: 100)),
Focus(
focusNode: focusBottom,
child: Container(width: 100, height: 100)),
],
),
),
));
focusTop.requestFocus();
final FocusNode scope = focusTop.enclosingScope;
scope.focusInDirection(TraversalDirection.down);
scope.focusInDirection(TraversalDirection.down);
await tester.pump();
expect(focusBottom.hasFocus, isTrue);
// Remove center focus node.
await tester.pumpWidget(DefaultFocusTraversal(
policy: policy,
child: FocusScope(
debugLabel: 'Scope',
child: Column(
children: <Widget>[
Focus(
focusNode: focusTop,
child: Container(width: 100, height: 100)),
Focus(
focusNode: focusBottom,
child: Container(width: 100, height: 100)),
],
),
),
));
expect(focusBottom.hasFocus, isTrue);
scope.focusInDirection(TraversalDirection.up);
await tester.pump();
expect(focusCenter.hasFocus, isFalse);
expect(focusTop.hasFocus, isTrue);
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment