Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
1bd8b580
Unverified
Commit
1bd8b580
authored
Jan 08, 2022
by
Greg Spencer
Committed by
GitHub
Jan 08, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make `FocusNode.traversalChildren` not be affected by parent's `canRequestFocus` (#95061)
parent
ee328e35
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
87 additions
and
7 deletions
+87
-7
focus_manager.dart
packages/flutter/lib/src/widgets/focus_manager.dart
+52
-7
focus_manager_test.dart
packages/flutter/test/widgets/focus_manager_test.dart
+35
-0
No files found.
packages/flutter/lib/src/widgets/focus_manager.dart
View file @
1bd8b580
...
...
@@ -446,15 +446,15 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
/// If set to false on a [FocusScopeNode], will cause all of the children of
/// the scope node to not be focusable.
///
/// If set to false on a [FocusNode], it will not affect the
children of the
/// node.
/// If set to false on a [FocusNode], it will not affect the
focusability of
///
children of the
node.
///
/// The [hasFocus] member can still return true if this node is the ancestor
/// of a node with primary focus.
///
/// This is different than [skipTraversal] because [skipTraversal] still
/// allows the node to be focused, just not traversed to via the
/// [FocusTraversalPolicy]
/// [FocusTraversalPolicy]
.
///
/// Setting [canRequestFocus] to false implies that the node will also be
/// skipped for traversal purposes.
...
...
@@ -575,9 +575,18 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
/// An iterator over the children that are allowed to be traversed by the
/// [FocusTraversalPolicy].
///
/// Returns the list of focusable, traversable children of this node,
/// regardless of those settings on this focus node. Will return an empty
/// iterable if [descendantsAreFocusable] is false.
///
/// See also
///
/// * [traversalDescendants], which traverses all of the node's descendants,
/// not just the immediate children.
Iterable
<
FocusNode
>
get
traversalChildren
{
if
(!
canRequestFocus
)
{
return
const
<
FocusNode
>[]
;
if
(!
descendantsAreFocusable
)
{
return
const
Iterable
<
FocusNode
>.
empty
()
;
}
return
children
.
where
(
(
FocusNode
node
)
=>
!
node
.
skipTraversal
&&
node
.
canRequestFocus
,
...
...
@@ -615,7 +624,12 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
/// Returns all descendants which do not have the [skipTraversal] and do have
/// the [canRequestFocus] flag set.
Iterable
<
FocusNode
>
get
traversalDescendants
=>
descendants
.
where
((
FocusNode
node
)
=>
!
node
.
skipTraversal
&&
node
.
canRequestFocus
);
Iterable
<
FocusNode
>
get
traversalDescendants
{
if
(!
descendantsAreFocusable
)
{
return
const
Iterable
<
FocusNode
>.
empty
();
}
return
descendants
.
where
((
FocusNode
node
)
=>
!
node
.
skipTraversal
&&
node
.
canRequestFocus
);
}
/// An [Iterable] over the ancestors of this node.
///
...
...
@@ -1184,6 +1198,37 @@ class FocusScopeNode extends FocusNode {
// last (which is the top of the stack).
final
List
<
FocusNode
>
_focusedChildren
=
<
FocusNode
>[];
/// An iterator over the children that are allowed to be traversed by the
/// [FocusTraversalPolicy].
///
/// Will return an empty iterable if this scope node is not focusable, or if
/// [descendantsAreFocusable] is false.
///
/// See also:
///
/// * [traversalDescendants], which traverses all of the node's descendants,
/// not just the immediate children.
@override
Iterable
<
FocusNode
>
get
traversalChildren
{
if
(!
canRequestFocus
)
{
return
const
Iterable
<
FocusNode
>.
empty
();
}
return
super
.
traversalChildren
;
}
/// Returns all descendants which do not have the [skipTraversal] and do have
/// the [canRequestFocus] flag set.
///
/// Will return an empty iterable if this scope node is not focusable, or if
/// [descendantsAreFocusable] is false.
@override
Iterable
<
FocusNode
>
get
traversalDescendants
{
if
(!
canRequestFocus
)
{
return
const
Iterable
<
FocusNode
>.
empty
();
}
return
super
.
traversalDescendants
;
}
/// Make the given [scope] the active child scope for this scope.
///
/// If the given [scope] is not yet a part of the focus tree, then add it to
...
...
@@ -1259,7 +1304,7 @@ class FocusScopeNode extends FocusNode {
final
List
<
String
>
childList
=
_focusedChildren
.
reversed
.
map
<
String
>((
FocusNode
child
)
{
return
child
.
toStringShort
();
}).
toList
();
properties
.
add
(
IterableProperty
<
String
>(
'focusedChildren'
,
childList
,
defaultValue:
<
String
>[]
));
properties
.
add
(
IterableProperty
<
String
>(
'focusedChildren'
,
childList
,
defaultValue:
const
Iterable
<
String
>.
empty
()
));
}
}
...
...
packages/flutter/test/widgets/focus_manager_test.dart
View file @
1bd8b580
...
...
@@ -151,6 +151,37 @@ void main() {
expect
(
scope
.
traversalDescendants
.
contains
(
child2
),
isFalse
);
});
testWidgets
(
"canRequestFocus doesn't affect traversalChildren"
,
(
WidgetTester
tester
)
async
{
final
BuildContext
context
=
await
setupWidget
(
tester
);
final
FocusScopeNode
scope
=
FocusScopeNode
(
debugLabel:
'Scope'
);
final
FocusAttachment
scopeAttachment
=
scope
.
attach
(
context
);
final
FocusNode
parent1
=
FocusNode
(
debugLabel:
'Parent 1'
);
final
FocusAttachment
parent1Attachment
=
parent1
.
attach
(
context
);
final
FocusNode
parent2
=
FocusNode
(
debugLabel:
'Parent 2'
);
final
FocusAttachment
parent2Attachment
=
parent2
.
attach
(
context
);
final
FocusNode
child1
=
FocusNode
(
debugLabel:
'Child 1'
);
final
FocusAttachment
child1Attachment
=
child1
.
attach
(
context
);
final
FocusNode
child2
=
FocusNode
(
debugLabel:
'Child 2'
);
final
FocusAttachment
child2Attachment
=
child2
.
attach
(
context
);
scopeAttachment
.
reparent
(
parent:
tester
.
binding
.
focusManager
.
rootScope
);
parent1Attachment
.
reparent
(
parent:
scope
);
parent2Attachment
.
reparent
(
parent:
scope
);
child1Attachment
.
reparent
(
parent:
parent1
);
child2Attachment
.
reparent
(
parent:
parent2
);
child1
.
requestFocus
();
await
tester
.
pump
();
expect
(
tester
.
binding
.
focusManager
.
primaryFocus
,
equals
(
child1
));
expect
(
scope
.
focusedChild
,
equals
(
child1
));
expect
(
parent2
.
traversalChildren
.
contains
(
child2
),
isTrue
);
expect
(
scope
.
traversalChildren
.
contains
(
parent2
),
isTrue
);
parent2
.
canRequestFocus
=
false
;
await
tester
.
pump
();
expect
(
parent2
.
traversalChildren
.
contains
(
child2
),
isTrue
);
expect
(
scope
.
traversalChildren
.
contains
(
parent2
),
isFalse
);
});
testWidgets
(
'implements debugFillProperties'
,
(
WidgetTester
tester
)
async
{
final
DiagnosticPropertiesBuilder
builder
=
DiagnosticPropertiesBuilder
();
FocusNode
(
...
...
@@ -502,6 +533,8 @@ void main() {
expect
(
scope
.
focusedChild
,
equals
(
child1
));
expect
(
scope
.
traversalDescendants
.
contains
(
child1
),
isTrue
);
expect
(
scope
.
traversalDescendants
.
contains
(
child2
),
isTrue
);
expect
(
scope
.
traversalChildren
.
contains
(
parent1
),
isTrue
);
expect
(
parent1
.
traversalChildren
.
contains
(
child2
),
isTrue
);
scope
.
canRequestFocus
=
false
;
await
tester
.
pump
();
...
...
@@ -512,6 +545,8 @@ void main() {
expect
(
scope
.
focusedChild
,
equals
(
child1
));
expect
(
scope
.
traversalDescendants
.
contains
(
child1
),
isFalse
);
expect
(
scope
.
traversalDescendants
.
contains
(
child2
),
isFalse
);
expect
(
scope
.
traversalChildren
.
contains
(
parent1
),
isFalse
);
expect
(
parent1
.
traversalChildren
.
contains
(
child2
),
isFalse
);
});
testWidgets
(
"skipTraversal doesn't affect children."
,
(
WidgetTester
tester
)
async
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment