Skip to content

Commit

Permalink
Reland: "Add code for updating focusedChild when removing grandchil…
Browse files Browse the repository at this point in the history
…dren from scope" (#136899)
  • Loading branch information
gspencergoog authored Oct 19, 2023
1 parent f82299e commit 0abb017
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 1 deletion.
8 changes: 7 additions & 1 deletion packages/flutter/lib/src/widgets/focus_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
40 changes: 40 additions & 0 deletions packages/flutter/test/widgets/focus_manager_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 0abb017

Please sign in to comment.