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
69019664
Unverified
Commit
69019664
authored
May 05, 2020
by
Greg Spencer
Committed by
GitHub
May 05, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Focus the last node when asked to focus previous and nothing is selected. (#56160)
parent
0ecc7a4b
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
98 additions
and
21 deletions
+98
-21
focus_traversal.dart
packages/flutter/lib/src/widgets/focus_traversal.dart
+55
-21
focus_traversal_test.dart
packages/flutter/test/widgets/focus_traversal_test.dart
+43
-0
No files found.
packages/flutter/lib/src/widgets/focus_traversal.dart
View file @
69019664
...
...
@@ -120,11 +120,12 @@ abstract class FocusTraversalPolicy with Diagnosticable {
/// A const constructor so subclasses can be const.
const
FocusTraversalPolicy
();
/// Returns the node that should receive focus if
there is no current focus
///
in the nearest [FocusScopeNode] that `currentNode` belongs to
.
/// Returns the node that should receive focus if
focus is traversing
///
forwards, and there is no current focus
.
///
/// This is used by [next]/[previous]/[inDirection] to determine which node to
/// focus if they are called when no node is currently focused.
/// The node returned is the node that should receive focus if focus is
/// traversing forwards (i.e. with [next]), and there is no current focus in
/// the nearest [FocusScopeNode] that `currentNode` belongs to.
///
/// The `currentNode` argument must not be null.
///
...
...
@@ -132,13 +133,46 @@ abstract class FocusTraversalPolicy with Diagnosticable {
/// set, on the nearest scope of the `currentNode`, otherwise, returns the
/// first node from [sortDescendants], or the given `currentNode` if there are
/// no descendants.
FocusNode
findFirstFocus
(
FocusNode
currentNode
)
{
///
/// See also:
///
/// * [next], the function that is called to move the focus to the next node.
/// * [DirectionalFocusTraversalPolicyMixin.findFirstFocusInDirection], a
/// function that finds the first focusable widget in a particular direction.
FocusNode
findFirstFocus
(
FocusNode
currentNode
)
=>
_findInitialFocus
(
currentNode
);
/// Returns the node that should receive focus if focus is traversing
/// backwards, and there is no current focus.
///
/// The node returned is the one that should receive focus if focus is
/// traversing backwards (i.e. with [previous]), and there is no current focus
/// in the nearest [FocusScopeNode] that `currentNode` belongs to.
///
/// The `currentNode` argument must not be null.
///
/// The default implementation returns the [FocusScopeNode.focusedChild], if
/// set, on the nearest scope of the `currentNode`, otherwise, returns the
/// last node from [sortDescendants], or the given `currentNode` if there are
/// no descendants.
///
/// See also:
///
/// * [previous], the function that is called to move the focus to the next node.
/// * [DirectionalFocusTraversalPolicyMixin.findFirstFocusInDirection], a
/// function that finds the first focusable widget in a particular direction.
FocusNode
findLastFocus
(
FocusNode
currentNode
)
=>
_findInitialFocus
(
currentNode
,
fromEnd:
true
);
FocusNode
_findInitialFocus
(
FocusNode
currentNode
,
{
bool
fromEnd
=
false
})
{
assert
(
currentNode
!=
null
);
final
FocusScopeNode
scope
=
currentNode
.
nearestScope
;
FocusNode
candidate
=
scope
.
focusedChild
;
if
(
candidate
==
null
&&
scope
.
descendants
.
isNotEmpty
)
{
final
Iterable
<
FocusNode
>
sorted
=
_sortAllDescendants
(
scope
);
candidate
=
sorted
.
isNotEmpty
?
sorted
.
first
:
null
;
if
(
sorted
.
isEmpty
)
{
candidate
=
null
;
}
else
{
candidate
=
fromEnd
?
sorted
.
last
:
sorted
.
first
;
}
}
// If we still didn't find any candidate, use the current node as a
...
...
@@ -312,20 +346,20 @@ abstract class FocusTraversalPolicy with Diagnosticable {
return
sortedDescendants
;
}
// Moves the focus to the next node in the FocusScopeNode nearest to the
// currentNode argument, either in a forward or reverse direction, depending
// on the value of the forward argument.
//
// This function is called by the next and previous members to move to the
// next or previous node, respectively.
//
//
Uses findFirstFocus to find the first node if there is no
//
FocusScopeNode.focusedChild
set. If there is a focused child for the
// scope, then it calls sortDescendants to get a sorted list of descendants,
// and then finds the node after the current first focus of the scope if
// forward is true, and the node before it if forward is false.
//
// Returns true if a node requested focus.
//
/
Moves the focus to the next node in the FocusScopeNode nearest to the
//
/
currentNode argument, either in a forward or reverse direction, depending
//
/
on the value of the forward argument.
//
/
//
/
This function is called by the next and previous members to move to the
//
/
next or previous node, respectively.
//
/
//
/ Uses [findFirstFocus]/[findLastFocus] to find the first/last node if there is
//
/ no [FocusScopeNode.focusedChild]
set. If there is a focused child for the
//
/
scope, then it calls sortDescendants to get a sorted list of descendants,
//
/
and then finds the node after the current first focus of the scope if
//
/
forward is true, and the node before it if forward is false.
//
/
//
/
Returns true if a node requested focus.
@protected
bool
_moveFocus
(
FocusNode
currentNode
,
{
@required
bool
forward
})
{
assert
(
forward
!=
null
);
...
...
@@ -336,7 +370,7 @@ abstract class FocusTraversalPolicy with Diagnosticable {
invalidateScopeData
(
nearestScope
);
final
FocusNode
focusedChild
=
nearestScope
.
focusedChild
;
if
(
focusedChild
==
null
)
{
final
FocusNode
firstFocus
=
f
indFir
stFocus
(
currentNode
);
final
FocusNode
firstFocus
=
f
orward
?
findFirstFocus
(
currentNode
)
:
findLa
stFocus
(
currentNode
);
if
(
firstFocus
!=
null
)
{
_focusAndEnsureVisible
(
firstFocus
,
...
...
packages/flutter/test/widgets/focus_traversal_test.dart
View file @
69019664
...
...
@@ -54,6 +54,49 @@ void main() {
expect
(
scope
.
hasFocus
,
isTrue
);
});
testWidgets
(
'Find the initial focus if there is none yet and traversing backwards.'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key1
=
GlobalKey
(
debugLabel:
'1'
);
final
GlobalKey
key2
=
GlobalKey
(
debugLabel:
'2'
);
final
GlobalKey
key3
=
GlobalKey
(
debugLabel:
'3'
);
final
GlobalKey
key4
=
GlobalKey
(
debugLabel:
'4'
);
final
GlobalKey
key5
=
GlobalKey
(
debugLabel:
'5'
);
await
tester
.
pumpWidget
(
FocusTraversalGroup
(
policy:
WidgetOrderTraversalPolicy
(),
child:
FocusScope
(
key:
key1
,
child:
Column
(
children:
<
Widget
>[
Focus
(
key:
key2
,
child:
Container
(
key:
key3
,
width:
100
,
height:
100
),
),
Focus
(
key:
key4
,
child:
Container
(
key:
key5
,
width:
100
,
height:
100
),
),
],
),
),
));
final
Element
firstChild
=
tester
.
element
(
find
.
byKey
(
key3
));
final
Element
secondChild
=
tester
.
element
(
find
.
byKey
(
key5
));
final
FocusNode
firstFocusNode
=
Focus
.
of
(
firstChild
);
final
FocusNode
secondFocusNode
=
Focus
.
of
(
secondChild
);
final
FocusNode
scope
=
Focus
.
of
(
firstChild
).
enclosingScope
;
expect
(
firstFocusNode
.
hasFocus
,
isFalse
);
expect
(
secondFocusNode
.
hasFocus
,
isFalse
);
secondFocusNode
.
previousFocus
();
await
tester
.
pump
();
expect
(
firstFocusNode
.
hasFocus
,
isFalse
);
expect
(
secondFocusNode
.
hasFocus
,
isTrue
);
expect
(
scope
.
hasFocus
,
isTrue
);
});
testWidgets
(
'Move focus to next node.'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key1
=
GlobalKey
(
debugLabel:
'1'
);
final
GlobalKey
key2
=
GlobalKey
(
debugLabel:
'2'
);
...
...
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