Unverified Commit 1793108b authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Add additional focus samples. (#50846)

parent 7a83c6fc
......@@ -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>
......
......@@ -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';
......
......@@ -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 removed, 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 unfocused, 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
/// [FocusScopeNode], [FocusManager.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]).
/// [FocusScopeNode] ([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 of
// this scope. That will cause this scope to return true from hasFocus,
// but false from hasPrimaryFocus.
// We found a FocusScopeNode at the leaf, so ask it to focus itself
// 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 current
/// 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 that
/// 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);
......
......@@ -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].
///
......
......@@ -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].
......
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