Revert "Add OrderedFocusTraversalPolicy and FocusTraversalGrou… (#50660)
This reverts commit 8ef5e2f046ed18ca32827e1a715ca5e405c646ac because it breaks some semantics tests.
This commit is contained in:
parent
7f819c935b
commit
c132c0faa9
@ -435,7 +435,7 @@ class _FocusDemoState extends State<FocusDemo> {
|
|||||||
kUndoActionKey: () => kUndoAction,
|
kUndoActionKey: () => kUndoAction,
|
||||||
kRedoActionKey: () => kRedoAction,
|
kRedoActionKey: () => kRedoAction,
|
||||||
},
|
},
|
||||||
child: FocusTraversalGroup(
|
child: DefaultFocusTraversal(
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
policy: ReadingOrderTraversalPolicy(),
|
||||||
child: Shortcuts(
|
child: Shortcuts(
|
||||||
shortcuts: <LogicalKeySet, Intent>{
|
shortcuts: <LogicalKeySet, Intent>{
|
||||||
|
@ -142,7 +142,7 @@ class _FocusDemoState extends State<FocusDemo> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final TextTheme textTheme = Theme.of(context).textTheme;
|
final TextTheme textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
return FocusTraversalGroup(
|
return DefaultFocusTraversal(
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
policy: ReadingOrderTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
debugLabel: 'Scope',
|
debugLabel: 'Scope',
|
||||||
|
@ -1395,7 +1395,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
|
|||||||
debugLabel: '<Default WidgetsApp Shortcuts>',
|
debugLabel: '<Default WidgetsApp Shortcuts>',
|
||||||
child: Actions(
|
child: Actions(
|
||||||
actions: widget.actions ?? WidgetsApp.defaultActions,
|
actions: widget.actions ?? WidgetsApp.defaultActions,
|
||||||
child: FocusTraversalGroup(
|
child: DefaultFocusTraversal(
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
policy: ReadingOrderTraversalPolicy(),
|
||||||
child: _MediaQueryFromWindow(
|
child: _MediaQueryFromWindow(
|
||||||
child: Localizations(
|
child: Localizations(
|
||||||
|
@ -230,12 +230,12 @@ class FocusAttachment {
|
|||||||
/// particular direction, is determined by the [FocusTraversalPolicy] in force.
|
/// particular direction, is determined by the [FocusTraversalPolicy] in force.
|
||||||
///
|
///
|
||||||
/// The ambient policy is determined by looking up the widget hierarchy for a
|
/// The ambient policy is determined by looking up the widget hierarchy for a
|
||||||
/// [FocusTraversalGroup] widget, and obtaining the focus traversal policy
|
/// [DefaultFocusTraversal] widget, and obtaining the focus traversal policy
|
||||||
/// from it. Different focus nodes can inherit difference policies, so part of
|
/// 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
|
/// the app can go in widget order, and part can go in reading order, depending
|
||||||
/// upon the use case.
|
/// upon the use case.
|
||||||
///
|
///
|
||||||
/// Predefined policies include [WidgetOrderTraversalPolicy],
|
/// Predefined policies include [WidgetOrderFocusTraversalPolicy],
|
||||||
/// [ReadingOrderTraversalPolicy], and [DirectionalFocusTraversalPolicyMixin],
|
/// [ReadingOrderTraversalPolicy], and [DirectionalFocusTraversalPolicyMixin],
|
||||||
/// but custom policies can be built based upon these policies.
|
/// but custom policies can be built based upon these policies.
|
||||||
///
|
///
|
||||||
@ -361,8 +361,8 @@ class FocusAttachment {
|
|||||||
/// events to focused nodes.
|
/// events to focused nodes.
|
||||||
/// * [FocusTraversalPolicy], a class used to determine how to move the focus
|
/// * [FocusTraversalPolicy], a class used to determine how to move the focus
|
||||||
/// to other nodes.
|
/// to other nodes.
|
||||||
/// * [FocusTraversalGroup], a widget used to group together and configure the
|
/// * [DefaultFocusTraversal], a widget used to configure the default focus
|
||||||
/// focus traversal policy for a widget subtree.
|
/// traversal policy for a widget subtree.
|
||||||
class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
|
class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
|
||||||
/// Creates a focus node.
|
/// Creates a focus node.
|
||||||
///
|
///
|
||||||
@ -426,8 +426,8 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
|
|||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [FocusTraversalGroup], a widget used to group together and configure the
|
/// * [DefaultFocusTraversal], a widget that sets the traversal policy for
|
||||||
/// focus traversal policy for a widget subtree.
|
/// its descendants.
|
||||||
/// * [FocusTraversalPolicy], a class that can be extended to describe a
|
/// * [FocusTraversalPolicy], a class that can be extended to describe a
|
||||||
/// traversal policy.
|
/// traversal policy.
|
||||||
bool get canRequestFocus {
|
bool get canRequestFocus {
|
||||||
@ -518,8 +518,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
|
|||||||
return _descendants;
|
return _descendants;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all descendants which do not have the [skipTraversal] and do have
|
/// Returns all descendants which do not have the [skipTraversal] flag set.
|
||||||
/// the [canRequestFocus] flag set.
|
|
||||||
Iterable<FocusNode> get traversalDescendants => descendants.where((FocusNode node) => !node.skipTraversal && node.canRequestFocus);
|
Iterable<FocusNode> get traversalDescendants => descendants.where((FocusNode node) => !node.skipTraversal && node.canRequestFocus);
|
||||||
|
|
||||||
/// An [Iterable] over the ancestors of this node.
|
/// An [Iterable] over the ancestors of this node.
|
||||||
@ -777,7 +776,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
|
|||||||
_manager?.primaryFocus?._setAsFocusedChild();
|
_manager?.primaryFocus?._setAsFocusedChild();
|
||||||
}
|
}
|
||||||
if (oldScope != null && child.context != null && child.enclosingScope != oldScope) {
|
if (oldScope != null && child.context != null && child.enclosingScope != oldScope) {
|
||||||
FocusTraversalGroup.of(child.context, nullOk: true)?.changedScope(node: child, oldScope: oldScope);
|
DefaultFocusTraversal.of(child.context, nullOk: true)?.changedScope(node: child, oldScope: oldScope);
|
||||||
}
|
}
|
||||||
if (child._requestFocusWhenReparented) {
|
if (child._requestFocusWhenReparented) {
|
||||||
child._doRequestFocus();
|
child._doRequestFocus();
|
||||||
@ -916,19 +915,19 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
|
|||||||
/// [FocusTraversalPolicy.next] method.
|
/// [FocusTraversalPolicy.next] method.
|
||||||
///
|
///
|
||||||
/// Returns true if it successfully found a node and requested focus.
|
/// Returns true if it successfully found a node and requested focus.
|
||||||
bool nextFocus() => FocusTraversalGroup.of(context).next(this);
|
bool nextFocus() => DefaultFocusTraversal.of(context).next(this);
|
||||||
|
|
||||||
/// Request to move the focus to the previous focus node, by calling the
|
/// Request to move the focus to the previous focus node, by calling the
|
||||||
/// [FocusTraversalPolicy.previous] method.
|
/// [FocusTraversalPolicy.previous] method.
|
||||||
///
|
///
|
||||||
/// Returns true if it successfully found a node and requested focus.
|
/// Returns true if it successfully found a node and requested focus.
|
||||||
bool previousFocus() => FocusTraversalGroup.of(context).previous(this);
|
bool previousFocus() => DefaultFocusTraversal.of(context).previous(this);
|
||||||
|
|
||||||
/// Request to move the focus to the nearest focus node in the given
|
/// Request to move the focus to the nearest focus node in the given
|
||||||
/// direction, by calling the [FocusTraversalPolicy.inDirection] method.
|
/// direction, by calling the [FocusTraversalPolicy.inDirection] method.
|
||||||
///
|
///
|
||||||
/// Returns true if it successfully found a node and requested focus.
|
/// Returns true if it successfully found a node and requested focus.
|
||||||
bool focusInDirection(TraversalDirection direction) => FocusTraversalGroup.of(context).inDirection(this, direction);
|
bool focusInDirection(TraversalDirection direction) => DefaultFocusTraversal.of(context).inDirection(this, direction);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
@ -462,10 +462,8 @@ class _FocusState extends State<Focus> {
|
|||||||
return _FocusMarker(
|
return _FocusMarker(
|
||||||
node: focusNode,
|
node: focusNode,
|
||||||
child: Semantics(
|
child: Semantics(
|
||||||
// If these values are false, then just don't set them, so they don't
|
focusable: _canRequestFocus,
|
||||||
// eclipse values set by children.
|
focused: _hasPrimaryFocus,
|
||||||
focusable: _canRequestFocus ? true : null,
|
|
||||||
focused: _hasPrimaryFocus ? true : null,
|
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2191,8 +2191,6 @@ abstract class BuildContext {
|
|||||||
/// Obtains the element corresponding to the nearest widget of the given type [T],
|
/// Obtains the element corresponding to the nearest widget of the given type [T],
|
||||||
/// which must be the type of a concrete [InheritedWidget] subclass.
|
/// which must be the type of a concrete [InheritedWidget] subclass.
|
||||||
///
|
///
|
||||||
/// Returns null if no such element is found.
|
|
||||||
///
|
|
||||||
/// Calling this method is O(1) with a small constant factor.
|
/// Calling this method is O(1) with a small constant factor.
|
||||||
///
|
///
|
||||||
/// This method does not establish a relationship with the target in the way
|
/// This method does not establish a relationship with the target in the way
|
||||||
|
@ -68,7 +68,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
expect(tester.getSemantics(find.byType(Focus)), matchesSemantics(
|
expect(tester.getSemantics(find.byType(Focus).last), matchesSemantics(
|
||||||
hasCheckedState: true,
|
hasCheckedState: true,
|
||||||
hasEnabledState: true,
|
hasEnabledState: true,
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
@ -83,7 +83,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
expect(tester.getSemantics(find.byType(Focus)), matchesSemantics(
|
expect(tester.getSemantics(find.byType(Focus).last), matchesSemantics(
|
||||||
hasCheckedState: true,
|
hasCheckedState: true,
|
||||||
hasEnabledState: true,
|
hasEnabledState: true,
|
||||||
isChecked: true,
|
isChecked: true,
|
||||||
@ -99,7 +99,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
expect(tester.getSemantics(find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_CheckboxRenderObjectWidget')), matchesSemantics(
|
expect(tester.getSemantics(find.byType(Focus).last), matchesSemantics(
|
||||||
hasCheckedState: true,
|
hasCheckedState: true,
|
||||||
hasEnabledState: true,
|
hasEnabledState: true,
|
||||||
));
|
));
|
||||||
@ -111,7 +111,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
expect(tester.getSemantics(find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_CheckboxRenderObjectWidget')), matchesSemantics(
|
expect(tester.getSemantics(find.byType(Focus).last), matchesSemantics(
|
||||||
hasCheckedState: true,
|
hasCheckedState: true,
|
||||||
hasEnabledState: true,
|
hasEnabledState: true,
|
||||||
isChecked: true,
|
isChecked: true,
|
||||||
|
@ -116,7 +116,7 @@ void main() {
|
|||||||
' The ancestors of this widget were:\n'
|
' The ancestors of this widget were:\n'
|
||||||
' Semantics\n'
|
' Semantics\n'
|
||||||
' Builder\n'
|
' Builder\n'
|
||||||
' RepaintBoundary-[GlobalKey#00000]\n'
|
' RepaintBoundary-[GlobalKey#2d465]\n'
|
||||||
' IgnorePointer\n'
|
' IgnorePointer\n'
|
||||||
' AnimatedBuilder\n'
|
' AnimatedBuilder\n'
|
||||||
' FadeTransition\n'
|
' FadeTransition\n'
|
||||||
@ -131,19 +131,19 @@ void main() {
|
|||||||
' PageStorage\n'
|
' PageStorage\n'
|
||||||
' Offstage\n'
|
' Offstage\n'
|
||||||
' _ModalScopeStatus\n'
|
' _ModalScopeStatus\n'
|
||||||
' _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#00000]\n'
|
' _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#969b7]\n'
|
||||||
' _EffectiveTickerMode\n'
|
' _EffectiveTickerMode\n'
|
||||||
' TickerMode\n'
|
' TickerMode\n'
|
||||||
' _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#00000]\n'
|
' _OverlayEntryWidget-[LabeledGlobalKey<_OverlayEntryWidgetState>#545d0]\n'
|
||||||
' _Theatre\n'
|
' _Theatre\n'
|
||||||
' Overlay-[LabeledGlobalKey<OverlayState>#00000]\n'
|
' Overlay-[LabeledGlobalKey<OverlayState>#31a52]\n'
|
||||||
' _FocusMarker\n'
|
' _FocusMarker\n'
|
||||||
' Semantics\n'
|
' Semantics\n'
|
||||||
' FocusScope\n'
|
' FocusScope\n'
|
||||||
' AbsorbPointer\n'
|
' AbsorbPointer\n'
|
||||||
' _PointerListener\n'
|
' _PointerListener\n'
|
||||||
' Listener\n'
|
' Listener\n'
|
||||||
' Navigator-[GlobalObjectKey<NavigatorState> _WidgetsAppState#00000]\n'
|
' Navigator-[GlobalObjectKey<NavigatorState> _WidgetsAppState#10579]\n'
|
||||||
' IconTheme\n'
|
' IconTheme\n'
|
||||||
' IconTheme\n'
|
' IconTheme\n'
|
||||||
' _InheritedCupertinoTheme\n'
|
' _InheritedCupertinoTheme\n'
|
||||||
@ -158,23 +158,19 @@ void main() {
|
|||||||
' CheckedModeBanner\n'
|
' CheckedModeBanner\n'
|
||||||
' Title\n'
|
' Title\n'
|
||||||
' Directionality\n'
|
' Directionality\n'
|
||||||
' _LocalizationsScope-[GlobalKey#00000]\n'
|
' _LocalizationsScope-[GlobalKey#a51e3]\n'
|
||||||
' Semantics\n'
|
' Semantics\n'
|
||||||
' Localizations\n'
|
' Localizations\n'
|
||||||
' MediaQuery\n'
|
' MediaQuery\n'
|
||||||
' _MediaQueryFromWindow\n'
|
' _MediaQueryFromWindow\n'
|
||||||
' Semantics\n'
|
' DefaultFocusTraversal\n'
|
||||||
' _FocusMarker\n'
|
|
||||||
' Focus\n'
|
|
||||||
' _FocusTraversalGroupMarker\n'
|
|
||||||
' FocusTraversalGroup\n'
|
|
||||||
' Actions\n'
|
' Actions\n'
|
||||||
' _ShortcutsMarker\n'
|
' _ShortcutsMarker\n'
|
||||||
' Semantics\n'
|
' Semantics\n'
|
||||||
' _FocusMarker\n'
|
' _FocusMarker\n'
|
||||||
' Focus\n'
|
' Focus\n'
|
||||||
' Shortcuts\n'
|
' Shortcuts\n'
|
||||||
' WidgetsApp-[GlobalObjectKey _MaterialAppState#00000]\n'
|
' WidgetsApp-[GlobalObjectKey _MaterialAppState#38e79]\n'
|
||||||
' ScrollConfiguration\n'
|
' ScrollConfiguration\n'
|
||||||
' MaterialApp\n'
|
' MaterialApp\n'
|
||||||
' [root]\n'
|
' [root]\n'
|
||||||
|
@ -570,7 +570,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget wrap({ Widget child }) {
|
Widget wrap({ Widget child }) {
|
||||||
return FocusTraversalGroup(
|
return DefaultFocusTraversal(
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
policy: ReadingOrderTraversalPolicy(),
|
||||||
child: Directionality(
|
child: Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -598,7 +598,7 @@ void main() {
|
|||||||
// This checks both FocusScopes that have their own nodes, as well as those
|
// This checks both FocusScopes that have their own nodes, as well as those
|
||||||
// that use external nodes.
|
// that use external nodes.
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
DefaultFocusTraversal(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -661,7 +661,7 @@ void main() {
|
|||||||
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Parent Scope 2');
|
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Parent Scope 2');
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
DefaultFocusTraversal(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -711,7 +711,7 @@ void main() {
|
|||||||
expect(find.text('b'), findsOneWidget);
|
expect(find.text('b'), findsOneWidget);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
DefaultFocusTraversal(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -746,7 +746,7 @@ void main() {
|
|||||||
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Parent Scope 2');
|
final FocusScopeNode parentFocusScope2 = FocusScopeNode(debugLabel: 'Parent Scope 2');
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
DefaultFocusTraversal(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FocusScope(
|
FocusScope(
|
||||||
@ -794,7 +794,7 @@ void main() {
|
|||||||
expect(find.text('b'), findsOneWidget);
|
expect(find.text('b'), findsOneWidget);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
DefaultFocusTraversal(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FocusScope(
|
FocusScope(
|
||||||
|
@ -12,15 +12,15 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group(WidgetOrderTraversalPolicy, () {
|
group(WidgetOrderFocusTraversalPolicy, () {
|
||||||
testWidgets('Find the initial focus if there is none yet.', (WidgetTester tester) async {
|
testWidgets('Find the initial focus if there is none yet.', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
final GlobalKey key4 = GlobalKey(debugLabel: '4');
|
final GlobalKey key4 = GlobalKey(debugLabel: '4');
|
||||||
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
||||||
await tester.pumpWidget(FocusTraversalGroup(
|
await tester.pumpWidget(DefaultFocusTraversal(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderFocusTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
key: key1,
|
key: key1,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -64,8 +64,8 @@ void main() {
|
|||||||
bool focus3;
|
bool focus3;
|
||||||
bool focus5;
|
bool focus5;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
DefaultFocusTraversal(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderFocusTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
debugLabel: 'key1',
|
debugLabel: 'key1',
|
||||||
key: key1,
|
key: key1,
|
||||||
@ -177,8 +177,8 @@ void main() {
|
|||||||
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
||||||
final GlobalKey key6 = GlobalKey(debugLabel: '6');
|
final GlobalKey key6 = GlobalKey(debugLabel: '6');
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
DefaultFocusTraversal(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderFocusTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
key: key1,
|
key: key1,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -250,8 +250,8 @@ void main() {
|
|||||||
final FocusNode testNode2 = FocusNode(debugLabel: 'Second Focus Node');
|
final FocusNode testNode2 = FocusNode(debugLabel: 'Second Focus Node');
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: FocusTraversalGroup(
|
home: DefaultFocusTraversal(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderFocusTraversalPolicy(),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Builder(builder: (BuildContext context) {
|
child: Builder(builder: (BuildContext context) {
|
||||||
return MaterialButton(
|
return MaterialButton(
|
||||||
@ -317,7 +317,7 @@ void main() {
|
|||||||
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
final GlobalKey key3 = GlobalKey(debugLabel: '3');
|
||||||
final GlobalKey key4 = GlobalKey(debugLabel: '4');
|
final GlobalKey key4 = GlobalKey(debugLabel: '4');
|
||||||
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
||||||
await tester.pumpWidget(FocusTraversalGroup(
|
await tester.pumpWidget(DefaultFocusTraversal(
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
policy: ReadingOrderTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
key: key1,
|
key: key1,
|
||||||
@ -364,7 +364,7 @@ void main() {
|
|||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: FocusTraversalGroup(
|
child: DefaultFocusTraversal(
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
policy: ReadingOrderTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
debugLabel: 'key1',
|
debugLabel: 'key1',
|
||||||
@ -473,7 +473,7 @@ void main() {
|
|||||||
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
final GlobalKey key5 = GlobalKey(debugLabel: '5');
|
||||||
final GlobalKey key6 = GlobalKey(debugLabel: '6');
|
final GlobalKey key6 = GlobalKey(debugLabel: '6');
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
FocusTraversalGroup(
|
DefaultFocusTraversal(
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
policy: ReadingOrderTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
key: key1,
|
key: key1,
|
||||||
@ -538,579 +538,6 @@ void main() {
|
|||||||
expect(secondFocusNode.hasFocus, isFalse);
|
expect(secondFocusNode.hasFocus, isFalse);
|
||||||
expect(scope.hasFocus, isTrue);
|
expect(scope.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Focus order is correct in the presence of different directionalities.', (WidgetTester tester) async {
|
|
||||||
const int nodeCount = 10;
|
|
||||||
final FocusScopeNode scopeNode = FocusScopeNode();
|
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
|
||||||
Widget buildTest(TextDirection topDirection) {
|
|
||||||
return Directionality(
|
|
||||||
textDirection: topDirection,
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
|
||||||
child: FocusScope(
|
|
||||||
node: scopeNode,
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: Row(children: <Widget>[
|
|
||||||
Focus(
|
|
||||||
focusNode: nodes[0],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
Focus(
|
|
||||||
focusNode: nodes[1],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
Focus(
|
|
||||||
focusNode: nodes[2],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: Row(children: <Widget>[
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.rtl,
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[3],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.rtl,
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[4],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[5],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
Row(children: <Widget>[
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[6],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.rtl,
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[7],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.rtl,
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[8],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[9],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await tester.pumpWidget(buildTest(TextDirection.rtl));
|
|
||||||
|
|
||||||
// The last four *are* correct: the Row is sensitive to the directionality
|
|
||||||
// too, so it swaps the positions of 7 and 8.
|
|
||||||
final List<int> order = <int>[];
|
|
||||||
for (int i = 0; i < nodeCount; ++i) {
|
|
||||||
nodes.first.nextFocus();
|
|
||||||
await tester.pump();
|
|
||||||
order.add(nodes.indexOf(primaryFocus));
|
|
||||||
}
|
|
||||||
expect(order, orderedEquals(<int>[0, 1, 2, 4, 3, 5, 6, 7, 8, 9]));
|
|
||||||
|
|
||||||
await tester.pumpWidget(buildTest(TextDirection.ltr));
|
|
||||||
|
|
||||||
order.clear();
|
|
||||||
for (int i = 0; i < nodeCount; ++i) {
|
|
||||||
nodes.first.nextFocus();
|
|
||||||
await tester.pump();
|
|
||||||
order.add(nodes.indexOf(primaryFocus));
|
|
||||||
}
|
|
||||||
expect(order, orderedEquals(<int>[0, 1, 2, 4, 3, 5, 6, 8, 7, 9]));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Focus order is reading order regardless of widget order, even when overlapping.', (WidgetTester tester) async {
|
|
||||||
const int nodeCount = 10;
|
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
|
||||||
await tester.pumpWidget(
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.rtl,
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
|
||||||
child: Stack(
|
|
||||||
alignment: const Alignment(-1, -1),
|
|
||||||
children: List<Widget>.generate(nodeCount, (int index) {
|
|
||||||
// Boxes that all have the same upper left origin corner.
|
|
||||||
return Focus(
|
|
||||||
focusNode: nodes[index],
|
|
||||||
child: Container(width: 10.0 * (index + 1), height: 10.0 * (index + 1)),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final List<int> order = <int>[];
|
|
||||||
for (int i = 0; i < nodeCount; ++i) {
|
|
||||||
nodes.first.nextFocus();
|
|
||||||
await tester.pump();
|
|
||||||
order.add(nodes.indexOf(primaryFocus));
|
|
||||||
}
|
|
||||||
expect(order, orderedEquals(<int>[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]));
|
|
||||||
|
|
||||||
// Concentric boxes.
|
|
||||||
await tester.pumpWidget(
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.rtl,
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
|
||||||
child: Stack(
|
|
||||||
alignment: const Alignment(0, 0),
|
|
||||||
children: List<Widget>.generate(nodeCount, (int index) {
|
|
||||||
return Focus(
|
|
||||||
focusNode: nodes[index],
|
|
||||||
child: Container(width: 10.0 * (index + 1), height: 10.0 * (index + 1)),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
order.clear();
|
|
||||||
for (int i = 0; i < nodeCount; ++i) {
|
|
||||||
nodes.first.nextFocus();
|
|
||||||
await tester.pump();
|
|
||||||
order.add(nodes.indexOf(primaryFocus));
|
|
||||||
}
|
|
||||||
expect(order, orderedEquals(<int>[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]));
|
|
||||||
|
|
||||||
// Stacked (vertically) and centered (horizontally, on each other)
|
|
||||||
// widgets, not overlapping.
|
|
||||||
await tester.pumpWidget(
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.rtl,
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: ReadingOrderTraversalPolicy(),
|
|
||||||
child: Stack(
|
|
||||||
alignment: const Alignment(0, 0),
|
|
||||||
children: List<Widget>.generate(nodeCount, (int index) {
|
|
||||||
return Positioned(
|
|
||||||
top: 5.0 * index * (index + 1),
|
|
||||||
left: 5.0 * (9 - index),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[index],
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(border: Border.all()),
|
|
||||||
width: 10.0 * (index + 1),
|
|
||||||
height: 10.0 * (index + 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
order.clear();
|
|
||||||
for (int i = 0; i < nodeCount; ++i) {
|
|
||||||
nodes.first.nextFocus();
|
|
||||||
await tester.pump();
|
|
||||||
order.add(nodes.indexOf(primaryFocus));
|
|
||||||
}
|
|
||||||
expect(order, orderedEquals(<int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group(OrderedTraversalPolicy, () {
|
|
||||||
testWidgets('Find the initial focus if there is none yet.', (WidgetTester tester) async {
|
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
|
||||||
await tester.pumpWidget(FocusTraversalGroup(
|
|
||||||
policy: OrderedTraversalPolicy(secondary: ReadingOrderTraversalPolicy()),
|
|
||||||
child: FocusScope(
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(2),
|
|
||||||
child: Focus(
|
|
||||||
child: Container(key: key1, width: 100, height: 100),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(1),
|
|
||||||
child: Focus(
|
|
||||||
child: Container(key: key2, width: 100, height: 100),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
final Element firstChild = tester.element(find.byKey(key1));
|
|
||||||
final Element secondChild = tester.element(find.byKey(key2));
|
|
||||||
final FocusNode firstFocusNode = Focus.of(firstChild);
|
|
||||||
final FocusNode secondFocusNode = Focus.of(secondChild);
|
|
||||||
final FocusNode scope = Focus.of(firstChild).enclosingScope;
|
|
||||||
secondFocusNode.nextFocus();
|
|
||||||
|
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
expect(firstFocusNode.hasFocus, isFalse);
|
|
||||||
expect(secondFocusNode.hasFocus, isTrue);
|
|
||||||
expect(scope.hasFocus, isTrue);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Fall back to the secondary sort if no FocusTraversalOrder exists.', (WidgetTester tester) async {
|
|
||||||
const int nodeCount = 10;
|
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
|
||||||
await tester.pumpWidget(
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.rtl,
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: OrderedTraversalPolicy(secondary: WidgetOrderTraversalPolicy()),
|
|
||||||
child: FocusScope(
|
|
||||||
child: Row(
|
|
||||||
children: List<Widget>.generate(
|
|
||||||
nodeCount,
|
|
||||||
(int index) => Focus(
|
|
||||||
focusNode: nodes[index],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Because it should be using widget order, this shouldn't be affected by
|
|
||||||
// the directionality.
|
|
||||||
for (int i = 0; i < nodeCount; ++i) {
|
|
||||||
nodes.first.nextFocus();
|
|
||||||
await tester.pump();
|
|
||||||
expect(nodes[i].hasPrimaryFocus, isTrue, reason: "node $i doesn't have focus, but should");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now check backwards.
|
|
||||||
for (int i = nodeCount - 1; i > 0; --i) {
|
|
||||||
nodes.first.previousFocus();
|
|
||||||
await tester.pump();
|
|
||||||
expect(nodes[i - 1].hasPrimaryFocus, isTrue, reason: "node ${i - 1} doesn't have focus, but should");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Move focus to next/previous node using numerical order.', (WidgetTester tester) async {
|
|
||||||
const int nodeCount = 10;
|
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
|
||||||
await tester.pumpWidget(
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: OrderedTraversalPolicy(secondary: WidgetOrderTraversalPolicy()),
|
|
||||||
child: FocusScope(
|
|
||||||
child: Row(
|
|
||||||
children: List<Widget>.generate(
|
|
||||||
nodeCount,
|
|
||||||
(int index) => FocusTraversalOrder(
|
|
||||||
order: NumericFocusOrder(nodeCount - index.toDouble()),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[index],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// The orders are assigned to be backwards from normal, so should go backwards.
|
|
||||||
for (int i = nodeCount - 1; i >= 0; --i) {
|
|
||||||
nodes.first.nextFocus();
|
|
||||||
await tester.pump();
|
|
||||||
expect(nodes[i].hasPrimaryFocus, isTrue, reason: "node $i doesn't have focus, but should");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now check backwards.
|
|
||||||
for (int i = 1; i < nodeCount; ++i) {
|
|
||||||
nodes.first.previousFocus();
|
|
||||||
await tester.pump();
|
|
||||||
expect(nodes[i].hasPrimaryFocus, isTrue, reason: "node $i doesn't have focus, but should");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Move focus to next/previous node using lexical order.', (WidgetTester tester) async {
|
|
||||||
const int nodeCount = 10;
|
|
||||||
|
|
||||||
/// Generate ['J' ... 'A'];
|
|
||||||
final List<String> keys = List<String>.generate(nodeCount, (int index) => String.fromCharCode('A'.codeUnits[0] + nodeCount - index - 1));
|
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node ${keys[index]}'));
|
|
||||||
await tester.pumpWidget(
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: OrderedTraversalPolicy(secondary: WidgetOrderTraversalPolicy()),
|
|
||||||
child: FocusScope(
|
|
||||||
child: Row(
|
|
||||||
children: List<Widget>.generate(
|
|
||||||
nodeCount,
|
|
||||||
(int index) => FocusTraversalOrder(
|
|
||||||
order: LexicalFocusOrder(keys[index]),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[index],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// The orders are assigned to be backwards from normal, so should go backwards.
|
|
||||||
for (int i = nodeCount - 1; i >= 0; --i) {
|
|
||||||
nodes.first.nextFocus();
|
|
||||||
await tester.pump();
|
|
||||||
expect(nodes[i].hasPrimaryFocus, isTrue, reason: "node $i doesn't have focus, but should");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now check backwards.
|
|
||||||
for (int i = 1; i < nodeCount; ++i) {
|
|
||||||
nodes.first.previousFocus();
|
|
||||||
await tester.pump();
|
|
||||||
expect(nodes[i].hasPrimaryFocus, isTrue, reason: "node $i doesn't have focus, but should");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Focus order is correct in the presence of FocusTraversalPolicyGroups.', (WidgetTester tester) async {
|
|
||||||
const int nodeCount = 10;
|
|
||||||
final FocusScopeNode scopeNode = FocusScopeNode();
|
|
||||||
final List<FocusNode> nodes = List<FocusNode>.generate(nodeCount, (int index) => FocusNode(debugLabel: 'Node $index'));
|
|
||||||
await tester.pumpWidget(
|
|
||||||
Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
|
||||||
child: FocusScope(
|
|
||||||
node: scopeNode,
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: OrderedTraversalPolicy(secondary: WidgetOrderTraversalPolicy()),
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(0),
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
|
||||||
child: Row(children: <Widget>[
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(9),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[9],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(8),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[8],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(7),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[7],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(1),
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: OrderedTraversalPolicy(secondary: WidgetOrderTraversalPolicy()),
|
|
||||||
child: Row(children: <Widget>[
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(4),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[4],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(5),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[5],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(6),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[6],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(2),
|
|
||||||
child: FocusTraversalGroup(
|
|
||||||
policy: OrderedTraversalPolicy(secondary: WidgetOrderTraversalPolicy()),
|
|
||||||
child: Row(children: <Widget>[
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const LexicalFocusOrder('D'),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[3],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const LexicalFocusOrder('C'),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[2],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const LexicalFocusOrder('B'),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[1],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FocusTraversalOrder(
|
|
||||||
order: const LexicalFocusOrder('A'),
|
|
||||||
child: Focus(
|
|
||||||
focusNode: nodes[0],
|
|
||||||
child: Container(width: 10, height: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final List<int> expectedOrder = <int>[9, 8, 7, 4, 5, 6, 0, 1, 2, 3];
|
|
||||||
final List<int> order = <int>[];
|
|
||||||
for (int i = 0; i < nodeCount; ++i) {
|
|
||||||
nodes.first.nextFocus();
|
|
||||||
await tester.pump();
|
|
||||||
order.add(nodes.indexOf(primaryFocus));
|
|
||||||
}
|
|
||||||
expect(order, orderedEquals(expectedOrder));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Find the initial focus when a route is pushed or popped.', (WidgetTester tester) async {
|
|
||||||
final GlobalKey key1 = GlobalKey(debugLabel: '1');
|
|
||||||
final GlobalKey key2 = GlobalKey(debugLabel: '2');
|
|
||||||
final FocusNode testNode1 = FocusNode(debugLabel: 'First Focus Node');
|
|
||||||
final FocusNode testNode2 = FocusNode(debugLabel: 'Second Focus Node');
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: FocusTraversalGroup(
|
|
||||||
policy: OrderedTraversalPolicy(secondary: WidgetOrderTraversalPolicy()),
|
|
||||||
child: Center(
|
|
||||||
child: Builder(builder: (BuildContext context) {
|
|
||||||
return FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(0),
|
|
||||||
child: MaterialButton(
|
|
||||||
key: key1,
|
|
||||||
focusNode: testNode1,
|
|
||||||
autofocus: true,
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).push<void>(
|
|
||||||
MaterialPageRoute<void>(
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return Center(
|
|
||||||
child: FocusTraversalOrder(
|
|
||||||
order: const NumericFocusOrder(0),
|
|
||||||
child: MaterialButton(
|
|
||||||
key: key2,
|
|
||||||
focusNode: testNode2,
|
|
||||||
autofocus: true,
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: const Text('Go Back'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const Text('Go Forward'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final Element firstChild = tester.element(find.text('Go Forward'));
|
|
||||||
final FocusNode firstFocusNode = Focus.of(firstChild);
|
|
||||||
final FocusNode scope = Focus.of(firstChild).enclosingScope;
|
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
expect(firstFocusNode.hasFocus, isTrue);
|
|
||||||
expect(scope.hasFocus, isTrue);
|
|
||||||
|
|
||||||
await tester.tap(find.text('Go Forward'));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
final Element secondChild = tester.element(find.text('Go Back'));
|
|
||||||
final FocusNode secondFocusNode = Focus.of(secondChild);
|
|
||||||
|
|
||||||
expect(firstFocusNode.hasFocus, isFalse);
|
|
||||||
expect(secondFocusNode.hasFocus, isTrue);
|
|
||||||
|
|
||||||
await tester.tap(find.text('Go Back'));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(firstFocusNode.hasFocus, isTrue);
|
|
||||||
expect(scope.hasFocus, isTrue);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
group(DirectionalFocusTraversalPolicyMixin, () {
|
group(DirectionalFocusTraversalPolicyMixin, () {
|
||||||
@ -1126,8 +553,8 @@ void main() {
|
|||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: FocusTraversalGroup(
|
child: DefaultFocusTraversal(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderFocusTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
debugLabel: 'Scope',
|
debugLabel: 'Scope',
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -1279,8 +706,8 @@ void main() {
|
|||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: FocusTraversalGroup(
|
child: DefaultFocusTraversal(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderFocusTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
debugLabel: 'Scope',
|
debugLabel: 'Scope',
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -1400,8 +827,8 @@ void main() {
|
|||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: FocusTraversalGroup(
|
child: DefaultFocusTraversal(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderFocusTraversalPolicy(),
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
debugLabel: 'scope',
|
debugLabel: 'scope',
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -1444,7 +871,7 @@ void main() {
|
|||||||
|
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
final FocusTraversalPolicy policy = FocusTraversalGroup.of(upperLeftKey.currentContext);
|
final FocusTraversalPolicy policy = DefaultFocusTraversal.of(upperLeftKey.currentContext);
|
||||||
|
|
||||||
expect(policy.findFirstFocusInDirection(scope, TraversalDirection.up), equals(lowerLeftNode));
|
expect(policy.findFirstFocusInDirection(scope, TraversalDirection.up), equals(lowerLeftNode));
|
||||||
expect(policy.findFirstFocusInDirection(scope, TraversalDirection.down), equals(upperLeftNode));
|
expect(policy.findFirstFocusInDirection(scope, TraversalDirection.down), equals(upperLeftNode));
|
||||||
@ -1458,7 +885,7 @@ void main() {
|
|||||||
final FocusNode focusBottom = FocusNode(debugLabel: 'bottom');
|
final FocusNode focusBottom = FocusNode(debugLabel: 'bottom');
|
||||||
|
|
||||||
final FocusTraversalPolicy policy = ReadingOrderTraversalPolicy();
|
final FocusTraversalPolicy policy = ReadingOrderTraversalPolicy();
|
||||||
await tester.pumpWidget(FocusTraversalGroup(
|
await tester.pumpWidget(DefaultFocusTraversal(
|
||||||
policy: policy,
|
policy: policy,
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
debugLabel: 'Scope',
|
debugLabel: 'Scope',
|
||||||
@ -1482,7 +909,7 @@ void main() {
|
|||||||
expect(focusBottom.hasFocus, isTrue);
|
expect(focusBottom.hasFocus, isTrue);
|
||||||
|
|
||||||
// Remove center focus node.
|
// Remove center focus node.
|
||||||
await tester.pumpWidget(FocusTraversalGroup(
|
await tester.pumpWidget(DefaultFocusTraversal(
|
||||||
policy: policy,
|
policy: policy,
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
debugLabel: 'Scope',
|
debugLabel: 'Scope',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user