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.
This commit is contained in:
parent
7992e3242d
commit
b2cc97013d
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user