Unverified Commit 24922425 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Add code for updating `focusedChild` when removing grandchildren from scope (#136771)

## Description

This adds code to make sure that grandchildren are removed from the `focusedChild` of a scope when the child is detached.

## Related Issues
 - Fixes https://github.com/flutter/flutter/issues/136758

## Tests
 - Added regression test.
parent 747128ff
......@@ -964,7 +964,13 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
assert(node._manager == _manager);
if (removeScopeFocus) {
node.enclosingScope?._focusedChildren.remove(node);
final FocusScopeNode? nodeScope = node.enclosingScope;
if (nodeScope != null) {
nodeScope._focusedChildren.remove(node);
node.descendants.where((FocusNode descendant) {
return descendant.enclosingScope == nodeScope;
}).forEach(nodeScope._focusedChildren.remove);
}
}
node._parent = null;
......
......@@ -459,6 +459,46 @@ void main() {
expect(child2.hasPrimaryFocus, isTrue);
});
// Regression test for https://github.com/flutter/flutter/issues/136758
testWidgetsWithLeakTracking('removing grandchildren from scope updates focusedChild', (WidgetTester tester) async {
final BuildContext context = await setupWidget(tester);
// Sets up this focus node tree:
//
// root
// |
// scope1
// |
// child1
// |
// child2
final FocusScopeNode scope1 = FocusScopeNode(debugLabel: 'scope2');
addTearDown(scope1.dispose);
final FocusAttachment scope2Attachment = scope1.attach(context);
scope2Attachment.reparent(parent: tester.binding.focusManager.rootScope);
final FocusNode child1 = FocusNode(debugLabel: 'child2');
addTearDown(child1.dispose);
final FocusAttachment child2Attachment = child1.attach(context);
final FocusNode child2 = FocusNode(debugLabel: 'child3');
addTearDown(child2.dispose);
final FocusAttachment child3Attachment = child2.attach(context);
child2Attachment.reparent(parent: scope1);
child3Attachment.reparent(parent: child1);
expect(child1.parent, equals(scope1));
expect(scope1.children.first, equals(child1));
child2.requestFocus();
await tester.pump();
expect(scope1.focusedChild, equals(child2));
// Detach the middle child and make sure that the scope is updated so that
// it no longer references child2 as the focused child.
child2Attachment.detach();
expect(scope1.focusedChild, isNull);
});
testWidgetsWithLeakTracking('Requesting focus before adding to tree results in a request after adding', (WidgetTester tester) async {
final BuildContext context = await setupWidget(tester);
final FocusScopeNode scope = FocusScopeNode();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment