Cover focus tests with leak tracking (#134457)
This commit is contained in:
parent
b90c1a8766
commit
adaf78a60d
@ -21,13 +21,16 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group(FocusNode, () {
|
group(FocusNode, () {
|
||||||
testWidgets('Can add children.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can add children.', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusNode parent = FocusNode();
|
final FocusNode parent = FocusNode();
|
||||||
|
addTearDown(parent.dispose);
|
||||||
final FocusAttachment parentAttachment = parent.attach(context);
|
final FocusAttachment parentAttachment = parent.attach(context);
|
||||||
final FocusNode child1 = FocusNode();
|
final FocusNode child1 = FocusNode();
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode();
|
final FocusNode child2 = FocusNode();
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
parentAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
parentAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
child1Attachment.reparent(parent: parent);
|
child1Attachment.reparent(parent: parent);
|
||||||
@ -41,13 +44,16 @@ void main() {
|
|||||||
expect(parent.children.last, equals(child2));
|
expect(parent.children.last, equals(child2));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can remove children.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can remove children.', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusNode parent = FocusNode();
|
final FocusNode parent = FocusNode();
|
||||||
|
addTearDown(parent.dispose);
|
||||||
final FocusAttachment parentAttachment = parent.attach(context);
|
final FocusAttachment parentAttachment = parent.attach(context);
|
||||||
final FocusNode child1 = FocusNode();
|
final FocusNode child1 = FocusNode();
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode();
|
final FocusNode child2 = FocusNode();
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
parentAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
parentAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
child1Attachment.reparent(parent: parent);
|
child1Attachment.reparent(parent: parent);
|
||||||
@ -67,9 +73,12 @@ void main() {
|
|||||||
expect(parent.children, isEmpty);
|
expect(parent.children, isEmpty);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Geometry is transformed properly.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Geometry is transformed properly.', (WidgetTester tester) async {
|
||||||
final FocusNode focusNode1 = FocusNode(debugLabel: 'Test Node 1');
|
final FocusNode focusNode1 = FocusNode(debugLabel: 'Test Node 1');
|
||||||
|
addTearDown(focusNode1.dispose);
|
||||||
final FocusNode focusNode2 = FocusNode(debugLabel: 'Test Node 2');
|
final FocusNode focusNode2 = FocusNode(debugLabel: 'Test Node 2');
|
||||||
|
addTearDown(focusNode2.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
@ -104,17 +113,22 @@ void main() {
|
|||||||
expect(focusNode2.offset, equals(const Offset(443.0, 194.5)));
|
expect(focusNode2.offset, equals(const Offset(443.0, 194.5)));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('descendantsAreFocusable disables focus for descendants.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('descendantsAreFocusable disables focus for descendants.', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
parent1Attachment.reparent(parent: scope);
|
parent1Attachment.reparent(parent: scope);
|
||||||
@ -152,17 +166,22 @@ void main() {
|
|||||||
expect(scope.traversalDescendants.contains(child2), isFalse);
|
expect(scope.traversalDescendants.contains(child2), isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('descendantsAreTraversable disables traversal for descendants.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('descendantsAreTraversable disables traversal for descendants.', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
|
|
||||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -185,17 +204,22 @@ void main() {
|
|||||||
expect(scope.traversalDescendants, equals(<FocusNode>[]));
|
expect(scope.traversalDescendants, equals(<FocusNode>[]));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("canRequestFocus doesn't affect traversalChildren", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("canRequestFocus doesn't affect traversalChildren", (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
parent1Attachment.reparent(parent: scope);
|
parent1Attachment.reparent(parent: scope);
|
||||||
@ -216,11 +240,11 @@ void main() {
|
|||||||
expect(scope.traversalChildren.contains(parent2), isFalse);
|
expect(scope.traversalChildren.contains(parent2), isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('implements debugFillProperties', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('implements debugFillProperties', (WidgetTester tester) async {
|
||||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
FocusNode(
|
final FocusNode focusNode = FocusNode(debugLabel: 'Label');
|
||||||
debugLabel: 'Label',
|
addTearDown(focusNode.dispose);
|
||||||
).debugFillProperties(builder);
|
focusNode.debugFillProperties(builder);
|
||||||
final List<String> description = builder.properties.map((DiagnosticsNode n) => n.toString()).toList();
|
final List<String> description = builder.properties.map((DiagnosticsNode n) => n.toString()).toList();
|
||||||
expect(description, <String>[
|
expect(description, <String>[
|
||||||
'context: null',
|
'context: null',
|
||||||
@ -232,8 +256,13 @@ void main() {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('onKeyEvent and onKey correctly cooperate', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('onKeyEvent and onKey correctly cooperate', (WidgetTester tester) async {
|
||||||
final FocusNode focusNode = FocusNode(debugLabel: 'Test Node 3');
|
final FocusNode focusNode1 = FocusNode(debugLabel: 'Test Node 1');
|
||||||
|
addTearDown(focusNode1.dispose);
|
||||||
|
final FocusNode focusNode2 = FocusNode(debugLabel: 'Test Node 2');
|
||||||
|
addTearDown(focusNode2.dispose);
|
||||||
|
final FocusNode focusNode3 = FocusNode(debugLabel: 'Test Node 3');
|
||||||
|
addTearDown(focusNode3.dispose);
|
||||||
List<List<KeyEventResult>> results = <List<KeyEventResult>>[
|
List<List<KeyEventResult>> results = <List<KeyEventResult>>[
|
||||||
<KeyEventResult>[KeyEventResult.ignored, KeyEventResult.ignored],
|
<KeyEventResult>[KeyEventResult.ignored, KeyEventResult.ignored],
|
||||||
<KeyEventResult>[KeyEventResult.ignored, KeyEventResult.ignored],
|
<KeyEventResult>[KeyEventResult.ignored, KeyEventResult.ignored],
|
||||||
@ -243,7 +272,7 @@ void main() {
|
|||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Focus(
|
Focus(
|
||||||
focusNode: FocusNode(debugLabel: 'Test Node 1'),
|
focusNode: focusNode1,
|
||||||
onKeyEvent: (_, KeyEvent event) {
|
onKeyEvent: (_, KeyEvent event) {
|
||||||
logs.add(0);
|
logs.add(0);
|
||||||
return results[0][0];
|
return results[0][0];
|
||||||
@ -253,7 +282,7 @@ void main() {
|
|||||||
return results[0][1];
|
return results[0][1];
|
||||||
},
|
},
|
||||||
child: Focus(
|
child: Focus(
|
||||||
focusNode: FocusNode(debugLabel: 'Test Node 2'),
|
focusNode: focusNode2,
|
||||||
onKeyEvent: (_, KeyEvent event) {
|
onKeyEvent: (_, KeyEvent event) {
|
||||||
logs.add(10);
|
logs.add(10);
|
||||||
return results[1][0];
|
return results[1][0];
|
||||||
@ -263,7 +292,7 @@ void main() {
|
|||||||
return results[1][1];
|
return results[1][1];
|
||||||
},
|
},
|
||||||
child: Focus(
|
child: Focus(
|
||||||
focusNode: focusNode,
|
focusNode: focusNode3,
|
||||||
onKeyEvent: (_, KeyEvent event) {
|
onKeyEvent: (_, KeyEvent event) {
|
||||||
logs.add(20);
|
logs.add(20);
|
||||||
return results[2][0];
|
return results[2][0];
|
||||||
@ -277,7 +306,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
focusNode.requestFocus();
|
focusNode3.requestFocus();
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
// All ignored.
|
// All ignored.
|
||||||
@ -328,15 +357,19 @@ void main() {
|
|||||||
|
|
||||||
group(FocusScopeNode, () {
|
group(FocusScopeNode, () {
|
||||||
|
|
||||||
testWidgets('Can setFirstFocus on a scope with no manager.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can setFirstFocus on a scope with no manager.', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||||
|
addTearDown(scope.dispose);
|
||||||
scope.attach(context);
|
scope.attach(context);
|
||||||
final FocusScopeNode parent = FocusScopeNode(debugLabel: 'Parent');
|
final FocusScopeNode parent = FocusScopeNode(debugLabel: 'Parent');
|
||||||
|
addTearDown(parent.dispose);
|
||||||
parent.attach(context);
|
parent.attach(context);
|
||||||
final FocusScopeNode child1 = FocusScopeNode(debugLabel: 'Child 1');
|
final FocusScopeNode child1 = FocusScopeNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusScopeNode child2 = FocusScopeNode(debugLabel: 'Child 2');
|
final FocusScopeNode child2 = FocusScopeNode(debugLabel: 'Child 2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
child2.attach(context);
|
child2.attach(context);
|
||||||
scope.setFirstFocus(parent);
|
scope.setFirstFocus(parent);
|
||||||
parent.setFirstFocus(child1);
|
parent.setFirstFocus(child1);
|
||||||
@ -353,15 +386,19 @@ void main() {
|
|||||||
expect(scope.focusedChild, equals(parent));
|
expect(scope.focusedChild, equals(parent));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Removing a node removes it from scope.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Removing a node removes it from scope.', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode();
|
final FocusScopeNode scope = FocusScopeNode();
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode parent = FocusNode();
|
final FocusNode parent = FocusNode();
|
||||||
|
addTearDown(parent.dispose);
|
||||||
final FocusAttachment parentAttachment = parent.attach(context);
|
final FocusAttachment parentAttachment = parent.attach(context);
|
||||||
final FocusNode child1 = FocusNode();
|
final FocusNode child1 = FocusNode();
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode();
|
final FocusNode child2 = FocusNode();
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
parentAttachment.reparent(parent: scope);
|
parentAttachment.reparent(parent: scope);
|
||||||
@ -378,15 +415,19 @@ void main() {
|
|||||||
expect(scope.focusedChild, isNull);
|
expect(scope.focusedChild, isNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can add children to scope and focus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can add children to scope and focus', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode();
|
final FocusScopeNode scope = FocusScopeNode();
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode parent = FocusNode();
|
final FocusNode parent = FocusNode();
|
||||||
|
addTearDown(parent.dispose);
|
||||||
final FocusAttachment parentAttachment = parent.attach(context);
|
final FocusAttachment parentAttachment = parent.attach(context);
|
||||||
final FocusNode child1 = FocusNode();
|
final FocusNode child1 = FocusNode();
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode();
|
final FocusNode child2 = FocusNode();
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
parentAttachment.reparent(parent: scope);
|
parentAttachment.reparent(parent: scope);
|
||||||
@ -418,11 +459,13 @@ void main() {
|
|||||||
expect(child2.hasPrimaryFocus, isTrue);
|
expect(child2.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Requesting focus before adding to tree results in a request after adding', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Requesting focus before adding to tree results in a request after adding', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode();
|
final FocusScopeNode scope = FocusScopeNode();
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode child = FocusNode();
|
final FocusNode child = FocusNode();
|
||||||
|
addTearDown(child.dispose);
|
||||||
child.requestFocus();
|
child.requestFocus();
|
||||||
expect(child.hasPrimaryFocus, isFalse); // not attached yet.
|
expect(child.hasPrimaryFocus, isFalse); // not attached yet.
|
||||||
|
|
||||||
@ -438,15 +481,19 @@ void main() {
|
|||||||
expect(child.hasPrimaryFocus, isTrue); // now attached and parented, so focus finally happened.
|
expect(child.hasPrimaryFocus, isTrue); // now attached and parented, so focus finally happened.
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Autofocus works.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Autofocus works.', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode parent = FocusNode(debugLabel: 'Parent');
|
final FocusNode parent = FocusNode(debugLabel: 'Parent');
|
||||||
|
addTearDown(parent.dispose);
|
||||||
final FocusAttachment parentAttachment = parent.attach(context);
|
final FocusAttachment parentAttachment = parent.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
parentAttachment.reparent(parent: scope);
|
parentAttachment.reparent(parent: scope);
|
||||||
@ -475,15 +522,19 @@ void main() {
|
|||||||
expect(child2.hasPrimaryFocus, isFalse);
|
expect(child2.hasPrimaryFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Adding a focusedChild to a scope sets scope as focusedChild in parent scope', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Adding a focusedChild to a scope sets scope as focusedChild in parent scope', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode();
|
final FocusScopeNode scope1 = FocusScopeNode();
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context);
|
final FocusAttachment scope1Attachment = scope1.attach(context);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode();
|
final FocusScopeNode scope2 = FocusScopeNode();
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context);
|
final FocusAttachment scope2Attachment = scope2.attach(context);
|
||||||
final FocusNode child1 = FocusNode();
|
final FocusNode child1 = FocusNode();
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode();
|
final FocusNode child2 = FocusNode();
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: scope1);
|
scope2Attachment.reparent(parent: scope1);
|
||||||
@ -507,17 +558,22 @@ void main() {
|
|||||||
expect(child2.hasPrimaryFocus, isFalse);
|
expect(child2.hasPrimaryFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can move node with focus without losing focus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can move node with focus without losing focus', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
parent1Attachment.reparent(parent: scope);
|
parent1Attachment.reparent(parent: scope);
|
||||||
@ -544,17 +600,22 @@ void main() {
|
|||||||
expect(parent2.children.first, equals(child1));
|
expect(parent2.children.first, equals(child1));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('canRequestFocus affects children.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('canRequestFocus affects children.', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
parent1Attachment.reparent(parent: scope);
|
parent1Attachment.reparent(parent: scope);
|
||||||
@ -584,17 +645,22 @@ void main() {
|
|||||||
expect(parent1.traversalChildren.contains(child2), isFalse);
|
expect(parent1.traversalChildren.contains(child2), isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("skipTraversal doesn't affect children.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("skipTraversal doesn't affect children.", (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope');
|
||||||
|
addTearDown(scope.dispose);
|
||||||
final FocusAttachment scopeAttachment = scope.attach(context);
|
final FocusAttachment scopeAttachment = scope.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scopeAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
parent1Attachment.reparent(parent: scope);
|
parent1Attachment.reparent(parent: scope);
|
||||||
@ -619,23 +685,31 @@ void main() {
|
|||||||
expect(scope.traversalDescendants.contains(child2), isTrue);
|
expect(scope.traversalDescendants.contains(child2), isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can move node between scopes and lose scope focus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can move node between scopes and lose scope focus', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context);
|
final FocusAttachment scope1Attachment = scope1.attach(context);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context);
|
final FocusAttachment scope2Attachment = scope2.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
||||||
|
addTearDown(child3.dispose);
|
||||||
final FocusAttachment child3Attachment = child3.attach(context);
|
final FocusAttachment child3Attachment = child3.attach(context);
|
||||||
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
||||||
|
addTearDown(child4.dispose);
|
||||||
final FocusAttachment child4Attachment = child4.attach(context);
|
final FocusAttachment child4Attachment = child4.attach(context);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -657,23 +731,31 @@ void main() {
|
|||||||
expect(parent2.children.contains(child1), isTrue);
|
expect(parent2.children.contains(child1), isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ancestors and descendants are computed and recomputed properly', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('ancestors and descendants are computed and recomputed properly', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1');
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1');
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context);
|
final FocusAttachment scope1Attachment = scope1.attach(context);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context);
|
final FocusAttachment scope2Attachment = scope2.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
||||||
|
addTearDown(child3.dispose);
|
||||||
final FocusAttachment child3Attachment = child3.attach(context);
|
final FocusAttachment child3Attachment = child3.attach(context);
|
||||||
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
||||||
|
addTearDown(child4.dispose);
|
||||||
final FocusAttachment child4Attachment = child4.attach(context);
|
final FocusAttachment child4Attachment = child4.attach(context);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -693,23 +775,31 @@ void main() {
|
|||||||
expect(tester.binding.focusManager.rootScope.descendants, equals(<FocusNode>[child1, child3, child4, parent2, scope2, child2, parent1, scope1]));
|
expect(tester.binding.focusManager.rootScope.descendants, equals(<FocusNode>[child1, child3, child4, parent2, scope2, child2, parent1, scope1]));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can move focus between scopes and keep focus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can move focus between scopes and keep focus', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode();
|
final FocusScopeNode scope1 = FocusScopeNode();
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context);
|
final FocusAttachment scope1Attachment = scope1.attach(context);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode();
|
final FocusScopeNode scope2 = FocusScopeNode();
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context);
|
final FocusAttachment scope2Attachment = scope2.attach(context);
|
||||||
final FocusNode parent1 = FocusNode();
|
final FocusNode parent1 = FocusNode();
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode();
|
final FocusNode parent2 = FocusNode();
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode();
|
final FocusNode child1 = FocusNode();
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode();
|
final FocusNode child2 = FocusNode();
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
final FocusNode child3 = FocusNode();
|
final FocusNode child3 = FocusNode();
|
||||||
|
addTearDown(child3.dispose);
|
||||||
final FocusAttachment child3Attachment = child3.attach(context);
|
final FocusAttachment child3Attachment = child3.attach(context);
|
||||||
final FocusNode child4 = FocusNode();
|
final FocusNode child4 = FocusNode();
|
||||||
|
addTearDown(child4.dispose);
|
||||||
final FocusAttachment child4Attachment = child4.attach(context);
|
final FocusAttachment child4Attachment = child4.attach(context);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -751,23 +841,31 @@ void main() {
|
|||||||
expect(scope2.focusedChild, equals(child4));
|
expect(scope2.focusedChild, equals(child4));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Unfocus with disposition previouslyFocusedChild works properly', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Unfocus with disposition previouslyFocusedChild works properly', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context);
|
final FocusAttachment scope1Attachment = scope1.attach(context);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context);
|
final FocusAttachment scope2Attachment = scope2.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
||||||
|
addTearDown(child3.dispose);
|
||||||
final FocusAttachment child3Attachment = child3.attach(context);
|
final FocusAttachment child3Attachment = child3.attach(context);
|
||||||
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
||||||
|
addTearDown(child4.dispose);
|
||||||
final FocusAttachment child4Attachment = child4.attach(context);
|
final FocusAttachment child4Attachment = child4.attach(context);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -832,23 +930,31 @@ void main() {
|
|||||||
expect(child3.hasPrimaryFocus, isTrue);
|
expect(child3.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Unfocus with disposition scope works properly', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Unfocus with disposition scope works properly', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context);
|
final FocusAttachment scope1Attachment = scope1.attach(context);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context);
|
final FocusAttachment scope2Attachment = scope2.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
||||||
|
addTearDown(child3.dispose);
|
||||||
final FocusAttachment child3Attachment = child3.attach(context);
|
final FocusAttachment child3Attachment = child3.attach(context);
|
||||||
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
||||||
|
addTearDown(child4.dispose);
|
||||||
final FocusAttachment child4Attachment = child4.attach(context);
|
final FocusAttachment child4Attachment = child4.attach(context);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -917,23 +1023,31 @@ void main() {
|
|||||||
expect(FocusManager.instance.rootScope.hasPrimaryFocus, isTrue);
|
expect(FocusManager.instance.rootScope.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Unfocus works properly when some nodes are unfocusable', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Unfocus works properly when some nodes are unfocusable', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context);
|
final FocusAttachment scope1Attachment = scope1.attach(context);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context);
|
final FocusAttachment scope2Attachment = scope2.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
||||||
|
addTearDown(child3.dispose);
|
||||||
final FocusAttachment child3Attachment = child3.attach(context);
|
final FocusAttachment child3Attachment = child3.attach(context);
|
||||||
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
||||||
|
addTearDown(child4.dispose);
|
||||||
final FocusAttachment child4Attachment = child4.attach(context);
|
final FocusAttachment child4Attachment = child4.attach(context);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -983,23 +1097,31 @@ void main() {
|
|||||||
expect(child2.hasPrimaryFocus, isFalse);
|
expect(child2.hasPrimaryFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Requesting focus on a scope works properly when some focusedChild nodes are unfocusable', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Requesting focus on a scope works properly when some focusedChild nodes are unfocusable', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1')..attach(context);
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context);
|
final FocusAttachment scope1Attachment = scope1.attach(context);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context);
|
final FocusAttachment scope2Attachment = scope2.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'parent2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
final FocusNode child3 = FocusNode(debugLabel: 'child3');
|
||||||
|
addTearDown(child3.dispose);
|
||||||
final FocusAttachment child3Attachment = child3.attach(context);
|
final FocusAttachment child3Attachment = child3.attach(context);
|
||||||
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
final FocusNode child4 = FocusNode(debugLabel: 'child4');
|
||||||
|
addTearDown(child4.dispose);
|
||||||
final FocusAttachment child4Attachment = child4.attach(context);
|
final FocusAttachment child4Attachment = child4.attach(context);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -1037,7 +1159,7 @@ void main() {
|
|||||||
expect(child4.hasPrimaryFocus, isTrue);
|
expect(child4.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Key handling bubbles up and terminates when handled.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Key handling bubbles up and terminates when handled.', (WidgetTester tester) async {
|
||||||
final Set<FocusNode> receivedAnEvent = <FocusNode>{};
|
final Set<FocusNode> receivedAnEvent = <FocusNode>{};
|
||||||
final Set<FocusNode> shouldHandle = <FocusNode>{};
|
final Set<FocusNode> shouldHandle = <FocusNode>{};
|
||||||
KeyEventResult handleEvent(FocusNode node, RawKeyEvent event) {
|
KeyEventResult handleEvent(FocusNode node, RawKeyEvent event) {
|
||||||
@ -1055,20 +1177,28 @@ void main() {
|
|||||||
|
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'Scope 1');
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'Scope 1');
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context, onKey: handleEvent);
|
final FocusAttachment scope1Attachment = scope1.attach(context, onKey: handleEvent);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'Scope 2');
|
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'Scope 2');
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context, onKey: handleEvent);
|
final FocusAttachment scope2Attachment = scope2.attach(context, onKey: handleEvent);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1', onKey: handleEvent);
|
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1', onKey: handleEvent);
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2', onKey: handleEvent);
|
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2', onKey: handleEvent);
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context, onKey: handleEvent);
|
final FocusAttachment child1Attachment = child1.attach(context, onKey: handleEvent);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
final FocusNode child2 = FocusNode(debugLabel: 'Child 2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context, onKey: handleEvent);
|
final FocusAttachment child2Attachment = child2.attach(context, onKey: handleEvent);
|
||||||
final FocusNode child3 = FocusNode(debugLabel: 'Child 3');
|
final FocusNode child3 = FocusNode(debugLabel: 'Child 3');
|
||||||
|
addTearDown(child3.dispose);
|
||||||
final FocusAttachment child3Attachment = child3.attach(context, onKey: handleEvent);
|
final FocusAttachment child3Attachment = child3.attach(context, onKey: handleEvent);
|
||||||
final FocusNode child4 = FocusNode(debugLabel: 'Child 4');
|
final FocusNode child4 = FocusNode(debugLabel: 'Child 4');
|
||||||
|
addTearDown(child4.dispose);
|
||||||
final FocusAttachment child4Attachment = child4.attach(context, onKey: handleEvent);
|
final FocusAttachment child4Attachment = child4.attach(context, onKey: handleEvent);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -1101,7 +1231,7 @@ void main() {
|
|||||||
expect(receivedAnEvent, isEmpty);
|
expect(receivedAnEvent, isEmpty);
|
||||||
}, variant: KeySimulatorTransitModeVariant.all());
|
}, variant: KeySimulatorTransitModeVariant.all());
|
||||||
|
|
||||||
testWidgets('Initial highlight mode guesses correctly.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Initial highlight mode guesses correctly.', (WidgetTester tester) async {
|
||||||
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.automatic;
|
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.automatic;
|
||||||
switch (defaultTargetPlatform) {
|
switch (defaultTargetPlatform) {
|
||||||
case TargetPlatform.fuchsia:
|
case TargetPlatform.fuchsia:
|
||||||
@ -1115,7 +1245,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
}, variant: TargetPlatformVariant.all());
|
}, variant: TargetPlatformVariant.all());
|
||||||
|
|
||||||
testWidgets('Mouse events change initial focus highlight mode on mobile.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Mouse events change initial focus highlight mode on mobile.', (WidgetTester tester) async {
|
||||||
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.touch));
|
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.touch));
|
||||||
RendererBinding.instance.initMouseTracker(); // Clear out the mouse state.
|
RendererBinding.instance.initMouseTracker(); // Clear out the mouse state.
|
||||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 0);
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 0);
|
||||||
@ -1123,7 +1253,7 @@ void main() {
|
|||||||
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
|
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
|
||||||
}, variant: TargetPlatformVariant.mobile());
|
}, variant: TargetPlatformVariant.mobile());
|
||||||
|
|
||||||
testWidgets('Mouse events change initial focus highlight mode on desktop.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Mouse events change initial focus highlight mode on desktop.', (WidgetTester tester) async {
|
||||||
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
|
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
|
||||||
RendererBinding.instance.initMouseTracker(); // Clear out the mouse state.
|
RendererBinding.instance.initMouseTracker(); // Clear out the mouse state.
|
||||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 0);
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 0);
|
||||||
@ -1131,12 +1261,12 @@ void main() {
|
|||||||
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
|
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
|
||||||
}, variant: TargetPlatformVariant.desktop());
|
}, variant: TargetPlatformVariant.desktop());
|
||||||
|
|
||||||
testWidgets('Keyboard events change initial focus highlight mode.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Keyboard events change initial focus highlight mode.', (WidgetTester tester) async {
|
||||||
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
|
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
|
||||||
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
|
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.traditional));
|
||||||
}, variant: TargetPlatformVariant.all());
|
}, variant: TargetPlatformVariant.all());
|
||||||
|
|
||||||
testWidgets('Events change focus highlight mode.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Events change focus highlight mode.', (WidgetTester tester) async {
|
||||||
await setupWidget(tester);
|
await setupWidget(tester);
|
||||||
int callCount = 0;
|
int callCount = 0;
|
||||||
FocusHighlightMode? lastMode;
|
FocusHighlightMode? lastMode;
|
||||||
@ -1177,11 +1307,11 @@ void main() {
|
|||||||
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.touch));
|
expect(FocusManager.instance.highlightMode, equals(FocusHighlightMode.touch));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('implements debugFillProperties', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('implements debugFillProperties', (WidgetTester tester) async {
|
||||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
FocusScopeNode(
|
final FocusScopeNode scope = FocusScopeNode(debugLabel: 'Scope Label');
|
||||||
debugLabel: 'Scope Label',
|
addTearDown(scope.dispose);
|
||||||
).debugFillProperties(builder);
|
scope.debugFillProperties(builder);
|
||||||
final List<String> description = builder.properties.map((DiagnosticsNode n) => n.toString()).toList();
|
final List<String> description = builder.properties.map((DiagnosticsNode n) => n.toString()).toList();
|
||||||
expect(description, <String>[
|
expect(description, <String>[
|
||||||
'context: null',
|
'context: null',
|
||||||
@ -1193,23 +1323,31 @@ void main() {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('debugDescribeFocusTree produces correct output', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('debugDescribeFocusTree produces correct output', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'Scope 1');
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'Scope 1');
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusAttachment scope1Attachment = scope1.attach(context);
|
final FocusAttachment scope1Attachment = scope1.attach(context);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(); // No label, Just to test that it works.
|
final FocusScopeNode scope2 = FocusScopeNode(); // No label, Just to test that it works.
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusAttachment scope2Attachment = scope2.attach(context);
|
final FocusAttachment scope2Attachment = scope2.attach(context);
|
||||||
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
final FocusNode parent1 = FocusNode(debugLabel: 'Parent 1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
final FocusNode parent2 = FocusNode(debugLabel: 'Parent 2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
final FocusNode child1 = FocusNode(debugLabel: 'Child 1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(); // No label, Just to test that it works.
|
final FocusNode child2 = FocusNode(); // No label, Just to test that it works.
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
final FocusNode child3 = FocusNode(debugLabel: 'Child 3');
|
final FocusNode child3 = FocusNode(debugLabel: 'Child 3');
|
||||||
|
addTearDown(child3.dispose);
|
||||||
final FocusAttachment child3Attachment = child3.attach(context);
|
final FocusAttachment child3Attachment = child3.attach(context);
|
||||||
final FocusNode child4 = FocusNode(debugLabel: 'Child 4');
|
final FocusNode child4 = FocusNode(debugLabel: 'Child 4');
|
||||||
|
addTearDown(child4.dispose);
|
||||||
final FocusAttachment child4Attachment = child4.attach(context);
|
final FocusAttachment child4Attachment = child4.attach(context);
|
||||||
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
@ -1269,11 +1407,13 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('Autofocus', () {
|
group('Autofocus', () {
|
||||||
testWidgets(
|
testWidgetsWithLeakTracking(
|
||||||
'works when the previous focused node is detached',
|
'works when the previous focused node is detached',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final FocusNode node1 = FocusNode();
|
final FocusNode node1 = FocusNode();
|
||||||
|
addTearDown(node1.dispose);
|
||||||
final FocusNode node2 = FocusNode();
|
final FocusNode node2 = FocusNode();
|
||||||
|
addTearDown(node2.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -1294,11 +1434,13 @@ void main() {
|
|||||||
expect(node2.hasPrimaryFocus, isTrue);
|
expect(node2.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets(
|
testWidgetsWithLeakTracking(
|
||||||
'node detached before autofocus is applied',
|
'node detached before autofocus is applied',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final FocusScopeNode scopeNode = FocusScopeNode();
|
final FocusScopeNode scopeNode = FocusScopeNode();
|
||||||
|
addTearDown(scopeNode.dispose);
|
||||||
final FocusNode node1 = FocusNode();
|
final FocusNode node1 = FocusNode();
|
||||||
|
addTearDown(node1.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -1322,10 +1464,13 @@ void main() {
|
|||||||
expect(scopeNode.hasPrimaryFocus, isTrue);
|
expect(scopeNode.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('autofocus the first candidate', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('autofocus the first candidate', (WidgetTester tester) async {
|
||||||
final FocusNode node1 = FocusNode();
|
final FocusNode node1 = FocusNode();
|
||||||
|
addTearDown(node1.dispose);
|
||||||
final FocusNode node2 = FocusNode();
|
final FocusNode node2 = FocusNode();
|
||||||
|
addTearDown(node2.dispose);
|
||||||
final FocusNode node3 = FocusNode();
|
final FocusNode node3 = FocusNode();
|
||||||
|
addTearDown(node3.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
@ -1355,10 +1500,13 @@ void main() {
|
|||||||
expect(node1.hasPrimaryFocus, isTrue);
|
expect(node1.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Autofocus works with global key reparenting', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Autofocus works with global key reparenting', (WidgetTester tester) async {
|
||||||
final FocusNode node = FocusNode();
|
final FocusNode node = FocusNode();
|
||||||
|
addTearDown(node.dispose);
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1');
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1');
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1409,15 +1557,19 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Doesn't lose focused child when reparenting if the nearestScope doesn't change.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Doesn't lose focused child when reparenting if the nearestScope doesn't change.", (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode parent1 = FocusScopeNode(debugLabel: 'parent1');
|
final FocusScopeNode parent1 = FocusScopeNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusScopeNode parent2 = FocusScopeNode(debugLabel: 'parent2');
|
final FocusScopeNode parent2 = FocusScopeNode(debugLabel: 'parent2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
parent1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
parent1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
child1Attachment.reparent(parent: parent1);
|
child1Attachment.reparent(parent: parent1);
|
||||||
@ -1435,7 +1587,7 @@ void main() {
|
|||||||
expect(parent1.focusedChild, equals(child2));
|
expect(parent1.focusedChild, equals(child2));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Ancestors get notified exactly as often as needed if focused child changes focus.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Ancestors get notified exactly as often as needed if focused child changes focus.', (WidgetTester tester) async {
|
||||||
bool topFocus = false;
|
bool topFocus = false;
|
||||||
bool parent1Focus = false;
|
bool parent1Focus = false;
|
||||||
bool parent2Focus = false;
|
bool parent2Focus = false;
|
||||||
@ -1460,14 +1612,19 @@ void main() {
|
|||||||
}
|
}
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode top = FocusScopeNode(debugLabel: 'top');
|
final FocusScopeNode top = FocusScopeNode(debugLabel: 'top');
|
||||||
|
addTearDown(top.dispose);
|
||||||
final FocusAttachment topAttachment = top.attach(context);
|
final FocusAttachment topAttachment = top.attach(context);
|
||||||
final FocusScopeNode parent1 = FocusScopeNode(debugLabel: 'parent1');
|
final FocusScopeNode parent1 = FocusScopeNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusScopeNode parent2 = FocusScopeNode(debugLabel: 'parent2');
|
final FocusScopeNode parent2 = FocusScopeNode(debugLabel: 'parent2');
|
||||||
|
addTearDown(parent2.dispose);
|
||||||
final FocusAttachment parent2Attachment = parent2.attach(context);
|
final FocusAttachment parent2Attachment = parent2.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
topAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
topAttachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
parent1Attachment.reparent(parent: top);
|
parent1Attachment.reparent(parent: top);
|
||||||
@ -1566,13 +1723,16 @@ void main() {
|
|||||||
expect(child2Notify, equals(0));
|
expect(child2Notify, equals(0));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus changes notify listeners.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus changes notify listeners.', (WidgetTester tester) async {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode parent1 = FocusScopeNode(debugLabel: 'parent1');
|
final FocusScopeNode parent1 = FocusScopeNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
final FocusNode child2 = FocusNode(debugLabel: 'child2');
|
||||||
|
addTearDown(child2.dispose);
|
||||||
final FocusAttachment child2Attachment = child2.attach(context);
|
final FocusAttachment child2Attachment = child2.attach(context);
|
||||||
parent1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
parent1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
child1Attachment.reparent(parent: parent1);
|
child1Attachment.reparent(parent: parent1);
|
||||||
@ -1618,9 +1778,12 @@ void main() {
|
|||||||
expect(()=> FocusNode().dispose(), dispatchesMemoryEvents(FocusNode));
|
expect(()=> FocusNode().dispose(), dispatchesMemoryEvents(FocusNode));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('FocusManager notifies listeners when a widget loses focus because it was removed.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('FocusManager notifies listeners when a widget loses focus because it was removed.', (WidgetTester tester) async {
|
||||||
final FocusNode nodeA = FocusNode(debugLabel: 'a');
|
final FocusNode nodeA = FocusNode(debugLabel: 'a');
|
||||||
|
addTearDown(nodeA.dispose);
|
||||||
final FocusNode nodeB = FocusNode(debugLabel: 'b');
|
final FocusNode nodeB = FocusNode(debugLabel: 'b');
|
||||||
|
addTearDown(nodeB.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.rtl,
|
textDirection: TextDirection.rtl,
|
||||||
@ -1664,7 +1827,7 @@ void main() {
|
|||||||
tester.binding.focusManager.removeListener(handleFocusChange);
|
tester.binding.focusManager.removeListener(handleFocusChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('debugFocusChanges causes logging of focus changes', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('debugFocusChanges causes logging of focus changes', (WidgetTester tester) async {
|
||||||
final bool oldDebugFocusChanges = debugFocusChanges;
|
final bool oldDebugFocusChanges = debugFocusChanges;
|
||||||
final DebugPrintCallback oldDebugPrint = debugPrint;
|
final DebugPrintCallback oldDebugPrint = debugPrint;
|
||||||
final StringBuffer messages = StringBuffer();
|
final StringBuffer messages = StringBuffer();
|
||||||
@ -1675,8 +1838,10 @@ void main() {
|
|||||||
try {
|
try {
|
||||||
final BuildContext context = await setupWidget(tester);
|
final BuildContext context = await setupWidget(tester);
|
||||||
final FocusScopeNode parent1 = FocusScopeNode(debugLabel: 'parent1');
|
final FocusScopeNode parent1 = FocusScopeNode(debugLabel: 'parent1');
|
||||||
|
addTearDown(parent1.dispose);
|
||||||
final FocusAttachment parent1Attachment = parent1.attach(context);
|
final FocusAttachment parent1Attachment = parent1.attach(context);
|
||||||
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
final FocusNode child1 = FocusNode(debugLabel: 'child1');
|
||||||
|
addTearDown(child1.dispose);
|
||||||
final FocusAttachment child1Attachment = child1.attach(context);
|
final FocusAttachment child1Attachment = child1.attach(context);
|
||||||
parent1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
parent1Attachment.reparent(parent: tester.binding.focusManager.rootScope);
|
||||||
child1Attachment.reparent(parent: parent1);
|
child1Attachment.reparent(parent: parent1);
|
||||||
@ -1709,7 +1874,7 @@ void main() {
|
|||||||
expect(messagesStr, contains(RegExp(r'FOCUS: Scheduling update, current focus is null, next focus will be FocusScopeNode#.*parent1')));
|
expect(messagesStr, contains(RegExp(r'FOCUS: Scheduling update, current focus is null, next focus will be FocusScopeNode#.*parent1')));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("doesn't call toString on a focus node when debugFocusChanges is false", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("doesn't call toString on a focus node when debugFocusChanges is false", (WidgetTester tester) async {
|
||||||
final bool oldDebugFocusChanges = debugFocusChanges;
|
final bool oldDebugFocusChanges = debugFocusChanges;
|
||||||
final DebugPrintCallback oldDebugPrint = debugPrint;
|
final DebugPrintCallback oldDebugPrint = debugPrint;
|
||||||
final StringBuffer messages = StringBuffer();
|
final StringBuffer messages = StringBuffer();
|
||||||
|
@ -6,12 +6,13 @@ import 'package:flutter/semantics.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
||||||
|
|
||||||
import 'semantics_tester.dart';
|
import 'semantics_tester.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('FocusScope', () {
|
group('FocusScope', () {
|
||||||
testWidgets('Can focus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can focus', (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> key = GlobalKey();
|
final GlobalKey<TestFocusState> key = GlobalKey();
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -27,7 +28,7 @@ void main() {
|
|||||||
expect(find.text('A FOCUSED'), findsOneWidget);
|
expect(find.text('A FOCUSED'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can unfocus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can unfocus', (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -62,7 +63,7 @@ void main() {
|
|||||||
expect(find.text('B FOCUSED'), findsOneWidget);
|
expect(find.text('B FOCUSED'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Autofocus works', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Autofocus works', (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -82,7 +83,7 @@ void main() {
|
|||||||
expect(find.text('B FOCUSED'), findsOneWidget);
|
expect(find.text('B FOCUSED'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can have multiple focused children and they update accordingly', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can have multiple focused children and they update accordingly', (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
|
|
||||||
@ -129,9 +130,11 @@ void main() {
|
|||||||
|
|
||||||
// This moves a focus node first into a focus scope that is added to its
|
// This moves a focus node first into a focus scope that is added to its
|
||||||
// parent, and then out of that focus scope again.
|
// parent, and then out of that focus scope again.
|
||||||
testWidgets('Can move focus in and out of FocusScope', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can move focus in and out of FocusScope', (WidgetTester tester) async {
|
||||||
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope Node');
|
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope Node');
|
||||||
|
addTearDown(parentFocusScope.dispose);
|
||||||
final FocusScopeNode childFocusScope = FocusScopeNode(debugLabel: 'Child Scope Node');
|
final FocusScopeNode childFocusScope = FocusScopeNode(debugLabel: 'Child Scope Node');
|
||||||
|
addTearDown(childFocusScope.dispose);
|
||||||
final GlobalKey<TestFocusState> key = GlobalKey();
|
final GlobalKey<TestFocusState> key = GlobalKey();
|
||||||
|
|
||||||
// Initially create the focus inside of the parent FocusScope.
|
// Initially create the focus inside of the parent FocusScope.
|
||||||
@ -274,10 +277,13 @@ void main() {
|
|||||||
childAttachment.detach();
|
childAttachment.detach();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Setting first focus requests focus for the scope properly.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Setting first focus requests focus for the scope properly.', (WidgetTester tester) async {
|
||||||
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope Node');
|
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope Node');
|
||||||
|
addTearDown(parentFocusScope.dispose);
|
||||||
final FocusScopeNode childFocusScope1 = FocusScopeNode(debugLabel: 'Child Scope Node 1');
|
final FocusScopeNode childFocusScope1 = FocusScopeNode(debugLabel: 'Child Scope Node 1');
|
||||||
|
addTearDown(childFocusScope1.dispose);
|
||||||
final FocusScopeNode childFocusScope2 = FocusScopeNode(debugLabel: 'Child Scope Node 2');
|
final FocusScopeNode childFocusScope2 = FocusScopeNode(debugLabel: 'Child Scope Node 2');
|
||||||
|
addTearDown(childFocusScope2.dispose);
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey(debugLabel: 'Key A');
|
final GlobalKey<TestFocusState> keyA = GlobalKey(debugLabel: 'Key A');
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey(debugLabel: 'Key B');
|
final GlobalKey<TestFocusState> keyB = GlobalKey(debugLabel: 'Key B');
|
||||||
final GlobalKey<TestFocusState> keyC = GlobalKey(debugLabel: 'Key C');
|
final GlobalKey<TestFocusState> keyC = GlobalKey(debugLabel: 'Key C');
|
||||||
@ -376,7 +382,7 @@ void main() {
|
|||||||
expect(childFocusScope2.isFirstFocus, isFalse);
|
expect(childFocusScope2.isFirstFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Removing focused widget moves focus to next widget', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Removing focused widget moves focus to next widget', (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
|
|
||||||
@ -420,10 +426,12 @@ void main() {
|
|||||||
expect(find.text('b'), findsOneWidget);
|
expect(find.text('b'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Adding a new FocusScope attaches the child to its parent.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Adding a new FocusScope attaches the child to its parent.', (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope Node');
|
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope Node');
|
||||||
|
addTearDown(parentFocusScope.dispose);
|
||||||
final FocusScopeNode childFocusScope = FocusScopeNode(debugLabel: 'Child Scope Node');
|
final FocusScopeNode childFocusScope = FocusScopeNode(debugLabel: 'Child Scope Node');
|
||||||
|
addTearDown(childFocusScope.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -466,11 +474,15 @@ void main() {
|
|||||||
expect(find.text('A FOCUSED'), findsOneWidget);
|
expect(find.text('A FOCUSED'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Setting parentNode determines focus tree hierarchy.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Setting parentNode determines focus tree hierarchy.', (WidgetTester tester) async {
|
||||||
final FocusNode topNode = FocusNode(debugLabel: 'Top');
|
final FocusNode topNode = FocusNode(debugLabel: 'Top');
|
||||||
|
addTearDown(topNode.dispose);
|
||||||
final FocusNode parentNode = FocusNode(debugLabel: 'Parent');
|
final FocusNode parentNode = FocusNode(debugLabel: 'Parent');
|
||||||
|
addTearDown(parentNode.dispose);
|
||||||
final FocusNode childNode = FocusNode(debugLabel: 'Child');
|
final FocusNode childNode = FocusNode(debugLabel: 'Child');
|
||||||
|
addTearDown(childNode.dispose);
|
||||||
final FocusNode insertedNode = FocusNode(debugLabel: 'Inserted');
|
final FocusNode insertedNode = FocusNode(debugLabel: 'Inserted');
|
||||||
|
addTearDown(insertedNode.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -532,11 +544,15 @@ void main() {
|
|||||||
expect(insertedNode.hasFocus, isFalse);
|
expect(insertedNode.hasFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Setting parentNode determines focus scope tree hierarchy.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Setting parentNode determines focus scope tree hierarchy.', (WidgetTester tester) async {
|
||||||
final FocusScopeNode topNode = FocusScopeNode(debugLabel: 'Top');
|
final FocusScopeNode topNode = FocusScopeNode(debugLabel: 'Top');
|
||||||
|
addTearDown(topNode.dispose);
|
||||||
final FocusScopeNode parentNode = FocusScopeNode(debugLabel: 'Parent');
|
final FocusScopeNode parentNode = FocusScopeNode(debugLabel: 'Parent');
|
||||||
|
addTearDown(parentNode.dispose);
|
||||||
final FocusScopeNode childNode = FocusScopeNode(debugLabel: 'Child');
|
final FocusScopeNode childNode = FocusScopeNode(debugLabel: 'Child');
|
||||||
|
addTearDown(childNode.dispose);
|
||||||
final FocusScopeNode insertedNode = FocusScopeNode(debugLabel: 'Inserted');
|
final FocusScopeNode insertedNode = FocusScopeNode(debugLabel: 'Inserted');
|
||||||
|
addTearDown(insertedNode.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusScope.withExternalFocusNode(
|
FocusScope.withExternalFocusNode(
|
||||||
@ -599,10 +615,11 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Arguably, this isn't correct behavior, but it is what happens now.
|
// Arguably, this isn't correct behavior, but it is what happens now.
|
||||||
testWidgets("Removing focused widget doesn't move focus to next widget within FocusScope", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Removing focused widget doesn't move focus to next widget within FocusScope", (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope');
|
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope');
|
||||||
|
addTearDown(parentFocusScope.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -656,12 +673,13 @@ void main() {
|
|||||||
expect(find.text('b'), findsOneWidget);
|
expect(find.text('b'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Removing a FocusScope removes its node from the tree', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Removing a FocusScope removes its node from the tree', (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> scopeKeyA = GlobalKey();
|
final GlobalKey<TestFocusState> scopeKeyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> scopeKeyB = GlobalKey();
|
final GlobalKey<TestFocusState> scopeKeyB = GlobalKey();
|
||||||
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope');
|
final FocusScopeNode parentFocusScope = FocusScopeNode(debugLabel: 'Parent Scope');
|
||||||
|
addTearDown(parentFocusScope.dispose);
|
||||||
|
|
||||||
// This checks both FocusScopes that have their own nodes, as well as those
|
// This checks both FocusScopes that have their own nodes, as well as those
|
||||||
// that use external nodes.
|
// that use external nodes.
|
||||||
@ -719,13 +737,15 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// By "pinned", it means kept in the tree by a GlobalKey.
|
// By "pinned", it means kept in the tree by a GlobalKey.
|
||||||
testWidgets("Removing pinned focused scope doesn't move focus to focused widget within next FocusScope", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Removing pinned focused scope doesn't move focus to focused widget within next FocusScope", (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> scopeKeyA = GlobalKey();
|
final GlobalKey<TestFocusState> scopeKeyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> scopeKeyB = GlobalKey();
|
final GlobalKey<TestFocusState> scopeKeyB = GlobalKey();
|
||||||
final FocusScopeNode parentFocusScope1 = FocusScopeNode(debugLabel: 'Parent Scope 1');
|
final FocusScopeNode parentFocusScope1 = FocusScopeNode(debugLabel: 'Parent Scope 1');
|
||||||
|
addTearDown(parentFocusScope1.dispose);
|
||||||
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Parent Scope 2');
|
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Parent Scope 2');
|
||||||
|
addTearDown(parentFocusScope2.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
FocusTraversalGroup(
|
||||||
@ -805,11 +825,13 @@ void main() {
|
|||||||
expect(find.text('B FOCUSED'), findsOneWidget);
|
expect(find.text('B FOCUSED'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Removing unpinned focused scope doesn't move focus to focused widget within next FocusScope", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Removing unpinned focused scope doesn't move focus to focused widget within next FocusScope", (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
final FocusScopeNode parentFocusScope1 = FocusScopeNode(debugLabel: 'Parent Scope 1');
|
final FocusScopeNode parentFocusScope1 = FocusScopeNode(debugLabel: 'Parent Scope 1');
|
||||||
|
addTearDown(parentFocusScope1.dispose);
|
||||||
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Parent Scope 2');
|
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Parent Scope 2');
|
||||||
|
addTearDown(parentFocusScope2.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
FocusTraversalGroup(
|
||||||
@ -885,9 +907,11 @@ void main() {
|
|||||||
expect(find.text('B FOCUSED'), findsOneWidget);
|
expect(find.text('B FOCUSED'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Moving widget from one scope to another retains focus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Moving widget from one scope to another retains focus', (WidgetTester tester) async {
|
||||||
final FocusScopeNode parentFocusScope1 = FocusScopeNode();
|
final FocusScopeNode parentFocusScope1 = FocusScopeNode();
|
||||||
|
addTearDown(parentFocusScope1.dispose);
|
||||||
final FocusScopeNode parentFocusScope2 = FocusScopeNode();
|
final FocusScopeNode parentFocusScope2 = FocusScopeNode();
|
||||||
|
addTearDown(parentFocusScope2.dispose);
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
|
|
||||||
@ -966,9 +990,11 @@ void main() {
|
|||||||
expect(find.text('b'), findsOneWidget);
|
expect(find.text('b'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Moving FocusScopeNodes retains focus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Moving FocusScopeNodes retains focus', (WidgetTester tester) async {
|
||||||
final FocusScopeNode parentFocusScope1 = FocusScopeNode(debugLabel: 'Scope 1');
|
final FocusScopeNode parentFocusScope1 = FocusScopeNode(debugLabel: 'Scope 1');
|
||||||
|
addTearDown(parentFocusScope1.dispose);
|
||||||
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Scope 2');
|
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Scope 2');
|
||||||
|
addTearDown(parentFocusScope2.dispose);
|
||||||
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
final GlobalKey<TestFocusState> keyA = GlobalKey();
|
||||||
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
final GlobalKey<TestFocusState> keyB = GlobalKey();
|
||||||
|
|
||||||
@ -1052,7 +1078,7 @@ void main() {
|
|||||||
expect(find.text('b'), findsOneWidget);
|
expect(find.text('b'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can focus root node.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can focus root node.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Focus(
|
Focus(
|
||||||
@ -1071,8 +1097,9 @@ void main() {
|
|||||||
expect(rootNode, equals(firstElement.owner!.focusManager.rootScope));
|
expect(rootNode, equals(firstElement.owner!.focusManager.rootScope));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can autofocus a node.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can autofocus a node.', (WidgetTester tester) async {
|
||||||
final FocusNode focusNode = FocusNode(debugLabel: 'Test Node');
|
final FocusNode focusNode = FocusNode(debugLabel: 'Test Node');
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Focus(
|
Focus(
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
@ -1095,9 +1122,11 @@ void main() {
|
|||||||
expect(focusNode.hasPrimaryFocus, isTrue);
|
expect(focusNode.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Won't autofocus a node if one is already focused.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Won't autofocus a node if one is already focused.", (WidgetTester tester) async {
|
||||||
final FocusNode focusNodeA = FocusNode(debugLabel: 'Test Node A');
|
final FocusNode focusNodeA = FocusNode(debugLabel: 'Test Node A');
|
||||||
|
addTearDown(focusNodeA.dispose);
|
||||||
final FocusNode focusNodeB = FocusNode(debugLabel: 'Test Node B');
|
final FocusNode focusNodeB = FocusNode(debugLabel: 'Test Node B');
|
||||||
|
addTearDown(focusNodeB.dispose);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Column(
|
Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -1134,9 +1163,10 @@ void main() {
|
|||||||
expect(focusNodeA.hasPrimaryFocus, isTrue);
|
expect(focusNodeA.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("FocusScope doesn't update the focusNode attributes when the widget updates if withExternalFocusNode is used", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("FocusScope doesn't update the focusNode attributes when the widget updates if withExternalFocusNode is used", (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final FocusScopeNode focusScopeNode = FocusScopeNode();
|
final FocusScopeNode focusScopeNode = FocusScopeNode();
|
||||||
|
addTearDown(focusScopeNode.dispose);
|
||||||
bool? keyEventHandled;
|
bool? keyEventHandled;
|
||||||
KeyEventResult handleCallback(FocusNode node, RawKeyEvent event) {
|
KeyEventResult handleCallback(FocusNode node, RawKeyEvent event) {
|
||||||
keyEventHandled = true;
|
keyEventHandled = true;
|
||||||
@ -1205,7 +1235,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('Focus', () {
|
group('Focus', () {
|
||||||
testWidgets('Focus.of stops at the nearest Focus widget.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus.of stops at the nearest Focus widget.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
@ -1213,6 +1243,7 @@ void main() {
|
|||||||
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
||||||
final GlobalKey key6 = GlobalKey(debugLabel: '6');
|
final GlobalKey key6 = GlobalKey(debugLabel: '6');
|
||||||
final FocusScopeNode scopeNode = FocusScopeNode();
|
final FocusScopeNode scopeNode = FocusScopeNode();
|
||||||
|
addTearDown(scopeNode.dispose);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusScope(
|
FocusScope(
|
||||||
key: key1,
|
key: key1,
|
||||||
@ -1252,7 +1283,7 @@ void main() {
|
|||||||
expect(Focus.of(element5).parent!.parent, equals(root));
|
expect(Focus.of(element5).parent!.parent, equals(root));
|
||||||
expect(Focus.of(element6).parent!.parent!.parent, equals(root));
|
expect(Focus.of(element6).parent!.parent!.parent, equals(root));
|
||||||
});
|
});
|
||||||
testWidgets('Can traverse Focus children.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can traverse Focus children.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
@ -1326,7 +1357,7 @@ void main() {
|
|||||||
expect(keys, equals(<Key>[key7, key8]));
|
expect(keys, equals(<Key>[key7, key8]));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can set focus.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can set focus.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
late bool gotFocus;
|
late bool gotFocus;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1346,7 +1377,7 @@ void main() {
|
|||||||
expect(node.hasFocus, isTrue);
|
expect(node.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus is ignored when set to not focusable.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus is ignored when set to not focusable.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
bool? gotFocus;
|
bool? gotFocus;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1367,7 +1398,7 @@ void main() {
|
|||||||
expect(node.hasFocus, isFalse);
|
expect(node.hasFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus is lost when set to not focusable.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus is lost when set to not focusable.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
bool? gotFocus;
|
bool? gotFocus;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1407,10 +1438,11 @@ void main() {
|
|||||||
expect(node.hasFocus, isFalse);
|
expect(node.hasFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Child of unfocusable Focus can get focus.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Child of unfocusable Focus can get focus.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
bool? gotFocus;
|
bool? gotFocus;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Focus(
|
Focus(
|
||||||
@ -1439,7 +1471,7 @@ void main() {
|
|||||||
expect(unfocusableNode.hasFocus, isTrue);
|
expect(unfocusableNode.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Nodes are removed when all Focuses are removed.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Nodes are removed when all Focuses are removed.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
late bool gotFocus;
|
late bool gotFocus;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1465,7 +1497,7 @@ void main() {
|
|||||||
expect(FocusManager.instance.rootScope.descendants, isEmpty);
|
expect(FocusManager.instance.rootScope.descendants, isEmpty);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus widgets set Semantics information about focus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus widgets set Semantics information about focus', (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> key = GlobalKey();
|
final GlobalKey<TestFocusState> key = GlobalKey();
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1494,7 +1526,7 @@ void main() {
|
|||||||
expect(semantics.hasFlag(SemanticsFlag.isFocusable), isFalse);
|
expect(semantics.hasFlag(SemanticsFlag.isFocusable), isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Setting canRequestFocus on focus node causes update.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Setting canRequestFocus on focus node causes update.', (WidgetTester tester) async {
|
||||||
final GlobalKey<TestFocusState> key = GlobalKey();
|
final GlobalKey<TestFocusState> key = GlobalKey();
|
||||||
|
|
||||||
final TestFocus testFocus = TestFocus(key: key);
|
final TestFocus testFocus = TestFocus(key: key);
|
||||||
@ -1511,7 +1543,7 @@ void main() {
|
|||||||
expect(key.currentState!.focusNode.canRequestFocus, isFalse);
|
expect(key.currentState!.focusNode.canRequestFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('canRequestFocus causes descendants of scope to be skipped.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('canRequestFocus causes descendants of scope to be skipped.', (WidgetTester tester) async {
|
||||||
final GlobalKey scope1 = GlobalKey(debugLabel: 'scope1');
|
final GlobalKey scope1 = GlobalKey(debugLabel: 'scope1');
|
||||||
final GlobalKey scope2 = GlobalKey(debugLabel: 'scope2');
|
final GlobalKey scope2 = GlobalKey(debugLabel: 'scope2');
|
||||||
final GlobalKey focus1 = GlobalKey(debugLabel: 'focus1');
|
final GlobalKey focus1 = GlobalKey(debugLabel: 'focus1');
|
||||||
@ -1620,11 +1652,15 @@ void main() {
|
|||||||
expect(Focus.of(container1.currentContext!).hasFocus, isTrue);
|
expect(Focus.of(container1.currentContext!).hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('skipTraversal works as expected.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('skipTraversal works as expected.', (WidgetTester tester) async {
|
||||||
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1');
|
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope1');
|
||||||
|
addTearDown(scope1.dispose);
|
||||||
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
final FocusScopeNode scope2 = FocusScopeNode(debugLabel: 'scope2');
|
||||||
|
addTearDown(scope2.dispose);
|
||||||
final FocusNode focus1 = FocusNode(debugLabel: 'focus1');
|
final FocusNode focus1 = FocusNode(debugLabel: 'focus1');
|
||||||
|
addTearDown(focus1.dispose);
|
||||||
final FocusNode focus2 = FocusNode(debugLabel: 'focus2');
|
final FocusNode focus2 = FocusNode(debugLabel: 'focus2');
|
||||||
|
addTearDown(focus2.dispose);
|
||||||
|
|
||||||
Future<void> pumpTest({
|
Future<void> pumpTest({
|
||||||
bool traverseScope1 = false,
|
bool traverseScope1 = false,
|
||||||
@ -1674,10 +1710,11 @@ void main() {
|
|||||||
expect(scope1.traversalDescendants, equals(<FocusNode>[focus2, focus1, scope2]));
|
expect(scope1.traversalDescendants, equals(<FocusNode>[focus2, focus1, scope2]));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('descendantsAreFocusable works as expected.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('descendantsAreFocusable works as expected.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
bool? gotFocus;
|
bool? gotFocus;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Focus(
|
Focus(
|
||||||
@ -1713,11 +1750,15 @@ void main() {
|
|||||||
expect(unfocusableNode.hasFocus, isFalse);
|
expect(unfocusableNode.hasFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('descendantsAreTraversable works as expected.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('descendantsAreTraversable works as expected.', (WidgetTester tester) async {
|
||||||
final FocusScopeNode scopeNode = FocusScopeNode(debugLabel: 'scope');
|
final FocusScopeNode scopeNode = FocusScopeNode(debugLabel: 'scope');
|
||||||
|
addTearDown(scopeNode.dispose);
|
||||||
final FocusNode node1 = FocusNode(debugLabel: 'node 1');
|
final FocusNode node1 = FocusNode(debugLabel: 'node 1');
|
||||||
|
addTearDown(node1.dispose);
|
||||||
final FocusNode node2 = FocusNode(debugLabel: 'node 2');
|
final FocusNode node2 = FocusNode(debugLabel: 'node 2');
|
||||||
|
addTearDown(node2.dispose);
|
||||||
final FocusNode node3 = FocusNode(debugLabel: 'node 3');
|
final FocusNode node3 = FocusNode(debugLabel: 'node 3');
|
||||||
|
addTearDown(node3.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -1746,7 +1787,7 @@ void main() {
|
|||||||
expect(node2.traversalDescendants, equals(<FocusNode>[]));
|
expect(node2.traversalDescendants, equals(<FocusNode>[]));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Focus doesn't introduce a Semantics node when includeSemantics is false", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Focus doesn't introduce a Semantics node when includeSemantics is false", (WidgetTester tester) async {
|
||||||
final SemanticsTester semantics = SemanticsTester(tester);
|
final SemanticsTester semantics = SemanticsTester(tester);
|
||||||
await tester.pumpWidget(Focus(includeSemantics: false, child: Container()));
|
await tester.pumpWidget(Focus(includeSemantics: false, child: Container()));
|
||||||
final TestSemantics expectedSemantics = TestSemantics.root();
|
final TestSemantics expectedSemantics = TestSemantics.root();
|
||||||
@ -1754,9 +1795,10 @@ void main() {
|
|||||||
semantics.dispose();
|
semantics.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus updates the onKey handler when the widget updates', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus updates the onKey handler when the widget updates', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
bool? keyEventHandled;
|
bool? keyEventHandled;
|
||||||
KeyEventResult handleCallback(FocusNode node, RawKeyEvent event) {
|
KeyEventResult handleCallback(FocusNode node, RawKeyEvent event) {
|
||||||
keyEventHandled = true;
|
keyEventHandled = true;
|
||||||
@ -1803,9 +1845,10 @@ void main() {
|
|||||||
expect(keyEventHandled, isTrue);
|
expect(keyEventHandled, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus updates the onKeyEvent handler when the widget updates', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus updates the onKeyEvent handler when the widget updates', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
bool? keyEventHandled;
|
bool? keyEventHandled;
|
||||||
KeyEventResult handleEventCallback(FocusNode node, KeyEvent event) {
|
KeyEventResult handleEventCallback(FocusNode node, KeyEvent event) {
|
||||||
keyEventHandled = true;
|
keyEventHandled = true;
|
||||||
@ -1852,9 +1895,10 @@ void main() {
|
|||||||
expect(keyEventHandled, isTrue);
|
expect(keyEventHandled, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Focus doesn't update the focusNode attributes when the widget updates if withExternalFocusNode is used", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Focus doesn't update the focusNode attributes when the widget updates if withExternalFocusNode is used", (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
bool? keyEventHandled;
|
bool? keyEventHandled;
|
||||||
KeyEventResult handleCallback(FocusNode node, RawKeyEvent event) {
|
KeyEventResult handleCallback(FocusNode node, RawKeyEvent event) {
|
||||||
keyEventHandled = true;
|
keyEventHandled = true;
|
||||||
@ -1921,7 +1965,7 @@ void main() {
|
|||||||
expect(keyEventHandled, isTrue);
|
expect(keyEventHandled, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus passes changes in attribute values to its focus node', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus passes changes in attribute values to its focus node', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Focus(
|
Focus(
|
||||||
child: Container(),
|
child: Container(),
|
||||||
@ -1931,10 +1975,11 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('ExcludeFocus', () {
|
group('ExcludeFocus', () {
|
||||||
testWidgets("Descendants of ExcludeFocus aren't focusable.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Descendants of ExcludeFocus aren't focusable.", (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
bool? gotFocus;
|
bool? gotFocus;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
ExcludeFocus(
|
ExcludeFocus(
|
||||||
@ -1970,10 +2015,13 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Regression test for https://github.com/flutter/flutter/issues/61700
|
// Regression test for https://github.com/flutter/flutter/issues/61700
|
||||||
testWidgets("ExcludeFocus doesn't transfer focus to another descendant.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("ExcludeFocus doesn't transfer focus to another descendant.", (WidgetTester tester) async {
|
||||||
final FocusNode parentFocusNode = FocusNode(debugLabel: 'group');
|
final FocusNode parentFocusNode = FocusNode(debugLabel: 'group');
|
||||||
|
addTearDown(parentFocusNode.dispose);
|
||||||
final FocusNode focusNode1 = FocusNode(debugLabel: 'node 1');
|
final FocusNode focusNode1 = FocusNode(debugLabel: 'node 1');
|
||||||
|
addTearDown(focusNode1.dispose);
|
||||||
final FocusNode focusNode2 = FocusNode(debugLabel: 'node 2');
|
final FocusNode focusNode2 = FocusNode(debugLabel: 'node 2');
|
||||||
|
addTearDown(focusNode2.dispose);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
ExcludeFocus(
|
ExcludeFocus(
|
||||||
excluding: false,
|
excluding: false,
|
||||||
@ -2039,7 +2087,7 @@ void main() {
|
|||||||
expect(parentFocusNode.enclosingScope!.hasPrimaryFocus, isTrue);
|
expect(parentFocusNode.enclosingScope!.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("ExcludeFocus doesn't introduce a Semantics node", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("ExcludeFocus doesn't introduce a Semantics node", (WidgetTester tester) async {
|
||||||
final SemanticsTester semantics = SemanticsTester(tester);
|
final SemanticsTester semantics = SemanticsTester(tester);
|
||||||
await tester.pumpWidget(ExcludeFocus(child: Container()));
|
await tester.pumpWidget(ExcludeFocus(child: Container()));
|
||||||
final TestSemantics expectedSemantics = TestSemantics.root();
|
final TestSemantics expectedSemantics = TestSemantics.root();
|
||||||
@ -2048,8 +2096,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Regression test for https://github.com/flutter/flutter/issues/92693
|
// Regression test for https://github.com/flutter/flutter/issues/92693
|
||||||
testWidgets('Setting parent FocusScope.canRequestFocus to false, does not set descendant Focus._internalNode._canRequestFocus to false', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Setting parent FocusScope.canRequestFocus to false, does not set descendant Focus._internalNode._canRequestFocus to false', (WidgetTester tester) async {
|
||||||
final FocusNode childFocusNode = FocusNode(debugLabel: 'node 1');
|
final FocusNode childFocusNode = FocusNode(debugLabel: 'node 1');
|
||||||
|
addTearDown(childFocusNode.dispose);
|
||||||
|
|
||||||
Widget buildFocusTree({required bool parentCanRequestFocus}) {
|
Widget buildFocusTree({required bool parentCanRequestFocus}) {
|
||||||
return FocusScope(
|
return FocusScope(
|
||||||
|
@ -8,12 +8,13 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
||||||
|
|
||||||
import 'semantics_tester.dart';
|
import 'semantics_tester.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group(WidgetOrderTraversalPolicy, () {
|
group(WidgetOrderTraversalPolicy, () {
|
||||||
testWidgets('Find the initial focus if there is none yet.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Find the initial focus if there is none yet.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
@ -52,7 +53,7 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Find the initial focus if there is none yet and traversing backwards.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Find the initial focus if there is none yet and traversing backwards.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
@ -95,7 +96,7 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Move focus to next node.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Move focus to next node.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
@ -212,7 +213,7 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Move focus to previous node.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Move focus to previous node.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
@ -286,9 +287,15 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Move focus to next/previous node while skipping nodes in policy', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Move focus to next/previous node while skipping nodes in policy', (WidgetTester tester) async {
|
||||||
final List<FocusNode> nodes =
|
final List<FocusNode> nodes =
|
||||||
List<FocusNode>.generate(7, (int index) => FocusNode(debugLabel: 'Node $index'));
|
List<FocusNode>.generate(7, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
FocusTraversalGroup(
|
||||||
policy: SkipAllButFirstAndLastPolicy(),
|
policy: SkipAllButFirstAndLastPolicy(),
|
||||||
@ -320,11 +327,14 @@ void main() {
|
|||||||
expect(nodes[0].hasPrimaryFocus, isTrue);
|
expect(nodes[0].hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Find the initial focus when a route is pushed or popped.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Find the initial focus when a route is pushed or popped.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final FocusNode testNode1 = FocusNode(debugLabel: 'First Focus Node');
|
final FocusNode testNode1 = FocusNode(debugLabel: 'First Focus Node');
|
||||||
|
addTearDown(testNode1.dispose);
|
||||||
final FocusNode testNode2 = FocusNode(debugLabel: 'Second Focus Node');
|
final FocusNode testNode2 = FocusNode(debugLabel: 'Second Focus Node');
|
||||||
|
addTearDown(testNode2.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: FocusTraversalGroup(
|
home: FocusTraversalGroup(
|
||||||
@ -386,9 +396,10 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Custom requestFocusCallback gets called on the next/previous focus.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Custom requestFocusCallback gets called on the next/previous focus.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final FocusNode testNode1 = FocusNode(debugLabel: 'Focus Node');
|
final FocusNode testNode1 = FocusNode(debugLabel: 'Focus Node');
|
||||||
|
addTearDown(testNode1.dispose);
|
||||||
bool calledCallback = false;
|
bool calledCallback = false;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -430,10 +441,14 @@ void main() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Nested navigator does not trap focus', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Nested navigator does not trap focus', (WidgetTester tester) async {
|
||||||
final FocusNode node1 = FocusNode();
|
final FocusNode node1 = FocusNode();
|
||||||
|
addTearDown(node1.dispose);
|
||||||
final FocusNode node2 = FocusNode();
|
final FocusNode node2 = FocusNode();
|
||||||
|
addTearDown(node2.dispose);
|
||||||
final FocusNode node3 = FocusNode();
|
final FocusNode node3 = FocusNode();
|
||||||
|
addTearDown(node3.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
@ -517,7 +532,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group(ReadingOrderTraversalPolicy, () {
|
group(ReadingOrderTraversalPolicy, () {
|
||||||
testWidgets('Find the initial focus if there is none yet.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Find the initial focus if there is none yet.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
@ -556,7 +571,7 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Move reading focus to next node.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Move reading focus to next node.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
@ -671,7 +686,7 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Move reading focus to previous node.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Move reading focus to previous node.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
@ -745,10 +760,17 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus order is correct in the presence of different directionalities.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus order is correct in the presence of different directionalities.', (WidgetTester tester) async {
|
||||||
const int nodeCount = 10;
|
const int nodeCount = 10;
|
||||||
final FocusScopeNode scopeNode = FocusScopeNode();
|
final FocusScopeNode scopeNode = FocusScopeNode();
|
||||||
|
addTearDown(scopeNode.dispose);
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Widget buildTest(TextDirection topDirection) {
|
Widget buildTest(TextDirection topDirection) {
|
||||||
return Directionality(
|
return Directionality(
|
||||||
textDirection: topDirection,
|
textDirection: topDirection,
|
||||||
@ -860,9 +882,15 @@ void main() {
|
|||||||
expect(order, orderedEquals(<int>[0, 1, 2, 4, 3, 5, 6, 8, 7, 9]));
|
expect(order, orderedEquals(<int>[0, 1, 2, 4, 3, 5, 6, 8, 7, 9]));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus order is reading order regardless of widget order, even when overlapping.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus order is reading order regardless of widget order, even when overlapping.', (WidgetTester tester) async {
|
||||||
const int nodeCount = 10;
|
const int nodeCount = 10;
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.rtl,
|
textDirection: TextDirection.rtl,
|
||||||
@ -954,9 +982,10 @@ void main() {
|
|||||||
expect(order, orderedEquals(<int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]));
|
expect(order, orderedEquals(<int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Custom requestFocusCallback gets called on the next/previous focus.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Custom requestFocusCallback gets called on the next/previous focus.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final FocusNode testNode1 = FocusNode(debugLabel: 'Focus Node');
|
final FocusNode testNode1 = FocusNode(debugLabel: 'Focus Node');
|
||||||
|
addTearDown(testNode1.dispose);
|
||||||
bool calledCallback = false;
|
bool calledCallback = false;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1001,7 +1030,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group(OrderedTraversalPolicy, () {
|
group(OrderedTraversalPolicy, () {
|
||||||
testWidgets('Find the initial focus if there is none yet.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Find the initial focus if there is none yet.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
await tester.pumpWidget(FocusTraversalGroup(
|
await tester.pumpWidget(FocusTraversalGroup(
|
||||||
@ -1040,9 +1069,15 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Fall back to the secondary sort if no FocusTraversalOrder exists.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Fall back to the secondary sort if no FocusTraversalOrder exists.', (WidgetTester tester) async {
|
||||||
const int nodeCount = 10;
|
const int nodeCount = 10;
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.rtl,
|
textDirection: TextDirection.rtl,
|
||||||
@ -1079,9 +1114,15 @@ void main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Move focus to next/previous node using numerical order.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Move focus to next/previous node using numerical order.', (WidgetTester tester) async {
|
||||||
const int nodeCount = 10;
|
const int nodeCount = 10;
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
@ -1120,12 +1161,18 @@ void main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Move focus to next/previous node using lexical order.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Move focus to next/previous node using lexical order.', (WidgetTester tester) async {
|
||||||
const int nodeCount = 10;
|
const int nodeCount = 10;
|
||||||
|
|
||||||
/// Generate ['J' ... 'A'];
|
/// Generate ['J' ... 'A'];
|
||||||
final List<String> keys = List<String>.generate(nodeCount, (int index) => String.fromCharCode('A'.codeUnits[0] + nodeCount - index - 1));
|
final List<String> keys = List<String>.generate(nodeCount, (int index) => String.fromCharCode('A'.codeUnits[0] + nodeCount - index - 1));
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node ${keys[index]}'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node ${keys[index]}'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
@ -1164,10 +1211,17 @@ void main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus order is correct in the presence of FocusTraversalPolicyGroups.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus order is correct in the presence of FocusTraversalPolicyGroups.', (WidgetTester tester) async {
|
||||||
const int nodeCount = 10;
|
const int nodeCount = 10;
|
||||||
final FocusScopeNode scopeNode = FocusScopeNode();
|
final FocusScopeNode scopeNode = FocusScopeNode();
|
||||||
|
addTearDown(scopeNode.dispose);
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
@ -1291,11 +1345,14 @@ void main() {
|
|||||||
expect(order, orderedEquals(expectedOrder));
|
expect(order, orderedEquals(expectedOrder));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Find the initial focus when a route is pushed or popped.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Find the initial focus when a route is pushed or popped.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final FocusNode testNode1 = FocusNode(debugLabel: 'First Focus Node');
|
final FocusNode testNode1 = FocusNode(debugLabel: 'First Focus Node');
|
||||||
|
addTearDown(testNode1.dispose);
|
||||||
final FocusNode testNode2 = FocusNode(debugLabel: 'Second Focus Node');
|
final FocusNode testNode2 = FocusNode(debugLabel: 'Second Focus Node');
|
||||||
|
addTearDown(testNode2.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: FocusTraversalGroup(
|
home: FocusTraversalGroup(
|
||||||
@ -1363,9 +1420,10 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Custom requestFocusCallback gets called on the next/previous focus.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Custom requestFocusCallback gets called on the next/previous focus.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final FocusNode testNode1 = FocusNode(debugLabel: 'Focus Node');
|
final FocusNode testNode1 = FocusNode(debugLabel: 'Focus Node');
|
||||||
|
addTearDown(testNode1.dispose);
|
||||||
bool calledCallback = false;
|
bool calledCallback = false;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1410,7 +1468,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group(DirectionalFocusTraversalPolicyMixin, () {
|
group(DirectionalFocusTraversalPolicyMixin, () {
|
||||||
testWidgets('Move focus in all directions.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Move focus in all directions.', (WidgetTester tester) async {
|
||||||
final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey');
|
final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey');
|
||||||
final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');
|
final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');
|
||||||
final GlobalKey lowerLeftKey = GlobalKey(debugLabel: 'lowerLeftKey');
|
final GlobalKey lowerLeftKey = GlobalKey(debugLabel: 'lowerLeftKey');
|
||||||
@ -1550,9 +1608,15 @@ void main() {
|
|||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Directional focus avoids hysteresis.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Directional focus avoids hysteresis.', (WidgetTester tester) async {
|
||||||
List<bool?> focus = List<bool?>.generate(6, (int _) => null);
|
List<bool?> focus = List<bool?>.generate(6, (int _) => null);
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(6, (int index) => FocusNode(debugLabel: 'Node $index'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(6, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Focus makeFocus(int index) {
|
Focus makeFocus(int index) {
|
||||||
return Focus(
|
return Focus(
|
||||||
debugLabel: '[$index]',
|
debugLabel: '[$index]',
|
||||||
@ -1668,11 +1732,16 @@ void main() {
|
|||||||
clear();
|
clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Directional prefers the closest node even on irregular grids', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Directional prefers the closest node even on irregular grids', (WidgetTester tester) async {
|
||||||
const int cols = 3;
|
const int cols = 3;
|
||||||
const int rows = 3;
|
const int rows = 3;
|
||||||
List<bool?> focus = List<bool?>.generate(rows * cols, (int _) => null);
|
List<bool?> focus = List<bool?>.generate(rows * cols, (int _) => null);
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(rows * cols, (int index) => FocusNode(debugLabel: 'Node $index'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(rows * cols, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Widget makeFocus(int row, int col) {
|
Widget makeFocus(int row, int col) {
|
||||||
final int index = row * rows + col;
|
final int index = row * rows + col;
|
||||||
@ -1804,10 +1873,15 @@ void main() {
|
|||||||
clear();
|
clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Closest vertical is picked when only out of band items are considered', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Closest vertical is picked when only out of band items are considered', (WidgetTester tester) async {
|
||||||
const int rows = 4;
|
const int rows = 4;
|
||||||
List<bool?> focus = List<bool?>.generate(rows, (int _) => null);
|
List<bool?> focus = List<bool?>.generate(rows, (int _) => null);
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(rows, (int index) => FocusNode(debugLabel: 'Node $index'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(rows, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Widget makeFocus(int row) {
|
Widget makeFocus(int row) {
|
||||||
return Padding(
|
return Padding(
|
||||||
@ -1890,10 +1964,15 @@ void main() {
|
|||||||
clear();
|
clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Closest horizontal is picked when only out of band items are considered', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Closest horizontal is picked when only out of band items are considered', (WidgetTester tester) async {
|
||||||
const int cols = 4;
|
const int cols = 4;
|
||||||
List<bool?> focus = List<bool?>.generate(cols, (int _) => null);
|
List<bool?> focus = List<bool?>.generate(cols, (int _) => null);
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(cols, (int index) => FocusNode(debugLabel: 'Node $index'));
|
final List<FocusNode> nodes = List<FocusNode>.generate(cols, (int index) => FocusNode(debugLabel: 'Node $index'));
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Widget makeFocus(int col) {
|
Widget makeFocus(int col) {
|
||||||
return Padding(
|
return Padding(
|
||||||
@ -1976,7 +2055,7 @@ void main() {
|
|||||||
clear();
|
clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can find first focus in all directions.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can find first focus in all directions.', (WidgetTester tester) async {
|
||||||
final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey');
|
final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey');
|
||||||
final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');
|
final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');
|
||||||
final GlobalKey lowerLeftKey = GlobalKey(debugLabel: 'lowerLeftKey');
|
final GlobalKey lowerLeftKey = GlobalKey(debugLabel: 'lowerLeftKey');
|
||||||
@ -2036,10 +2115,13 @@ void main() {
|
|||||||
expect(policy.findFirstFocusInDirection(scope, TraversalDirection.right), equals(upperLeftNode));
|
expect(policy.findFirstFocusInDirection(scope, TraversalDirection.right), equals(upperLeftNode));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Can find focus when policy data dirty', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Can find focus when policy data dirty', (WidgetTester tester) async {
|
||||||
final FocusNode focusTop = FocusNode(debugLabel: 'top');
|
final FocusNode focusTop = FocusNode(debugLabel: 'top');
|
||||||
|
addTearDown(focusTop.dispose);
|
||||||
final FocusNode focusCenter = FocusNode(debugLabel: 'center');
|
final FocusNode focusCenter = FocusNode(debugLabel: 'center');
|
||||||
|
addTearDown(focusCenter.dispose);
|
||||||
final FocusNode focusBottom = FocusNode(debugLabel: 'bottom');
|
final FocusNode focusBottom = FocusNode(debugLabel: 'bottom');
|
||||||
|
addTearDown(focusBottom.dispose);
|
||||||
|
|
||||||
final FocusTraversalPolicy policy = ReadingOrderTraversalPolicy();
|
final FocusTraversalPolicy policy = ReadingOrderTraversalPolicy();
|
||||||
await tester.pumpWidget(FocusTraversalGroup(
|
await tester.pumpWidget(FocusTraversalGroup(
|
||||||
@ -2087,7 +2169,7 @@ void main() {
|
|||||||
expect(focusTop.hasFocus, isTrue);
|
expect(focusTop.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus traversal actions are invoked when shortcuts are used.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus traversal actions are invoked when shortcuts are used.', (WidgetTester tester) async {
|
||||||
final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey');
|
final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey');
|
||||||
final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');
|
final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');
|
||||||
final GlobalKey lowerLeftKey = GlobalKey(debugLabel: 'lowerLeftKey');
|
final GlobalKey lowerLeftKey = GlobalKey(debugLabel: 'lowerLeftKey');
|
||||||
@ -2176,7 +2258,7 @@ void main() {
|
|||||||
expect(Focus.of(upperLeftKey.currentContext!).hasPrimaryFocus, isTrue);
|
expect(Focus.of(upperLeftKey.currentContext!).hasPrimaryFocus, isTrue);
|
||||||
}, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347
|
}, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347
|
||||||
|
|
||||||
testWidgets('Focus traversal actions works when current focus skip traversal', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus traversal actions works when current focus skip traversal', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: 'key1');
|
final GlobalKey key1 = GlobalKey(debugLabel: 'key1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: 'key2');
|
final GlobalKey key2 = GlobalKey(debugLabel: 'key2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: 'key3');
|
final GlobalKey key3 = GlobalKey(debugLabel: 'key3');
|
||||||
@ -2231,12 +2313,21 @@ void main() {
|
|||||||
expect(Focus.of(key3.currentContext!).hasPrimaryFocus, isTrue);
|
expect(Focus.of(key3.currentContext!).hasPrimaryFocus, isTrue);
|
||||||
}, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347
|
}, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347
|
||||||
|
|
||||||
testWidgets('Focus traversal inside a vertical scrollable scrolls to stay visible.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus traversal inside a vertical scrollable scrolls to stay visible.', (WidgetTester tester) async {
|
||||||
final List<int> items = List<int>.generate(11, (int index) => index).toList();
|
final List<int> items = List<int>.generate(11, (int index) => index).toList();
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(11, (int index) => FocusNode(debugLabel: 'Item ${index + 1}')).toList();
|
final List<FocusNode> nodes = List<FocusNode>.generate(11, (int index) => FocusNode(debugLabel: 'Item ${index + 1}')).toList();
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
final FocusNode topNode = FocusNode(debugLabel: 'Header');
|
final FocusNode topNode = FocusNode(debugLabel: 'Header');
|
||||||
|
addTearDown(topNode.dispose);
|
||||||
final FocusNode bottomNode = FocusNode(debugLabel: 'Footer');
|
final FocusNode bottomNode = FocusNode(debugLabel: 'Footer');
|
||||||
|
addTearDown(bottomNode.dispose);
|
||||||
final ScrollController controller = ScrollController();
|
final ScrollController controller = ScrollController();
|
||||||
|
addTearDown(controller.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Column(
|
home: Column(
|
||||||
@ -2328,12 +2419,21 @@ void main() {
|
|||||||
expect(controller.offset, equals(0.0));
|
expect(controller.offset, equals(0.0));
|
||||||
}, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347
|
}, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347
|
||||||
|
|
||||||
testWidgets('Focus traversal inside a horizontal scrollable scrolls to stay visible.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus traversal inside a horizontal scrollable scrolls to stay visible.', (WidgetTester tester) async {
|
||||||
final List<int> items = List<int>.generate(11, (int index) => index).toList();
|
final List<int> items = List<int>.generate(11, (int index) => index).toList();
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(11, (int index) => FocusNode(debugLabel: 'Item ${index + 1}')).toList();
|
final List<FocusNode> nodes = List<FocusNode>.generate(11, (int index) => FocusNode(debugLabel: 'Item ${index + 1}')).toList();
|
||||||
|
addTearDown(() {
|
||||||
|
for (final FocusNode node in nodes) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
final FocusNode leftNode = FocusNode(debugLabel: 'Left Side');
|
final FocusNode leftNode = FocusNode(debugLabel: 'Left Side');
|
||||||
|
addTearDown(leftNode.dispose);
|
||||||
final FocusNode rightNode = FocusNode(debugLabel: 'Right Side');
|
final FocusNode rightNode = FocusNode(debugLabel: 'Right Side');
|
||||||
|
addTearDown(rightNode.dispose);
|
||||||
final ScrollController controller = ScrollController();
|
final ScrollController controller = ScrollController();
|
||||||
|
addTearDown(controller.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Row(
|
home: Row(
|
||||||
@ -2426,23 +2526,31 @@ void main() {
|
|||||||
expect(controller.offset, equals(0.0));
|
expect(controller.offset, equals(0.0));
|
||||||
}, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347
|
}, skip: isBrowser, variant: KeySimulatorTransitModeVariant.all()); // https://github.com/flutter/flutter/issues/35347
|
||||||
|
|
||||||
testWidgets('Arrow focus traversal actions can be re-enabled for text fields.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Arrow focus traversal actions can be re-enabled for text fields.', (WidgetTester tester) async {
|
||||||
final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey');
|
final GlobalKey upperLeftKey = GlobalKey(debugLabel: 'upperLeftKey');
|
||||||
final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');
|
final GlobalKey upperRightKey = GlobalKey(debugLabel: 'upperRightKey');
|
||||||
final GlobalKey lowerLeftKey = GlobalKey(debugLabel: 'lowerLeftKey');
|
final GlobalKey lowerLeftKey = GlobalKey(debugLabel: 'lowerLeftKey');
|
||||||
final GlobalKey lowerRightKey = GlobalKey(debugLabel: 'lowerRightKey');
|
final GlobalKey lowerRightKey = GlobalKey(debugLabel: 'lowerRightKey');
|
||||||
|
|
||||||
final TextEditingController controller1 = TextEditingController();
|
final TextEditingController controller1 = TextEditingController();
|
||||||
|
addTearDown(controller1.dispose);
|
||||||
final TextEditingController controller2 = TextEditingController();
|
final TextEditingController controller2 = TextEditingController();
|
||||||
|
addTearDown(controller2.dispose);
|
||||||
final TextEditingController controller3 = TextEditingController();
|
final TextEditingController controller3 = TextEditingController();
|
||||||
|
addTearDown(controller3.dispose);
|
||||||
final TextEditingController controller4 = TextEditingController();
|
final TextEditingController controller4 = TextEditingController();
|
||||||
|
addTearDown(controller4.dispose);
|
||||||
|
|
||||||
final FocusNode focusNodeUpperLeft = FocusNode(debugLabel: 'upperLeft');
|
final FocusNode focusNodeUpperLeft = FocusNode(debugLabel: 'upperLeft');
|
||||||
|
addTearDown(focusNodeUpperLeft.dispose);
|
||||||
final FocusNode focusNodeUpperRight = FocusNode(debugLabel: 'upperRight');
|
final FocusNode focusNodeUpperRight = FocusNode(debugLabel: 'upperRight');
|
||||||
|
addTearDown(focusNodeUpperRight.dispose);
|
||||||
final FocusNode focusNodeLowerLeft = FocusNode(debugLabel: 'lowerLeft');
|
final FocusNode focusNodeLowerLeft = FocusNode(debugLabel: 'lowerLeft');
|
||||||
|
addTearDown(focusNodeLowerLeft.dispose);
|
||||||
final FocusNode focusNodeLowerRight = FocusNode(debugLabel: 'lowerRight');
|
final FocusNode focusNodeLowerRight = FocusNode(debugLabel: 'lowerRight');
|
||||||
|
addTearDown(focusNodeLowerRight.dispose);
|
||||||
|
|
||||||
Widget generateTestWidgets(bool ignoreTextFields) {
|
Widget generatetestWidgetsWithLeakTracking(bool ignoreTextFields) {
|
||||||
final Map<ShortcutActivator, Intent> shortcuts = <ShortcutActivator, Intent>{
|
final Map<ShortcutActivator, Intent> shortcuts = <ShortcutActivator, Intent>{
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowLeft): DirectionalFocusIntent(TraversalDirection.left, ignoreTextFields: ignoreTextFields),
|
const SingleActivator(LogicalKeyboardKey.arrowLeft): DirectionalFocusIntent(TraversalDirection.left, ignoreTextFields: ignoreTextFields),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowRight): DirectionalFocusIntent(TraversalDirection.right, ignoreTextFields: ignoreTextFields),
|
const SingleActivator(LogicalKeyboardKey.arrowRight): DirectionalFocusIntent(TraversalDirection.right, ignoreTextFields: ignoreTextFields),
|
||||||
@ -2521,7 +2629,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(generateTestWidgets(false));
|
await tester.pumpWidget(generatetestWidgetsWithLeakTracking(false));
|
||||||
|
|
||||||
expect(focusNodeUpperLeft.hasPrimaryFocus, isTrue);
|
expect(focusNodeUpperLeft.hasPrimaryFocus, isTrue);
|
||||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||||
@ -2533,7 +2641,7 @@ void main() {
|
|||||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||||
expect(focusNodeUpperLeft.hasPrimaryFocus, isTrue);
|
expect(focusNodeUpperLeft.hasPrimaryFocus, isTrue);
|
||||||
|
|
||||||
await tester.pumpWidget(generateTestWidgets(true));
|
await tester.pumpWidget(generatetestWidgetsWithLeakTracking(true));
|
||||||
|
|
||||||
expect(focusNodeUpperLeft.hasPrimaryFocus, isTrue);
|
expect(focusNodeUpperLeft.hasPrimaryFocus, isTrue);
|
||||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||||
@ -2549,7 +2657,7 @@ void main() {
|
|||||||
expect(focusNodeUpperLeft.hasPrimaryFocus, isTrue);
|
expect(focusNodeUpperLeft.hasPrimaryFocus, isTrue);
|
||||||
}, variant: KeySimulatorTransitModeVariant.all());
|
}, variant: KeySimulatorTransitModeVariant.all());
|
||||||
|
|
||||||
testWidgets('Focus traversal does not break when no focusable is available on a MaterialApp', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus traversal does not break when no focusable is available on a MaterialApp', (WidgetTester tester) async {
|
||||||
final List<Object> events = <Object>[];
|
final List<Object> events = <Object>[];
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(home: Container()));
|
await tester.pumpWidget(MaterialApp(home: Container()));
|
||||||
@ -2565,7 +2673,7 @@ void main() {
|
|||||||
expect(events.length, 2);
|
expect(events.length, 2);
|
||||||
}, variant: KeySimulatorTransitModeVariant.all());
|
}, variant: KeySimulatorTransitModeVariant.all());
|
||||||
|
|
||||||
testWidgets('Focus traversal does not throw when no focusable is available in a group', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus traversal does not throw when no focusable is available in a group', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const MaterialApp(home: Scaffold(body: ListTile(title: Text('title')))));
|
await tester.pumpWidget(const MaterialApp(home: Scaffold(body: ListTile(title: Text('title')))));
|
||||||
final FocusNode? initialFocus = primaryFocus;
|
final FocusNode? initialFocus = primaryFocus;
|
||||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||||
@ -2573,7 +2681,7 @@ void main() {
|
|||||||
expect(primaryFocus, equals(initialFocus));
|
expect(primaryFocus, equals(initialFocus));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus traversal does not break when no focusable is available on a WidgetsApp', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Focus traversal does not break when no focusable is available on a WidgetsApp', (WidgetTester tester) async {
|
||||||
final List<RawKeyEvent> events = <RawKeyEvent>[];
|
final List<RawKeyEvent> events = <RawKeyEvent>[];
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -2599,9 +2707,10 @@ void main() {
|
|||||||
expect(events.length, 2);
|
expect(events.length, 2);
|
||||||
}, variant: KeySimulatorTransitModeVariant.all());
|
}, variant: KeySimulatorTransitModeVariant.all());
|
||||||
|
|
||||||
testWidgets('Custom requestFocusCallback gets called on focusInDirection up/down/left/right.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Custom requestFocusCallback gets called on focusInDirection up/down/left/right.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final FocusNode testNode1 = FocusNode(debugLabel: 'Focus Node');
|
final FocusNode testNode1 = FocusNode(debugLabel: 'Focus Node');
|
||||||
|
addTearDown(testNode1.dispose);
|
||||||
bool calledCallback = false;
|
bool calledCallback = false;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -2655,7 +2764,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group(FocusTraversalGroup, () {
|
group(FocusTraversalGroup, () {
|
||||||
testWidgets("Focus traversal group doesn't introduce a Semantics node", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Focus traversal group doesn't introduce a Semantics node", (WidgetTester tester) async {
|
||||||
final SemanticsTester semantics = SemanticsTester(tester);
|
final SemanticsTester semantics = SemanticsTester(tester);
|
||||||
await tester.pumpWidget(FocusTraversalGroup(child: Container()));
|
await tester.pumpWidget(FocusTraversalGroup(child: Container()));
|
||||||
final TestSemantics expectedSemantics = TestSemantics.root();
|
final TestSemantics expectedSemantics = TestSemantics.root();
|
||||||
@ -2663,11 +2772,13 @@ void main() {
|
|||||||
semantics.dispose();
|
semantics.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Descendants of FocusTraversalGroup aren't focusable if descendantsAreFocusable is false.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Descendants of FocusTraversalGroup aren't focusable if descendantsAreFocusable is false.", (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
bool? gotFocus;
|
bool? gotFocus;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
FocusTraversalGroup(
|
||||||
descendantsAreFocusable: false,
|
descendantsAreFocusable: false,
|
||||||
@ -2702,13 +2813,16 @@ void main() {
|
|||||||
expect(unfocusableNode.hasFocus, isFalse);
|
expect(unfocusableNode.hasFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Group applies correct policy if focus tree is different from widget tree.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Group applies correct policy if focus tree is different from widget tree.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
final GlobalKey key4 = GlobalKey(debugLabel: '4');
|
final GlobalKey key4 = GlobalKey(debugLabel: '4');
|
||||||
final FocusNode focusNode = FocusNode(debugLabel: 'child');
|
final FocusNode focusNode = FocusNode(debugLabel: 'child');
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
final FocusNode parentFocusNode = FocusNode(debugLabel: 'parent');
|
final FocusNode parentFocusNode = FocusNode(debugLabel: 'parent');
|
||||||
|
addTearDown(parentFocusNode.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Column(
|
Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -2744,9 +2858,11 @@ void main() {
|
|||||||
expect(FocusTraversalGroup.of(key2.currentContext!), const TypeMatcher<SkipAllButFirstAndLastPolicy>());
|
expect(FocusTraversalGroup.of(key2.currentContext!), const TypeMatcher<SkipAllButFirstAndLastPolicy>());
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Descendants of FocusTraversalGroup aren't traversable if descendantsAreTraversable is false.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Descendants of FocusTraversalGroup aren't traversable if descendantsAreTraversable is false.", (WidgetTester tester) async {
|
||||||
final FocusNode node1 = FocusNode();
|
final FocusNode node1 = FocusNode();
|
||||||
|
addTearDown(node1.dispose);
|
||||||
final FocusNode node2 = FocusNode();
|
final FocusNode node2 = FocusNode();
|
||||||
|
addTearDown(node2.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
FocusTraversalGroup(
|
||||||
@ -2779,9 +2895,11 @@ void main() {
|
|||||||
expect(node2.hasPrimaryFocus, isFalse);
|
expect(node2.hasPrimaryFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("FocusTraversalGroup with skipTraversal for all descendants set to true doesn't cause an exception.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("FocusTraversalGroup with skipTraversal for all descendants set to true doesn't cause an exception.", (WidgetTester tester) async {
|
||||||
final FocusNode node1 = FocusNode();
|
final FocusNode node1 = FocusNode();
|
||||||
|
addTearDown(node1.dispose);
|
||||||
final FocusNode node2 = FocusNode();
|
final FocusNode node2 = FocusNode();
|
||||||
|
addTearDown(node2.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
FocusTraversalGroup(
|
||||||
@ -2815,11 +2933,13 @@ void main() {
|
|||||||
expect(node2.hasPrimaryFocus, isFalse);
|
expect(node2.hasPrimaryFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Nested FocusTraversalGroup with unfocusable children doesn't assert.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Nested FocusTraversalGroup with unfocusable children doesn't assert.", (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
bool? gotFocus;
|
bool? gotFocus;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
FocusTraversalGroup(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -2864,9 +2984,11 @@ void main() {
|
|||||||
expect(unfocusableNode.hasFocus, isFalse);
|
expect(unfocusableNode.hasFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Empty FocusTraversalGroup doesn't cause an exception.", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Empty FocusTraversalGroup doesn't cause an exception.", (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey(debugLabel: 'Test Key');
|
final GlobalKey key = GlobalKey(debugLabel: 'Test Key');
|
||||||
final FocusNode focusNode = FocusNode(debugLabel: 'Test Node');
|
final FocusNode focusNode = FocusNode(debugLabel: 'Test Node');
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
FocusTraversalGroup(
|
||||||
child: Directionality(
|
child: Directionality(
|
||||||
@ -2895,9 +3017,11 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group(RawKeyboardListener, () {
|
group(RawKeyboardListener, () {
|
||||||
testWidgets('Raw keyboard listener introduces a Semantics node by default', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Raw keyboard listener introduces a Semantics node by default', (WidgetTester tester) async {
|
||||||
final SemanticsTester semantics = SemanticsTester(tester);
|
final SemanticsTester semantics = SemanticsTester(tester);
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
RawKeyboardListener(
|
RawKeyboardListener(
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
@ -2922,9 +3046,11 @@ void main() {
|
|||||||
semantics.dispose();
|
semantics.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Raw keyboard listener doesn't introduce a Semantics node when specified", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Raw keyboard listener doesn't introduce a Semantics node when specified", (WidgetTester tester) async {
|
||||||
final SemanticsTester semantics = SemanticsTester(tester);
|
final SemanticsTester semantics = SemanticsTester(tester);
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
|
addTearDown(focusNode.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
RawKeyboardListener(
|
RawKeyboardListener(
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
@ -2939,11 +3065,15 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group(ExcludeFocusTraversal, () {
|
group(ExcludeFocusTraversal, () {
|
||||||
testWidgets("Descendants aren't traversable", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Descendants aren't traversable", (WidgetTester tester) async {
|
||||||
final FocusNode node1 = FocusNode(debugLabel: 'node 1');
|
final FocusNode node1 = FocusNode(debugLabel: 'node 1');
|
||||||
|
addTearDown(node1.dispose);
|
||||||
final FocusNode node2 = FocusNode(debugLabel: 'node 2');
|
final FocusNode node2 = FocusNode(debugLabel: 'node 2');
|
||||||
|
addTearDown(node2.dispose);
|
||||||
final FocusNode node3 = FocusNode(debugLabel: 'node 3');
|
final FocusNode node3 = FocusNode(debugLabel: 'node 3');
|
||||||
|
addTearDown(node3.dispose);
|
||||||
final FocusNode node4 = FocusNode(debugLabel: 'node 4');
|
final FocusNode node4 = FocusNode(debugLabel: 'node 4');
|
||||||
|
addTearDown(node4.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
FocusTraversalGroup(
|
||||||
@ -2987,7 +3117,7 @@ void main() {
|
|||||||
expect(node4.hasPrimaryFocus, isTrue);
|
expect(node4.hasPrimaryFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Doesn't introduce a Semantics node", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Doesn't introduce a Semantics node", (WidgetTester tester) async {
|
||||||
final SemanticsTester semantics = SemanticsTester(tester);
|
final SemanticsTester semantics = SemanticsTester(tester);
|
||||||
await tester.pumpWidget(ExcludeFocusTraversal(child: Container()));
|
await tester.pumpWidget(ExcludeFocusTraversal(child: Container()));
|
||||||
final TestSemantics expectedSemantics = TestSemantics.root();
|
final TestSemantics expectedSemantics = TestSemantics.root();
|
||||||
@ -3003,9 +3133,11 @@ void main() {
|
|||||||
// other focusable HTML elements surrounding Flutter.
|
// other focusable HTML elements surrounding Flutter.
|
||||||
//
|
//
|
||||||
// See also: https://github.com/flutter/flutter/issues/114463
|
// See also: https://github.com/flutter/flutter/issues/114463
|
||||||
testWidgets('Default route edge traversal behavior', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Default route edge traversal behavior', (WidgetTester tester) async {
|
||||||
final FocusNode nodeA = FocusNode();
|
final FocusNode nodeA = FocusNode();
|
||||||
|
addTearDown(nodeA.dispose);
|
||||||
final FocusNode nodeB = FocusNode();
|
final FocusNode nodeB = FocusNode();
|
||||||
|
addTearDown(nodeB.dispose);
|
||||||
|
|
||||||
Future<bool> nextFocus() async {
|
Future<bool> nextFocus() async {
|
||||||
final bool result = Actions.invoke(
|
final bool result = Actions.invoke(
|
||||||
@ -3083,12 +3215,15 @@ void main() {
|
|||||||
// This test creates a FocusScopeNode configured to traverse focus in a closed
|
// This test creates a FocusScopeNode configured to traverse focus in a closed
|
||||||
// loop. After traversing one loop, it changes the behavior to leave the
|
// loop. After traversing one loop, it changes the behavior to leave the
|
||||||
// FlutterView, then verifies that the new behavior did indeed take effect.
|
// FlutterView, then verifies that the new behavior did indeed take effect.
|
||||||
testWidgets('FocusScopeNode.traversalEdgeBehavior takes effect after update', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('FocusScopeNode.traversalEdgeBehavior takes effect after update', (WidgetTester tester) async {
|
||||||
final FocusScopeNode scope = FocusScopeNode();
|
final FocusScopeNode scope = FocusScopeNode();
|
||||||
|
addTearDown(scope.dispose);
|
||||||
expect(scope.traversalEdgeBehavior, TraversalEdgeBehavior.closedLoop);
|
expect(scope.traversalEdgeBehavior, TraversalEdgeBehavior.closedLoop);
|
||||||
|
|
||||||
final FocusNode nodeA = FocusNode();
|
final FocusNode nodeA = FocusNode();
|
||||||
|
addTearDown(nodeA.dispose);
|
||||||
final FocusNode nodeB = FocusNode();
|
final FocusNode nodeB = FocusNode();
|
||||||
|
addTearDown(nodeB.dispose);
|
||||||
|
|
||||||
Future<bool> nextFocus() async {
|
Future<bool> nextFocus() async {
|
||||||
final bool result = Actions.invoke(
|
final bool result = Actions.invoke(
|
||||||
@ -3172,7 +3307,7 @@ void main() {
|
|||||||
expect(nodeB.hasFocus, true);
|
expect(nodeB.hasFocus, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('NextFocusAction converts invoke result to KeyEventResult', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('NextFocusAction converts invoke result to KeyEventResult', (WidgetTester tester) async {
|
||||||
expect(
|
expect(
|
||||||
NextFocusAction().toKeyEventResult(const NextFocusIntent(), true),
|
NextFocusAction().toKeyEventResult(const NextFocusIntent(), true),
|
||||||
KeyEventResult.handled,
|
KeyEventResult.handled,
|
||||||
@ -3183,7 +3318,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('PreviousFocusAction converts invoke result to KeyEventResult', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('PreviousFocusAction converts invoke result to KeyEventResult', (WidgetTester tester) async {
|
||||||
expect(
|
expect(
|
||||||
PreviousFocusAction().toKeyEventResult(const PreviousFocusIntent(), true),
|
PreviousFocusAction().toKeyEventResult(const PreviousFocusIntent(), true),
|
||||||
KeyEventResult.handled,
|
KeyEventResult.handled,
|
||||||
@ -3194,9 +3329,10 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('RequestFocusAction calls the RequestFocusIntent.requestFocusCallback', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('RequestFocusAction calls the RequestFocusIntent.requestFocusCallback', (WidgetTester tester) async {
|
||||||
bool calledCallback = false;
|
bool calledCallback = false;
|
||||||
final FocusNode nodeA = FocusNode();
|
final FocusNode nodeA = FocusNode();
|
||||||
|
addTearDown(nodeA.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user