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
1793108b
Unverified
Commit
1793108b
authored
Feb 20, 2020
by
Greg Spencer
Committed by
GitHub
Feb 20, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add additional focus samples. (#50846)
parent
7a83c6fc
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
437 additions
and
94 deletions
+437
-94
text_form_field.dart
packages/flutter/lib/src/material/text_form_field.dart
+48
-0
actions.dart
packages/flutter/lib/src/widgets/actions.dart
+4
-2
focus_manager.dart
packages/flutter/lib/src/widgets/focus_manager.dart
+68
-67
focus_scope.dart
packages/flutter/lib/src/widgets/focus_scope.dart
+311
-22
focus_traversal.dart
packages/flutter/lib/src/widgets/focus_traversal.dart
+6
-3
No files found.
packages/flutter/lib/src/material/text_form_field.dart
View file @
1793108b
...
...
@@ -62,6 +62,54 @@ export 'package:flutter/services.dart' show SmartQuotesType, SmartDashesType;
/// ```
/// {@end-tool}
///
/// {@tool dartpad --template=stateful_widget_material}
/// This example shows how to move the focus to the next field when the user
/// presses the ENTER key.
///
/// ```dart imports
/// import 'package:flutter/services.dart';
/// ```
///
/// ```dart
/// Widget build(BuildContext context) {
/// return Material(
/// child: Center(
/// child: Shortcuts(
/// shortcuts: <LogicalKeySet, Intent>{
/// // Pressing enter on the field will now move to the next field.
/// LogicalKeySet(LogicalKeyboardKey.enter):
/// Intent(NextFocusAction.key),
/// },
/// child: FocusTraversalGroup(
/// child: Form(
/// autovalidate: true,
/// onChanged: () {
/// Form.of(primaryFocus.context).save();
/// },
/// child: Wrap(
/// children: List<Widget>.generate(5, (int index) {
/// return Padding(
/// padding: const EdgeInsets.all(8.0),
/// child: ConstrainedBox(
/// constraints: BoxConstraints.tight(Size(200, 50)),
/// child: TextFormField(
/// onSaved: (String value) {
/// print('Value for field $index saved as "$value"');
/// },
/// ),
/// ),
/// );
/// }),
/// ),
/// ),
/// ),
/// ),
/// ),
/// );
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
/// * <https://material.io/design/components/text-fields.html>
...
...
packages/flutter/lib/src/widgets/actions.dart
View file @
1793108b
...
...
@@ -374,12 +374,14 @@ class Actions extends InheritedWidget {
/// {@tool dartpad --template=stateful_widget_material}
/// This example shows how keyboard interaction can be added to a custom control
/// that changes color when hovered and focused, and can toggle a light when
/// activated, either by touch or by hitting the `X` key on the keyboard.
/// activated, either by touch or by hitting the `X` key on the keyboard when
/// the "And Me" button has the keyboard focus (be sure to use TAB to move the
/// focus to the "And Me" button before trying it out).
///
/// This example defines its own key binding for the `X` key, but in this case,
/// there is also a default key binding for [ActivateAction] in the default key
/// bindings created by [WidgetsApp] (the parent for [MaterialApp], and
/// [CupertinoApp]), so the `ENTER` key will also activate the
control
.
/// [CupertinoApp]), so the `ENTER` key will also activate the
buttons
.
///
/// ```dart imports
/// import 'package:flutter/services.dart';
...
...
packages/flutter/lib/src/widgets/focus_manager.dart
View file @
1793108b
...
...
@@ -40,6 +40,9 @@ typedef FocusOnKeyCallback = bool Function(FocusNode node, RawKeyEvent event);
/// An attachment point for a [FocusNode].
///
/// Using a [FocusAttachment] is rarely needed, unless you are building
/// something akin to the [Focus] or [FocusScope] widgets from scratch.
///
/// Once created, a [FocusNode] must be attached to the widget tree by its
/// _host_ [StatefulWidget] via a [FocusAttachment] object. [FocusAttachment]s
/// are owned by the [StatefulWidget] that hosts a [FocusNode] or
...
...
@@ -134,7 +137,8 @@ class FocusAttachment {
///
/// _Please see the [Focus] and [FocusScope] widgets, which are utility widgets
/// that manage their own [FocusNode]s and [FocusScopeNode]s, respectively. If
/// they aren't appropriate, [FocusNode]s can be managed directly._
/// they aren't appropriate, [FocusNode]s can be managed directly, but doing
/// this yourself is rare._
///
/// [FocusNode]s are persistent objects that form a _focus tree_ that is a
/// representation of the widgets in the hierarchy that are interested in focus.
...
...
@@ -144,9 +148,9 @@ class FocusAttachment {
/// or if the [Focus] and [FocusScope] widgets provide insufficient control.
///
/// [FocusNodes] are organized into _scopes_ (see [FocusScopeNode]), which form
/// sub-trees of nodes that
can be traversed as a group. Within a scope, the
///
most recent nodes to have focus are remembered, and if a node is focused and
///
then remov
ed, the previous node receives focus again.
/// sub-trees of nodes that
restrict traversal to a group of nodes. Within a
///
scope, the most recent nodes to have focus are remembered, and if a node is
///
focused and then unfocus
ed, the previous node receives focus again.
///
/// The focus node hierarchy can be traversed using the [parent], [children],
/// [ancestors] and [descendants] accessors.
...
...
@@ -208,13 +212,13 @@ class FocusAttachment {
/// {@template flutter.widgets.focus_manager.focus.keyEvents}
/// ## Key Event Propagation
///
/// The [FocusManager] receives
all key events and will pass them to the focused
///
nodes. It starts with the node with the primary focus, and will call the
///
[onKey] callback for that node. If the callback returns false, indicating
///
that it did not handle the event, the [FocusManager] will move to the parent
///
of that node and call its [onKey]. If that [onKey] returns true, then it
///
will stop propagating the event. If it reaches the root [FocusScopeNode],
/// [FocusManager.rootScope], the event is discarded.
/// The [FocusManager] receives
key events from [RawKeyboard] and will pass them
///
to the focused nodes. It starts with the node with the primary focus, and
///
will call the [onKey] callback for that node. If the callback returns false,
///
indicating that it did not handle the event, the [FocusManager] will move to
///
the parent of that node and call its [onKey]. If that [onKey] returns true,
///
then it will stop propagating the event. If it reaches the root
/// [Focus
ScopeNode], [Focus
Manager.rootScope], the event is discarded.
/// {@endtemplate}
///
/// ## Focus Traversal
...
...
@@ -232,14 +236,16 @@ class FocusAttachment {
/// particular direction, is determined by the [FocusTraversalPolicy] in force.
///
/// The ambient policy is determined by looking up the widget hierarchy for a
/// [FocusTraversalGroup] widget, and obtaining the focus traversal policy
/// from it. Different focus nodes can inherit difference policies, so part of
/// the app can go in widget order, and part can go in reading order, depending
/// [FocusTraversalGroup] widget, and obtaining the focus traversal policy from
/// it. Different focus nodes can inherit difference policies, so part of the
/// app can go in a predefined order (using [OrderedTraversalPolicy]), and part
/// can go in reading order (using [ReadingOrderTraversalPolicy]), depending
/// upon the use case.
///
/// Predefined policies include [WidgetOrderTraversalPolicy],
/// [ReadingOrderTraversalPolicy], and [DirectionalFocusTraversalPolicyMixin],
/// but custom policies can be built based upon these policies.
/// [ReadingOrderTraversalPolicy], [OrderedTraversalPolicy], and
/// [DirectionalFocusTraversalPolicyMixin], but custom policies can be built
/// based upon these policies. See [FocusTraversalPolicy] for more information.
///
/// {@tool dartpad --template=stateless_widget_scaffold}
/// This example shows how a FocusNode should be managed if not using the
...
...
@@ -355,16 +361,12 @@ class FocusAttachment {
///
/// * [Focus], a widget that manages a [FocusNode] and provides access to
/// focus information and actions to its descendant widgets.
/// * [FocusScope], a widget that manages a [FocusScopeNode] and provides
/// access to scope information and actions to its descendant widgets.
/// * [FocusAttachment], a widget that connects a [FocusScopeNode] to the
/// widget tree.
/// * [FocusManager], a singleton that manages the focus and distributes key
/// events to focused nodes.
/// * [FocusTraversalPolicy], a class used to determine how to move the focus
/// to other nodes.
/// * [FocusTraversalGroup], a widget used to group together and configure the
/// focus traversal policy for a widget subtree.
/// * [FocusManager], a singleton that manages the primary focus and
/// distributes key events to focused nodes.
/// * [FocusTraversalPolicy], a class used to determine how to move the focus
/// to other nodes.
class
FocusNode
with
DiagnosticableTreeMixin
,
ChangeNotifier
{
/// Creates a focus node.
///
...
...
@@ -526,7 +528,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
///
/// Iterates the ancestors of this node starting at the parent and iterating
/// over successively more remote ancestors of this node, ending at the root
/// [FocusScope] ([FocusManager.rootScope]).
/// [FocusScope
Node
] ([FocusManager.rootScope]).
Iterable
<
FocusNode
>
get
ancestors
{
if
(
_ancestors
==
null
)
{
final
List
<
FocusNode
>
result
=
<
FocusNode
>[];
...
...
@@ -985,14 +987,10 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
///
/// * [Focus], a widget that manages a [FocusNode] and provides access to focus
/// information and actions to its descendant widgets.
/// * [FocusScope], a widget that manages a [FocusScopeNode] and provides
/// access to scope information and actions to its descendant widgets.
/// * [FocusAttachment], a widget that connects a [FocusScopeNode] to the focus
/// tree.
/// * [FocusManager], a singleton that manages the focus and distributes key
/// events to focused nodes.
/// * [FocusManager], a singleton that manages the primary focus and
/// distributes key events to focused nodes.
class
FocusScopeNode
extends
FocusNode
{
/// Creates a
FocusScope node
.
/// Creates a
[FocusScopeNode]
.
///
/// All parameters are optional.
FocusScopeNode
({
...
...
@@ -1092,9 +1090,9 @@ class FocusScopeNode extends FocusNode {
_markNextFocus
(
this
);
}
}
else
{
// We found a FocusScope
at the leaf, so ask it to focus itself instead o
f
//
this scope. That will cause this scope to return true from hasFocus,
// but false from hasPrimaryFocus.
// We found a FocusScope
Node at the leaf, so ask it to focus itsel
f
//
instead of this scope. That will cause this scope to return true from
//
hasFocus,
but false from hasPrimaryFocus.
primaryFocus
.
_doRequestFocus
();
}
}
...
...
@@ -1148,54 +1146,57 @@ enum FocusHighlightStrategy {
/// Manages the focus tree.
///
/// The focus tree
keeps track of which [FocusNode] is the user's curren
t
///
keyboard focus. The widget that owns the [FocusNode] often listens for
///
keyboard events
.
/// The focus tree
is a separate, sparser, tree from the widget tree tha
t
///
maintains the hierarchical relationship between focusable widgets in the
///
widget tree
.
///
/// The focus manager is responsible for holding the [FocusScopeNode] that is
/// the root of the focus tree and tracking which [FocusNode] has the overall
/// focus.
/// The focus manager is responsible for tracking which [FocusNode] has the
/// primary input focus (the [primaryFocus]), holding the [FocusScopeNode] that
/// is the root of the focus tree (the [rootScope]), and what the current
/// [highlightMode] is. It also distributes key events from [RawKeyboard] to the
/// nodes in the focus tree.
///
/// The
[FocusManager]
is held by the [WidgetsBinding] as
/// The
singleton [FocusManager] instance
is held by the [WidgetsBinding] as
/// [WidgetsBinding.focusManager], and can be conveniently accessed using the
/// [focusManager] global accessor.
///
/// To find the [FocusScopeNode] for a given [BuildContext], use
/// [FocusScope.of].
/// [FocusManager.instance] static accessor.
///
/// The [FocusManager] knows nothing about [FocusNode]s other than the one that
/// is currently focused (accessible via the [primaryFocus] global accessor). If
/// a [FocusScopeNode] is removed, then the [FocusManager] will attempt to focus
/// the next [FocusScopeNode] in the focus tree that it maintains, but if the
/// current focus in that [FocusScopeNode] is null, it will stop there, and no
/// [FocusNode] will have focus.
/// To find the [FocusNode] for a given [BuildContext], use [Focus.of]. To find
/// the [FocusScopeNode] for a given [BuildContext], use [FocusScope.of].
///
/// If you would like notification whenever the [primaryFocus] changes, register
/// a listener with [addListener]. When you no longer want to receive events, as
/// when your object is about to be disposed, you must unregister with
/// [removeListener] to avoid memory leaks. Removing listeners is typically done
/// in [State.dispose] on stateful widgets.
/// a listener with [addListener]. When you no longer want to receive these
/// events, as when your object is about to be disposed, you must unregister
/// with [removeListener] to avoid memory leaks. Removing listeners is typically
/// done in [State.dispose] on stateful widgets.
///
/// The [highlightMode] describes how focus highlights should be displayed on
/// components in the UI. The [highlightMode] changes are notified separately
/// via [addHighlightModeListener] and removed with
/// [removeHighlightModeListener]. The highlight mode changes when the user
/// switches from a mouse to a touch interface, or vice versa.
///
/// The widgets that are used to manage focus in the widget tree are:
///
/// The [highlightMode] changes are notified separately via
/// [addHighlightModeListener] and removed with [removeHighlightModeListener].
/// * [Focus], a widget that manages a [FocusNode] in the focus tree so that
/// the focus tree reflects changes in the widget hierarchy.
/// * [FocusScope], a widget that manages a [FocusScopeNode] in the focus tree,
/// creating a new scope for restricting focus to a set of focus nodes.
/// * [FocusTraversalGroup], a widget that groups together nodes that should be
/// traversed using an order described by a given [FocusTraversalPolicy].
///
/// See also:
///
/// * [FocusNode], which is a node in the focus tree that can receive focus.
/// * [FocusScopeNode], which is a node in the focus tree used to collect
/// subtrees into groups.
/// * [Focus.of], which provides the nearest ancestor [FocusNode] for a given
/// [BuildContext].
/// * [FocusScope.of], which provides the nearest ancestor [FocusScopeNode] for
/// a given [BuildContext].
/// * The [focusManager] and [primaryFocus] global accessors, for convenient
/// access from anywhere to the current focus manager state.
/// subtrees into groups and restrict focus to them.
/// * The [primaryFocus] global accessor, for convenient access from anywhere
/// to the current focus manager state.
class
FocusManager
with
DiagnosticableTreeMixin
,
ChangeNotifier
implements
Diagnosticable
{
/// Creates an object that manages the focus tree.
///
/// This constructor is rarely called directly. To access the [FocusManager],
/// consider using the [
focusManager] accessor instead (which gets it from the
/// [WidgetsBinding] singleton).
/// consider using the [
FocusManager.instance] accessor instead (which gets it
///
from the
[WidgetsBinding] singleton).
FocusManager
()
{
rootScope
.
_manager
=
this
;
RawKeyboard
.
instance
.
addListener
(
_handleRawKeyEvent
);
...
...
packages/flutter/lib/src/widgets/focus_scope.dart
View file @
1793108b
...
...
@@ -9,6 +9,8 @@ import 'focus_manager.dart';
import
'framework.dart'
;
import
'inherited_notifier.dart'
;
// TODO(gspencergoog): Add more information about unfocus here once https://github.com/flutter/flutter/pull/50831 lands.
/// A widget that manages a [FocusNode] to allow keyboard focus to be given
/// to this widget and its descendants.
///
...
...
@@ -33,10 +35,20 @@ import 'inherited_notifier.dart';
///
/// Managing a [FocusNode] means managing its lifecycle, listening for changes
/// in focus, and re-parenting it when needed to keep the focus hierarchy in
/// sync with the widget hierarchy. See [FocusNode] for more information about
/// the details of what node management entails if not using a [Focus] widget.
/// sync with the widget hierarchy. This widget does all of those things for
/// you. See [FocusNode] for more information about the details of what node
/// management entails if you are not using a [Focus] widget and you need to do
/// it yourself.
///
/// To collect a sub-tree of nodes into an exclusive group that restricts focus
/// traversal to the group, use a [FocusScope]. To collect a sub-tree of nodes
/// into a group that has a specific order to its traversal but allows the
/// traversal to escape the group, use a [FocusTraversalGroup].
///
/// To collect a sub-tree of nodes into a group, use a [FocusScope].
/// To move the focus, use methods on [FocusNode] by getting the [FocusNode]
/// through the [of] method. For instance, to move the focus to the next node in
/// the focus traversal order, call `Focus.of(context).nextFocus()`. To unfocus
/// a widget, call `Focus.of(context).unfocus()`.
///
/// {@tool dartpad --template=stateful_widget_scaffold}
/// This example shows how to manage focus using the [Focus] and [FocusScope]
...
...
@@ -118,6 +130,128 @@ import 'inherited_notifier.dart';
/// ```
/// {@end-tool}
///
/// {@tool dartpad --template=stateless_widget_material}
/// This example shows how to wrap another widget in a [Focus] widget to make it
/// focusable. It wraps a [Container], and changes the container's color when it
/// is set as the [FocusManager.primaryFocus].
///
/// If you also want to handle mouse hover and/or keyboard actions on a widget,
/// consider using a [FocusableActionDetector], which combines several different
/// widgets to provide those capabilities.
///
/// ```dart preamble
/// class FocusableText extends StatelessWidget {
/// const FocusableText(this.data, {Key key, this.autofocus}) : super(key: key);
///
/// /// The string to display as the text for this widget.
/// final String data;
///
/// /// Whether or not to focus this widget initially if nothing else is focused.
/// final bool autofocus;
///
/// @override
/// Widget build(BuildContext context) {
/// return Focus(
/// autofocus: autofocus,
/// child: Builder(builder: (BuildContext context) {
/// // The contents of this Builder are being made focusable. It is inside
/// // of a Builder because the builder provides the correct context
/// // variable for Focus.of() to be able to find the Focus widget that is
/// // the Builder's parent. Without the builder, the context variable used
/// // would be the one given the FocusableText build function, and that
/// // would start looking for a Focus widget ancestor of the FocusableText
/// // instead of finding the one inside of its build function.
/// return Container(
/// padding: EdgeInsets.all(8.0),
/// // Change the color based on whether or not this Container has focus.
/// color: Focus.of(context).hasPrimaryFocus ? Colors.black12 : null,
/// child: Text(data),
/// );
/// }),
/// );
/// }
/// }
/// ```
///
/// ```dart
/// Widget build(BuildContext context) {
/// return Scaffold(
/// body: ListView.builder(
/// itemBuilder: (context, index) => FocusableText(
/// 'Item $index',
/// autofocus: index == 0,
/// ),
/// itemCount: 50,
/// ),
/// );
/// }
/// ```
/// {@end-tool}
///
/// {@tool dartpad --template=stateful_widget_material}
/// This example shows how to focus a newly-created widget immediately after it
/// is created.
///
/// The focus node will not actually be given the focus until after the frame in
/// which it has requested focus is drawn, so it is OK to call
/// [FocusNode.requestFocus] on a node which is not yet in the focus tree.
///
/// ```dart
/// int focusedChild = 0;
/// List<Widget> children = <Widget>[];
/// List<FocusNode> childFocusNodes = <FocusNode>[];
///
/// @override
/// void initState() {
/// super.initState();
/// // Add the first child.
/// _addChild();
/// }
///
/// @override
/// void dispose() {
/// super.dispose();
/// childFocusNodes.forEach((FocusNode node) => node.dispose());
/// }
///
/// void _addChild() {
/// // Calling requestFocus here creates a deferred request for focus, since the
/// // node is not yet part of the focus tree.
/// childFocusNodes
/// .add(FocusNode(debugLabel: 'Child ${children.length}')..requestFocus());
///
/// children.add(Padding(
/// padding: const EdgeInsets.all(2.0),
/// child: ActionChip(
/// focusNode: childFocusNodes.last,
/// label: Text('CHILD ${children.length}'),
/// onPressed: () {},
/// ),
/// ));
/// }
///
/// @override
/// Widget build(BuildContext context) {
/// return Scaffold(
/// body: Center(
/// child: Wrap(
/// children: children,
/// ),
/// ),
/// floatingActionButton: FloatingActionButton(
/// onPressed: () {
/// setState(() {
/// focusedChild = children.length;
/// _addChild();
/// });
/// },
/// child: Icon(Icons.add),
/// ),
/// );
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [FocusNode], which represents a node in the focus hierarchy and
...
...
@@ -131,8 +265,8 @@ import 'inherited_notifier.dart';
/// distributes key events to focused nodes.
/// * [FocusTraversalPolicy], an object used to determine how to move the focus
/// to other nodes.
/// * [
DefaultFocusTraversal], a widget used to configure the default focus
/// traversal policy
for a widget subtree
.
/// * [
FocusTraversalGroup], a widget that groups together and imposes a
/// traversal policy
on the [Focus] nodes below it in the widget hierarchy
.
class
Focus
extends
StatefulWidget
{
/// Creates a widget that manages a [FocusNode].
///
...
...
@@ -225,7 +359,7 @@ class Focus extends StatefulWidget {
/// Sets the [FocusNode.skipTraversal] flag on the focus node so that it won't
/// be visited by the [FocusTraversalPolicy].
///
/// This is sometimes useful if a
Focus
widget should receive key events as
/// This is sometimes useful if a
[Focus]
widget should receive key events as
/// part of the focus chain, but shouldn't be accessible via focus traversal.
///
/// This is different from [canRequestFocus] because it only implies that the
...
...
@@ -276,6 +410,9 @@ class Focus extends StatefulWidget {
/// [nullOk].
///
/// The [context] and [nullOk] arguments must not be null.
///
/// Calling this function creates a dependency that will rebuild the given
/// context when the focus changes.
static
FocusNode
of
(
BuildContext
context
,
{
bool
nullOk
=
false
,
bool
scopeOk
=
false
})
{
assert
(
context
!=
null
);
assert
(
nullOk
!=
null
);
...
...
@@ -322,6 +459,9 @@ class Focus extends StatefulWidget {
/// Returns false if no [Focus] widget is found before reaching the nearest
/// [FocusScope], or if the root of the focus tree is reached without finding
/// a [Focus] widget.
///
/// Calling this function creates a dependency that will rebuild the given
/// context when the focus changes.
static
bool
isAt
(
BuildContext
context
)
=>
Focus
.
of
(
context
,
nullOk:
true
)?.
hasFocus
??
false
;
@override
...
...
@@ -487,8 +627,16 @@ class _FocusState extends State<Focus> {
}
}
/// A [FocusScope] is similar to a [Focus], but also serves as a scope for other
/// [Focus]s and [FocusScope]s, grouping them together.
/// A [FocusScope] is similar to a [Focus], but also serves as a scope for its
/// descendants, restricting focus traversal to the scoped controls.
///
/// For example a new [FocusScope] is created automatically when a route is
/// pushed, keeping the focus traversal from moving to a control in a previous
/// route.
///
/// If you just want to group widgets together in a group so that they are
/// traversed in a particular order, but the focus can still leave the group,
/// use a [FocusTraversalGroup].
///
/// Like [Focus], [FocusScope] provides an [onFocusChange] as a way to be
/// notified when the focus is given to or removed from this widget.
...
...
@@ -499,16 +647,12 @@ class _FocusState extends State<Focus> {
/// ancestors of that node, stopping if one of them returns true from [onKey],
/// indicating that it has handled the event.
///
/// A [FocusScope] manages a [FocusScopeNode]. Managing a [FocusScopeNode] means
/// managing its lifecycle, listening for changes in focus, and re-parenting it
/// when the widget hierarchy changes. See [FocusNode] and [FocusScopeNode] for
/// more information about the details of what node management entails if not
/// using a [FocusScope] widget.
///
/// A [DefaultTraversalPolicy] widget provides the [FocusTraversalPolicy] for
/// the [FocusScopeNode]s owned by its descendant widgets. Each [FocusScopeNode]
/// has [FocusNode] descendants. The traversal policy defines what "previous
/// focus", "next focus", and "move focus in this direction" means for them.
/// Managing a [FocusScopeNode] means managing its lifecycle, listening for
/// changes in focus, and re-parenting it when needed to keep the focus
/// hierarchy in sync with the widget hierarchy. This widget does all of those
/// things for you. See [FocusScopeNode] for more information about the details
/// of what node management entails if you are not using a [FocusScope] widget
/// and you need to do it yourself.
///
/// [FocusScopeNode]s remember the last [FocusNode] that was focused within
/// their descendants, and can move that focus to the next/previous node, or a
...
...
@@ -516,8 +660,153 @@ class _FocusState extends State<Focus> {
/// [FocusNode.previousFocus], or [FocusNode.focusInDirection] are called on a
/// [FocusNode] or [FocusScopeNode].
///
/// To move the focus, use methods on [FocusScopeNode]. For instance, to move
/// the focus to the next node, call `Focus.of(context).nextFocus()`.
/// To move the focus, use methods on [FocusNode] by getting the [FocusNode]
/// through the [of] method. For instance, to move the focus to the next node in
/// the focus traversal order, call `Focus.of(context).nextFocus()`. To unfocus
/// a widget, call `Focus.of(context).unfocus()`.
///
/// {@tool dartpad --template=stateful_widget_material}
/// This example demonstrates using a [FocusScope] to restrict focus to a particular
/// portion of the app. In this case, restricting focus to the visible part of a
/// Stack.
///
/// ```dart preamble
/// /// A demonstration pane.
/// ///
/// /// This is just a separate widget to simplify the example.
/// class Pane extends StatelessWidget {
/// const Pane({
/// Key key,
/// this.focusNode,
/// this.onPressed,
/// this.child,
/// this.backgroundColor,
/// this.icon,
/// }) : super(key: key);
///
/// final FocusNode focusNode;
/// final VoidCallback onPressed;
/// final Widget child;
/// final Color backgroundColor;
/// final Widget icon;
///
/// @override
/// Widget build(BuildContext context) {
/// return Material(
/// color: backgroundColor,
/// child: Stack(
/// fit: StackFit.expand,
/// children: <Widget>[
/// Center(
/// child: child,
/// ),
/// Align(
/// alignment: Alignment.topLeft,
/// child: IconButton(
/// autofocus: true,
/// focusNode: focusNode,
/// onPressed: onPressed,
/// icon: icon,
/// ),
/// ),
/// ],
/// ),
/// );
/// }
/// }
/// ```
///
/// ```dart
/// bool backdropIsVisible = false;
/// FocusNode backdropNode = FocusNode(debugLabel: 'Close Backdrop Button');
/// FocusNode foregroundNode = FocusNode(debugLabel: 'Option Button');
///
/// @override
/// void dispose() {
/// super.dispose();
/// backdropNode.dispose();
/// foregroundNode.dispose();
/// }
///
/// Widget _buildStack(BuildContext context, BoxConstraints constraints) {
/// Size stackSize = constraints.biggest;
/// return Stack(
/// fit: StackFit.expand,
/// // The backdrop is behind the front widget in the Stack, but the widgets
/// // would still be active and traversable without the FocusScope.
/// children: <Widget>[
/// // TRY THIS: Try removing this FocusScope entirely to see how it affects
/// // the behavior. Without this FocusScope, the "ANOTHER BUTTON TO FOCUS"
/// // button, and the IconButton in the backdrop Pane would be focusable
/// // even when the backdrop wasn't visible.
/// FocusScope(
/// // TRY THIS: Try commenting out this line. Notice that the focus
/// // starts on the backdrop and is stuck there? It seems like the app is
/// // non-responsive, but it actually isn't. This line makes sure that
/// // this focus scope and its children can't be focused when they're not
/// // visible. It might help to make the background color of the
/// // foreground pane semi-transparent to see it clearly.
/// canRequestFocus: backdropIsVisible,
/// child: Pane(
/// icon: Icon(Icons.close),
/// focusNode: backdropNode,
/// backgroundColor: Colors.lightBlue,
/// onPressed: () => setState(() => backdropIsVisible = false),
/// child: Column(
/// mainAxisAlignment: MainAxisAlignment.center,
/// children: <Widget>[
/// // This button would be not visible, but still focusable from
/// // the foreground pane without the FocusScope.
/// RaisedButton(
/// onPressed: () => print('You pressed the other button!'),
/// child: Text('ANOTHER BUTTON TO FOCUS'),
/// ),
/// DefaultTextStyle(
/// style: Theme.of(context).textTheme.headline2,
/// child: Text('BACKDROP')),
/// ],
/// ),
/// ),
/// ),
/// AnimatedPositioned(
/// curve: Curves.easeInOut,
/// duration: const Duration(milliseconds: 300),
/// top: backdropIsVisible ? stackSize.height * 0.9 : 0.0,
/// width: stackSize.width,
/// height: stackSize.height,
/// onEnd: () {
/// if (backdropIsVisible) {
/// backdropNode.requestFocus();
/// } else {
/// foregroundNode.requestFocus();
/// }
/// },
/// child: Pane(
/// icon: Icon(Icons.menu),
/// focusNode: foregroundNode,
/// // TRY THIS: Try changing this to Colors.green.withOpacity(0.8) to see for
/// // yourself that the hidden components do/don't get focus.
/// backgroundColor: Colors.green,
/// onPressed: backdropIsVisible
/// ? null
/// : () => setState(() => backdropIsVisible = true),
/// child: DefaultTextStyle(
/// style: Theme.of(context).textTheme.headline2,
/// child: Text('FOREGROUND')),
/// ),
/// ),
/// ],
/// );
/// }
///
/// @override
/// Widget build(BuildContext context) {
/// // Use a LayoutBuilder so that we can base the size of the stack on the size
/// // of its parent.
/// return LayoutBuilder(builder: _buildStack);
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
...
...
@@ -530,8 +819,8 @@ class _FocusState extends State<Focus> {
/// events to focused nodes.
/// * [FocusTraversalPolicy], an object used to determine how to move the focus
/// to other nodes.
/// * [
DefaultFocusTraversal], a widget used to configure the default focus
///
traversal
policy for a widget subtree.
/// * [
FocusTraversalGroup], a widget used to configure the focus traversal
/// policy for a widget subtree.
class
FocusScope
extends
Focus
{
/// Creates a widget that manages a [FocusScopeNode].
///
...
...
packages/flutter/lib/src/widgets/focus_traversal.dart
View file @
1793108b
...
...
@@ -951,8 +951,8 @@ class _ReadingOrderDirectionalGroupData extends Diagnosticable {
/// 3. Pick the closest to the beginning of the reading order from among the
/// nodes discovered above.
///
/// It uses the ambient [Directionality] in the context for the enclosing
scope
/// to determine which direction is "reading order".
/// It uses the ambient [Directionality] in the context for the enclosing
///
[FocusTraversalGroup]
to determine which direction is "reading order".
///
/// See also:
///
...
...
@@ -1296,11 +1296,14 @@ class _OrderedFocusInfo {
/// ),
/// );
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [WidgetOrderFocusTraversalPolicy], a policy that relies on the widget
/// * [FocusTraversalGroup], a widget that groups together and imposes a
/// traversal policy on the [Focus] nodes below it in the widget hierarchy.
/// * [WidgetOrderTraversalPolicy], a policy that relies on the widget
/// creation order to describe the order of traversal.
/// * [ReadingOrderTraversalPolicy], a policy that describes the order as the
/// natural "reading order" for the current [Directionality].
...
...
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