[flutter] tab navigation does not notify the focus scope node (#86141)

This commit is contained in:
Jonah Williams 2021-07-09 12:06:05 -07:00 committed by GitHub
parent 731360fd84
commit 6d8c850ee7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 12 deletions

View file

@ -2011,11 +2011,8 @@ class _FocusTrap extends SingleChildRenderObjectWidget {
}
class _RenderFocusTrap extends RenderProxyBoxWithHitTestBehavior {
_RenderFocusTrap(this._focusScopeNode) {
focusScopeNode.addListener(_currentFocusListener);
}
_RenderFocusTrap(this._focusScopeNode);
FocusNode? currentFocus;
Rect? currentFocusRect;
Expando<BoxHitTestResult> cachedResults = Expando<BoxHitTestResult>();
@ -2024,13 +2021,7 @@ class _RenderFocusTrap extends RenderProxyBoxWithHitTestBehavior {
set focusScopeNode(FocusScopeNode value) {
if (focusScopeNode == value)
return;
focusScopeNode.removeListener(_currentFocusListener);
_focusScopeNode = value;
focusScopeNode.addListener(_currentFocusListener);
}
void _currentFocusListener() {
currentFocus = focusScopeNode.focusedChild;
}
@override
@ -2069,11 +2060,11 @@ class _RenderFocusTrap extends RenderProxyBoxWithHitTestBehavior {
|| event.buttons != kPrimaryButton
|| event.kind != PointerDeviceKind.mouse
|| _shouldIgnoreEvents
|| currentFocus == null) {
|| _focusScopeNode.focusedChild == null) {
return;
}
final BoxHitTestResult? result = cachedResults[entry];
final FocusNode? focusNode = currentFocus;
final FocusNode? focusNode = _focusScopeNode.focusedChild;
if (focusNode == null || result == null)
return;

View file

@ -5,6 +5,7 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
@ -429,4 +430,62 @@ void main() {
expect(focusNodeA.hasFocus, false);
expect(focusNodeB.hasFocus, true);
}, variant: TargetPlatformVariant.desktop());
testWidgets('A Focused text-field will lose focus when clicking outside of its hitbox with a mouse on desktop after tab navigation', (WidgetTester tester) async {
final FocusNode focusNodeA = FocusNode();
final FocusNode focusNodeB = FocusNode();
final Key key = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Material(
child: ListView(
children: <Widget>[
const TextField(),
const TextField(),
TextField(
focusNode: focusNodeA,
),
Container(
key: key,
height: 200,
),
TextField(
focusNode: focusNodeB,
),
],
),
),
),
);
// Tab over to the 3rd text field.
for (int i = 0; i < 3; i += 1) {
await tester.sendKeyDownEvent(LogicalKeyboardKey.tab);
await tester.sendKeyUpEvent(LogicalKeyboardKey.tab);
}
expect(focusNodeA.hasFocus, true);
expect(focusNodeB.hasFocus, false);
// Click on the container to not hit either text field.
final TestGesture down2 = await tester.startGesture(tester.getCenter(find.byKey(key)), kind: PointerDeviceKind.mouse);
await tester.pump();
await tester.pumpAndSettle();
await down2.up();
await down2.removePointer();
expect(focusNodeA.hasFocus, false);
expect(focusNodeB.hasFocus, false);
// Second text field can still gain focus.
final TestGesture down3 = await tester.startGesture(tester.getCenter(find.byType(TextField).last), kind: PointerDeviceKind.mouse);
await tester.pump();
await tester.pumpAndSettle();
await down3.up();
await down3.removePointer();
expect(focusNodeA.hasFocus, false);
expect(focusNodeB.hasFocus, true);
}, variant: TargetPlatformVariant.desktop());
}