Unverified Commit 590cc27b authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Revert "Implement focus traversal for desktop platforms, shoehorn edition. (#30040)" (#31461)

This reverts commit 4218c0bc.
parent 096439b4
...@@ -62,7 +62,7 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> { ...@@ -62,7 +62,7 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
if (!_focusNode.hasFocus) { if (!_focusNode.hasFocus) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
_focusNode.requestFocus(); FocusScope.of(context).requestFocus(_focusNode);
}, },
child: Text('Tap to focus', style: textTheme.display1), child: Text('Tap to focus', style: textTheme.display1),
); );
......
...@@ -17,7 +17,7 @@ class MyApp extends StatelessWidget { ...@@ -17,7 +17,7 @@ class MyApp extends StatelessWidget {
return MaterialApp( return MaterialApp(
title: _title, title: _title,
home: Scaffold( home: Scaffold(
appBar: AppBar(title: const Text(_title)), appBar: AppBar(title: Text(_title)),
body: MyStatefulWidget(), body: MyStatefulWidget(),
), ),
); );
......
...@@ -17,7 +17,7 @@ class MyApp extends StatelessWidget { ...@@ -17,7 +17,7 @@ class MyApp extends StatelessWidget {
return MaterialApp( return MaterialApp(
title: _title, title: _title,
home: Scaffold( home: Scaffold(
appBar: AppBar(title: const Text(_title)), appBar: AppBar(title: Text(_title)),
body: MyStatelessWidget(), body: MyStatelessWidget(),
), ),
); );
......
...@@ -90,7 +90,7 @@ import 'package:flutter/foundation.dart'; ...@@ -90,7 +90,7 @@ import 'package:flutter/foundation.dart';
/// onTap: () { /// onTap: () {
/// FocusScope.of(context).requestFocus(_focusNode); /// FocusScope.of(context).requestFocus(_focusNode);
/// }, /// },
/// child: const Text('Tap to focus'), /// child: Text('Tap to focus'),
/// ); /// );
/// } /// }
/// return Text(_message ?? 'Press a key'); /// return Text(_message ?? 'Press a key');
......
...@@ -304,7 +304,7 @@ class _TabSwitchingViewState extends State<_TabSwitchingView> { ...@@ -304,7 +304,7 @@ class _TabSwitchingViewState extends State<_TabSwitchingView> {
tabs = List<Widget>(widget.tabNumber); tabs = List<Widget>(widget.tabNumber);
tabFocusNodes = List<FocusScopeNode>.generate( tabFocusNodes = List<FocusScopeNode>.generate(
widget.tabNumber, widget.tabNumber,
(int index) => FocusScopeNode(debugLabel: 'Tab Focus Scope $index'), (int index) => FocusScopeNode(),
); );
} }
...@@ -327,7 +327,7 @@ class _TabSwitchingViewState extends State<_TabSwitchingView> { ...@@ -327,7 +327,7 @@ class _TabSwitchingViewState extends State<_TabSwitchingView> {
@override @override
void dispose() { void dispose() {
for (FocusScopeNode focusScopeNode in tabFocusNodes) { for (FocusScopeNode focusScopeNode in tabFocusNodes) {
focusScopeNode.dispose(); focusScopeNode.detach();
} }
super.dispose(); super.dispose();
} }
......
...@@ -191,7 +191,7 @@ abstract class SearchDelegate<T> { ...@@ -191,7 +191,7 @@ abstract class SearchDelegate<T> {
/// ///
/// * [showSuggestions] to show the search suggestions again. /// * [showSuggestions] to show the search suggestions again.
void showResults(BuildContext context) { void showResults(BuildContext context) {
_focusNode?.unfocus(); _focusNode.unfocus();
_currentBody = _SearchBody.results; _currentBody = _SearchBody.results;
} }
...@@ -208,8 +208,7 @@ abstract class SearchDelegate<T> { ...@@ -208,8 +208,7 @@ abstract class SearchDelegate<T> {
/// ///
/// * [showResults] to show the search results. /// * [showResults] to show the search results.
void showSuggestions(BuildContext context) { void showSuggestions(BuildContext context) {
assert(_focusNode != null, '_focusNode must be set by route before showSuggestions is called.'); FocusScope.of(context).requestFocus(_focusNode);
_focusNode.requestFocus();
_currentBody = _SearchBody.suggestions; _currentBody = _SearchBody.suggestions;
} }
...@@ -219,7 +218,7 @@ abstract class SearchDelegate<T> { ...@@ -219,7 +218,7 @@ abstract class SearchDelegate<T> {
/// to [showSearch] that launched the search initially. /// to [showSearch] that launched the search initially.
void close(BuildContext context, T result) { void close(BuildContext context, T result) {
_currentBody = null; _currentBody = null;
_focusNode?.unfocus(); _focusNode.unfocus();
Navigator.of(context) Navigator.of(context)
..popUntil((Route<dynamic> route) => route == _route) ..popUntil((Route<dynamic> route) => route == _route)
..pop(result); ..pop(result);
...@@ -233,9 +232,7 @@ abstract class SearchDelegate<T> { ...@@ -233,9 +232,7 @@ abstract class SearchDelegate<T> {
/// page. /// page.
Animation<double> get transitionAnimation => _proxyAnimation; Animation<double> get transitionAnimation => _proxyAnimation;
// The focus node to use for manipulating focus on the search page. This is final FocusNode _focusNode = FocusNode();
// managed, owned, and set by the _SearchPageRoute using this delegate.
FocusNode _focusNode;
final TextEditingController _queryTextController = TextEditingController(); final TextEditingController _queryTextController = TextEditingController();
...@@ -249,6 +246,7 @@ abstract class SearchDelegate<T> { ...@@ -249,6 +246,7 @@ abstract class SearchDelegate<T> {
} }
_SearchPageRoute<T> _route; _SearchPageRoute<T> _route;
} }
/// Describes the body that is currently shown under the [AppBar] in the /// Describes the body that is currently shown under the [AppBar] in the
...@@ -348,18 +346,13 @@ class _SearchPage<T> extends StatefulWidget { ...@@ -348,18 +346,13 @@ class _SearchPage<T> extends StatefulWidget {
} }
class _SearchPageState<T> extends State<_SearchPage<T>> { class _SearchPageState<T> extends State<_SearchPage<T>> {
// This node is owned, but not hosted by, the search page. Hosting is done by
// the text field.
FocusNode focusNode = FocusNode();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
queryTextController.addListener(_onQueryChanged); queryTextController.addListener(_onQueryChanged);
widget.animation.addStatusListener(_onAnimationStatusChanged); widget.animation.addStatusListener(_onAnimationStatusChanged);
widget.delegate._currentBodyNotifier.addListener(_onSearchBodyChanged); widget.delegate._currentBodyNotifier.addListener(_onSearchBodyChanged);
focusNode.addListener(_onFocusChanged); widget.delegate._focusNode.addListener(_onFocusChanged);
widget.delegate._focusNode = focusNode;
} }
@override @override
...@@ -368,8 +361,7 @@ class _SearchPageState<T> extends State<_SearchPage<T>> { ...@@ -368,8 +361,7 @@ class _SearchPageState<T> extends State<_SearchPage<T>> {
queryTextController.removeListener(_onQueryChanged); queryTextController.removeListener(_onQueryChanged);
widget.animation.removeStatusListener(_onAnimationStatusChanged); widget.animation.removeStatusListener(_onAnimationStatusChanged);
widget.delegate._currentBodyNotifier.removeListener(_onSearchBodyChanged); widget.delegate._currentBodyNotifier.removeListener(_onSearchBodyChanged);
widget.delegate._focusNode = null; widget.delegate._focusNode.removeListener(_onFocusChanged);
focusNode.dispose();
} }
void _onAnimationStatusChanged(AnimationStatus status) { void _onAnimationStatusChanged(AnimationStatus status) {
...@@ -378,12 +370,12 @@ class _SearchPageState<T> extends State<_SearchPage<T>> { ...@@ -378,12 +370,12 @@ class _SearchPageState<T> extends State<_SearchPage<T>> {
} }
widget.animation.removeStatusListener(_onAnimationStatusChanged); widget.animation.removeStatusListener(_onAnimationStatusChanged);
if (widget.delegate._currentBody == _SearchBody.suggestions) { if (widget.delegate._currentBody == _SearchBody.suggestions) {
focusNode.requestFocus(); FocusScope.of(context).requestFocus(widget.delegate._focusNode);
} }
} }
void _onFocusChanged() { void _onFocusChanged() {
if (focusNode.hasFocus && widget.delegate._currentBody != _SearchBody.suggestions) { if (widget.delegate._focusNode.hasFocus && widget.delegate._currentBody != _SearchBody.suggestions) {
widget.delegate.showSuggestions(context); widget.delegate.showSuggestions(context);
} }
} }
...@@ -444,7 +436,7 @@ class _SearchPageState<T> extends State<_SearchPage<T>> { ...@@ -444,7 +436,7 @@ class _SearchPageState<T> extends State<_SearchPage<T>> {
leading: widget.delegate.buildLeading(context), leading: widget.delegate.buildLeading(context),
title: TextField( title: TextField(
controller: queryTextController, controller: queryTextController,
focusNode: focusNode, focusNode: widget.delegate._focusNode,
style: theme.textTheme.title, style: theme.textTheme.title,
textInputAction: TextInputAction.search, textInputAction: TextInputAction.search,
onSubmitted: (String _) { onSubmitted: (String _) {
......
...@@ -1455,8 +1455,7 @@ class RenderEditable extends RenderBox { ...@@ -1455,8 +1455,7 @@ class RenderEditable extends RenderBox {
} }
TextSelection _selectWordAtOffset(TextPosition position) { TextSelection _selectWordAtOffset(TextPosition position) {
assert(_textLayoutLastWidth == constraints.maxWidth, assert(_textLayoutLastWidth == constraints.maxWidth);
'Last width ($_textLayoutLastWidth) not the same as max width constraint (${constraints.maxWidth}).');
final TextRange word = _textPainter.getWordBoundary(position); final TextRange word = _textPainter.getWordBoundary(position);
// When long-pressing past the end of the text, we want a collapsed cursor. // When long-pressing past the end of the text, we want a collapsed cursor.
if (position.offset >= word.end) if (position.offset >= word.end)
...@@ -1528,8 +1527,7 @@ class RenderEditable extends RenderBox { ...@@ -1528,8 +1527,7 @@ class RenderEditable extends RenderBox {
} }
void _paintCaret(Canvas canvas, Offset effectiveOffset, TextPosition textPosition) { void _paintCaret(Canvas canvas, Offset effectiveOffset, TextPosition textPosition) {
assert(_textLayoutLastWidth == constraints.maxWidth, assert(_textLayoutLastWidth == constraints.maxWidth);
'Last width ($_textLayoutLastWidth) not the same as max width constraint (${constraints.maxWidth}).');
// If the floating cursor is enabled, the text cursor's color is [backgroundCursorColor] while // If the floating cursor is enabled, the text cursor's color is [backgroundCursorColor] while
// the floating cursor's color is _cursorColor; // the floating cursor's color is _cursorColor;
...@@ -1595,8 +1593,7 @@ class RenderEditable extends RenderBox { ...@@ -1595,8 +1593,7 @@ class RenderEditable extends RenderBox {
} }
void _paintFloatingCaret(Canvas canvas, Offset effectiveOffset) { void _paintFloatingCaret(Canvas canvas, Offset effectiveOffset) {
assert(_textLayoutLastWidth == constraints.maxWidth, assert(_textLayoutLastWidth == constraints.maxWidth);
'Last width ($_textLayoutLastWidth) not the same as max width constraint (${constraints.maxWidth}).');
assert(_floatingCursorOn); assert(_floatingCursorOn);
// We always want the floating cursor to render at full opacity. // We always want the floating cursor to render at full opacity.
...@@ -1683,8 +1680,7 @@ class RenderEditable extends RenderBox { ...@@ -1683,8 +1680,7 @@ class RenderEditable extends RenderBox {
} }
void _paintSelection(Canvas canvas, Offset effectiveOffset) { void _paintSelection(Canvas canvas, Offset effectiveOffset) {
assert(_textLayoutLastWidth == constraints.maxWidth, assert(_textLayoutLastWidth == constraints.maxWidth);
'Last width ($_textLayoutLastWidth) not the same as max width constraint (${constraints.maxWidth}).');
assert(_selectionRects != null); assert(_selectionRects != null);
final Paint paint = Paint()..color = _selectionColor; final Paint paint = Paint()..color = _selectionColor;
for (ui.TextBox box in _selectionRects) for (ui.TextBox box in _selectionRects)
...@@ -1692,8 +1688,7 @@ class RenderEditable extends RenderBox { ...@@ -1692,8 +1688,7 @@ class RenderEditable extends RenderBox {
} }
void _paintContents(PaintingContext context, Offset offset) { void _paintContents(PaintingContext context, Offset offset) {
assert(_textLayoutLastWidth == constraints.maxWidth, assert(_textLayoutLastWidth == constraints.maxWidth);
'Last width ($_textLayoutLastWidth) not the same as max width constraint (${constraints.maxWidth}).');
final Offset effectiveOffset = offset + _paintOffset; final Offset effectiveOffset = offset + _paintOffset;
bool showSelection = false; bool showSelection = false;
......
...@@ -90,7 +90,7 @@ import 'package:flutter/foundation.dart'; ...@@ -90,7 +90,7 @@ import 'package:flutter/foundation.dart';
/// onTap: () { /// onTap: () {
/// FocusScope.of(context).requestFocus(_focusNode); /// FocusScope.of(context).requestFocus(_focusNode);
/// }, /// },
/// child: const Text('Tap to focus'), /// child: Text('Tap to focus'),
/// ); /// );
/// } /// }
/// return Text(_message ?? 'Press a key'); /// return Text(_message ?? 'Press a key');
......
...@@ -787,7 +787,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -787,7 +787,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
final LayerLink _layerLink = LayerLink(); final LayerLink _layerLink = LayerLink();
bool _didAutoFocus = false; bool _didAutoFocus = false;
FocusAttachment _focusAttachment;
// This value is an eyeball estimation of the time it takes for the iOS cursor // This value is an eyeball estimation of the time it takes for the iOS cursor
// to ease in and out. // to ease in and out.
...@@ -810,7 +809,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -810,7 +809,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
void initState() { void initState() {
super.initState(); super.initState();
widget.controller.addListener(_didChangeTextEditingValue); widget.controller.addListener(_didChangeTextEditingValue);
_focusAttachment = widget.focusNode.attach(context);
widget.focusNode.addListener(_handleFocusChanged); widget.focusNode.addListener(_handleFocusChanged);
_scrollController.addListener(() { _selectionOverlay?.updateForScroll(); }); _scrollController.addListener(() { _selectionOverlay?.updateForScroll(); });
_cursorBlinkOpacityController = AnimationController(vsync: this, duration: _fadeDuration); _cursorBlinkOpacityController = AnimationController(vsync: this, duration: _fadeDuration);
...@@ -838,8 +836,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -838,8 +836,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
} }
if (widget.focusNode != oldWidget.focusNode) { if (widget.focusNode != oldWidget.focusNode) {
oldWidget.focusNode.removeListener(_handleFocusChanged); oldWidget.focusNode.removeListener(_handleFocusChanged);
_focusAttachment?.detach();
_focusAttachment = widget.focusNode.attach(context);
widget.focusNode.addListener(_handleFocusChanged); widget.focusNode.addListener(_handleFocusChanged);
updateKeepAlive(); updateKeepAlive();
} }
...@@ -856,7 +852,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -856,7 +852,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
assert(_cursorTimer == null); assert(_cursorTimer == null);
_selectionOverlay?.dispose(); _selectionOverlay?.dispose();
_selectionOverlay = null; _selectionOverlay = null;
_focusAttachment.detach();
widget.focusNode.removeListener(_handleFocusChanged); widget.focusNode.removeListener(_handleFocusChanged);
super.dispose(); super.dispose();
} }
...@@ -1096,7 +1091,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1096,7 +1091,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if (_hasFocus) { if (_hasFocus) {
_openInputConnection(); _openInputConnection();
} else { } else {
widget.focusNode.requestFocus(); final List<FocusScopeNode> ancestorScopes = FocusScope.ancestorsOf(context);
for (int i = ancestorScopes.length - 1; i >= 1; i -= 1)
ancestorScopes[i].setFirstFocus(ancestorScopes[i - 1]);
FocusScope.of(context).requestFocus(widget.focusNode);
} }
} }
...@@ -1402,7 +1400,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1402,7 +1400,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMediaQuery(context));
_focusAttachment.reparent(); FocusScope.of(context).reparentIfNeeded(widget.focusNode);
super.build(context); // See AutomaticKeepAliveClientMixin. super.build(context); // See AutomaticKeepAliveClientMixin.
final TextSelectionControls controls = widget.selectionControls; final TextSelectionControls controls = widget.selectionControls;
......
...@@ -2124,7 +2124,7 @@ class BuildOwner { ...@@ -2124,7 +2124,7 @@ class BuildOwner {
/// the [FocusScopeNode] for a given [BuildContext]. /// the [FocusScopeNode] for a given [BuildContext].
/// ///
/// See [FocusManager] for more details. /// See [FocusManager] for more details.
FocusManager focusManager = FocusManager(); final FocusManager focusManager = FocusManager();
/// Adds an element to the dirty elements list so that it will be rebuilt /// Adds an element to the dirty elements list so that it will be rebuilt
/// when [WidgetsBinding.drawFrame] calls [buildScope]. /// when [WidgetsBinding.drawFrame] calls [buildScope].
......
...@@ -1468,7 +1468,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin { ...@@ -1468,7 +1468,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
final Set<Route<dynamic>> _poppedRoutes = <Route<dynamic>>{}; final Set<Route<dynamic>> _poppedRoutes = <Route<dynamic>>{};
/// The [FocusScopeNode] for the [FocusScope] that encloses the routes. /// The [FocusScopeNode] for the [FocusScope] that encloses the routes.
final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: 'Navigator Scope'); final FocusScopeNode focusScopeNode = FocusScopeNode();
final List<OverlayEntry> _initialOverlayEntries = <OverlayEntry>[]; final List<OverlayEntry> _initialOverlayEntries = <OverlayEntry>[];
...@@ -1556,7 +1556,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin { ...@@ -1556,7 +1556,7 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
route.dispose(); route.dispose();
_poppedRoutes.clear(); _poppedRoutes.clear();
_history.clear(); _history.clear();
focusScopeNode.dispose(); focusScopeNode.detach();
super.dispose(); super.dispose();
assert(() { _debugLocked = false; return true; }()); assert(() { _debugLocked = false; return true; }());
} }
......
...@@ -7,7 +7,6 @@ import 'package:flutter/services.dart'; ...@@ -7,7 +7,6 @@ import 'package:flutter/services.dart';
import 'basic.dart'; import 'basic.dart';
import 'focus_manager.dart'; import 'focus_manager.dart';
import 'focus_scope.dart';
import 'framework.dart'; import 'framework.dart';
export 'package:flutter/services.dart' show RawKeyEvent; export 'package:flutter/services.dart' show RawKeyEvent;
...@@ -113,5 +112,5 @@ class _RawKeyboardListenerState extends State<RawKeyboardListener> { ...@@ -113,5 +112,5 @@ class _RawKeyboardListenerState extends State<RawKeyboardListener> {
} }
@override @override
Widget build(BuildContext context) => Focus(focusNode: widget.focusNode, child: widget.child); Widget build(BuildContext context) => widget.child;
} }
...@@ -583,9 +583,6 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> { ...@@ -583,9 +583,6 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
// This is the combination of the two animations for the route. // This is the combination of the two animations for the route.
Listenable _listenable; Listenable _listenable;
/// The node this scope will use for its root [FocusScope] widget.
final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: '$_ModalScopeState Focus Scope');
@override @override
void initState() { void initState() {
super.initState(); super.initState();
...@@ -595,14 +592,12 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> { ...@@ -595,14 +592,12 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
if (widget.route.secondaryAnimation != null) if (widget.route.secondaryAnimation != null)
animations.add(widget.route.secondaryAnimation); animations.add(widget.route.secondaryAnimation);
_listenable = Listenable.merge(animations); _listenable = Listenable.merge(animations);
widget.route._grabFocusIfNeeded(focusScopeNode);
} }
@override @override
void didUpdateWidget(_ModalScope<T> oldWidget) { void didUpdateWidget(_ModalScope<T> oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
assert(widget.route == oldWidget.route); assert(widget.route == oldWidget.route);
widget.route._grabFocusIfNeeded(focusScopeNode);
} }
@override @override
...@@ -617,12 +612,6 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> { ...@@ -617,12 +612,6 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
}); });
} }
@override
void dispose() {
super.dispose();
focusScopeNode.dispose();
}
// This should be called to wrap any changes to route.isCurrent, route.canPop, // This should be called to wrap any changes to route.isCurrent, route.canPop,
// and route.offstage. // and route.offstage.
void _routeSetState(VoidCallback fn) { void _routeSetState(VoidCallback fn) {
...@@ -640,7 +629,7 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> { ...@@ -640,7 +629,7 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
child: PageStorage( child: PageStorage(
bucket: widget.route._storageBucket, // immutable bucket: widget.route._storageBucket, // immutable
child: FocusScope( child: FocusScope(
node: focusScopeNode, // immutable node: widget.route.focusScopeNode, // immutable
child: RepaintBoundary( child: RepaintBoundary(
child: AnimatedBuilder( child: AnimatedBuilder(
animation: _listenable, // immutable animation: _listenable, // immutable
...@@ -898,6 +887,9 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T ...@@ -898,6 +887,9 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
return child; return child;
} }
/// The node this route will use for its root [FocusScope] widget.
final FocusScopeNode focusScopeNode = FocusScopeNode();
@override @override
void install(OverlayEntry insertionPoint) { void install(OverlayEntry insertionPoint) {
super.install(insertionPoint); super.install(insertionPoint);
...@@ -905,20 +897,18 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T ...@@ -905,20 +897,18 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
_secondaryAnimationProxy = ProxyAnimation(super.secondaryAnimation); _secondaryAnimationProxy = ProxyAnimation(super.secondaryAnimation);
} }
bool _wantsFocus = false;
void _grabFocusIfNeeded(FocusScopeNode node) {
if (_wantsFocus) {
_wantsFocus = false;
navigator.focusScopeNode.setFirstFocus(node);
}
}
@override @override
TickerFuture didPush() { TickerFuture didPush() {
_wantsFocus = true; navigator.focusScopeNode.setFirstFocus(focusScopeNode);
return super.didPush(); return super.didPush();
} }
@override
void dispose() {
focusScopeNode.detach();
super.dispose();
}
// The API for subclasses to override - used by this class // The API for subclasses to override - used by this class
/// Whether you can dismiss this route by tapping the modal barrier. /// Whether you can dismiss this route by tapping the modal barrier.
......
...@@ -106,10 +106,7 @@ void main() { ...@@ -106,10 +106,7 @@ void main() {
testWidgets('Last tab gets focus', (WidgetTester tester) async { testWidgets('Last tab gets focus', (WidgetTester tester) async {
// 2 nodes for 2 tabs // 2 nodes for 2 tabs
final List<FocusNode> focusNodes = <FocusNode>[ final List<FocusNode> focusNodes = <FocusNode>[FocusNode(), FocusNode()];
FocusNode(debugLabel: 'Node 1'),
FocusNode(debugLabel: 'Node 2'),
];
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
...@@ -142,10 +139,7 @@ void main() { ...@@ -142,10 +139,7 @@ void main() {
testWidgets('Do not affect focus order in the route', (WidgetTester tester) async { testWidgets('Do not affect focus order in the route', (WidgetTester tester) async {
final List<FocusNode> focusNodes = <FocusNode>[ final List<FocusNode> focusNodes = <FocusNode>[
FocusNode(debugLabel: 'Node 1'), FocusNode(), FocusNode(), FocusNode(), FocusNode(),
FocusNode(debugLabel: 'Node 2'),
FocusNode(debugLabel: 'Node 3'),
FocusNode(debugLabel: 'Node 4'),
]; ];
await tester.pumpWidget( await tester.pumpWidget(
......
...@@ -9,14 +9,11 @@ void main() { ...@@ -9,14 +9,11 @@ void main() {
testWidgets('Dialog interaction', (WidgetTester tester) async { testWidgets('Dialog interaction', (WidgetTester tester) async {
expect(tester.testTextInput.isVisible, isFalse); expect(tester.testTextInput.isVisible, isFalse);
final FocusNode focusNode = FocusNode(debugLabel: 'Editable Text Node');
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( const MaterialApp(
home: Material( home: Material(
child: Center( child: Center(
child: TextField( child: TextField(
focusNode: focusNode,
autofocus: true, autofocus: true,
), ),
), ),
...@@ -133,7 +130,7 @@ void main() { ...@@ -133,7 +130,7 @@ void main() {
await tester.pumpWidget(Container()); await tester.pumpWidget(Container());
expect(tester.testTextInput.isVisible, isFalse); expect(tester.testTextInput.isVisible, isFalse);
}); }, skip: true); // https://github.com/flutter/flutter/issues/29384.
testWidgets('Focus triggers keep-alive', (WidgetTester tester) async { testWidgets('Focus triggers keep-alive', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
......
...@@ -2754,31 +2754,29 @@ void main() { ...@@ -2754,31 +2754,29 @@ void main() {
controller = TextEditingController(); controller = TextEditingController();
}); });
Future<void> setupWidget(WidgetTester tester) async { MaterialApp setupWidget() {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
controller = TextEditingController(); controller = TextEditingController();
await tester.pumpWidget( return MaterialApp(
MaterialApp( home: Material(
home: Material( child: RawKeyboardListener(
child: RawKeyboardListener( focusNode: focusNode,
focusNode: focusNode, onKey: null,
onKey: null, child: TextField(
child: TextField( controller: controller,
controller: controller, maxLines: 3,
maxLines: 3, strutStyle: StrutStyle.disabled,
strutStyle: StrutStyle.disabled,
),
), ),
), ) ,
), ),
); );
focusNode.requestFocus();
await tester.pump();
} }
testWidgets('Shift test 1', (WidgetTester tester) async { testWidgets('Shift test 1', (WidgetTester tester) async {
await setupWidget(tester);
await tester.pumpWidget(setupWidget());
const String testValue = 'a big house'; const String testValue = 'a big house';
await tester.enterText(find.byType(TextField), testValue); await tester.enterText(find.byType(TextField), testValue);
...@@ -2791,7 +2789,7 @@ void main() { ...@@ -2791,7 +2789,7 @@ void main() {
}); });
testWidgets('Control Shift test', (WidgetTester tester) async { testWidgets('Control Shift test', (WidgetTester tester) async {
await setupWidget(tester); await tester.pumpWidget(setupWidget());
const String testValue = 'their big house'; const String testValue = 'their big house';
await tester.enterText(find.byType(TextField), testValue); await tester.enterText(find.byType(TextField), testValue);
...@@ -2807,7 +2805,7 @@ void main() { ...@@ -2807,7 +2805,7 @@ void main() {
}); });
testWidgets('Down and up test', (WidgetTester tester) async { testWidgets('Down and up test', (WidgetTester tester) async {
await setupWidget(tester); await tester.pumpWidget(setupWidget());
const String testValue = 'a big house'; const String testValue = 'a big house';
await tester.enterText(find.byType(TextField), testValue); await tester.enterText(find.byType(TextField), testValue);
...@@ -2829,7 +2827,7 @@ void main() { ...@@ -2829,7 +2827,7 @@ void main() {
}); });
testWidgets('Down and up test 2', (WidgetTester tester) async { testWidgets('Down and up test 2', (WidgetTester tester) async {
await setupWidget(tester); await tester.pumpWidget(setupWidget());
const String testValue = 'a big house\njumped over a mouse\nOne more line yay'; // 11 \n 19 const String testValue = 'a big house\njumped over a mouse\nOne more line yay'; // 11 \n 19
await tester.enterText(find.byType(TextField), testValue); await tester.enterText(find.byType(TextField), testValue);
...@@ -2916,8 +2914,6 @@ void main() { ...@@ -2916,8 +2914,6 @@ void main() {
), ),
), ),
); );
focusNode.requestFocus();
await tester.pump();
const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19 const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19
await tester.enterText(find.byType(TextField), testValue); await tester.enterText(find.byType(TextField), testValue);
...@@ -2988,8 +2984,6 @@ void main() { ...@@ -2988,8 +2984,6 @@ void main() {
), ),
), ),
); );
focusNode.requestFocus();
await tester.pump();
const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19 const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19
await tester.enterText(find.byType(TextField), testValue); await tester.enterText(find.byType(TextField), testValue);
...@@ -3099,8 +3093,6 @@ void main() { ...@@ -3099,8 +3093,6 @@ void main() {
), ),
), ),
); );
focusNode.requestFocus();
await tester.pump();
const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19 const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19
await tester.enterText(find.byType(TextField), testValue); await tester.enterText(find.byType(TextField), testValue);
......
...@@ -16,8 +16,8 @@ import 'editable_text_utils.dart'; ...@@ -16,8 +16,8 @@ import 'editable_text_utils.dart';
import 'semantics_tester.dart'; import 'semantics_tester.dart';
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode(debugLabel: 'EditableText Node'); final FocusNode focusNode = FocusNode();
final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: 'EditableText Scope Node'); final FocusScopeNode focusScopeNode = FocusScopeNode();
const TextStyle textStyle = TextStyle(); const TextStyle textStyle = TextStyle();
const Color cursorColor = Color.fromARGB(0xFF, 0xFF, 0x00, 0x00); const Color cursorColor = Color.fromARGB(0xFF, 0xFF, 0x00, 0x00);
...@@ -975,9 +975,6 @@ void main() { ...@@ -975,9 +975,6 @@ void main() {
), ),
)); ));
focusNode.requestFocus();
await tester.pump();
expect( expect(
semantics, semantics,
includesNodeWith( includesNodeWith(
...@@ -1535,8 +1532,6 @@ void main() { ...@@ -1535,8 +1532,6 @@ void main() {
), ),
); );
focusNode.requestFocus();
// Now change it to make it obscure text. // Now change it to make it obscure text.
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: EditableText( home: EditableText(
...@@ -1911,7 +1906,7 @@ void main() { ...@@ -1911,7 +1906,7 @@ void main() {
); );
final GlobalKey<EditableTextState> editableTextKey = final GlobalKey<EditableTextState> editableTextKey =
GlobalKey<EditableTextState>(); GlobalKey<EditableTextState>();
final FocusNode focusNode = FocusNode(debugLabel: 'Test Focus Node'); final FocusNode focusNode = FocusNode();
await tester.pumpWidget(MaterialApp( // So we can show overlays. await tester.pumpWidget(MaterialApp( // So we can show overlays.
home: EditableText( home: EditableText(
......
This diff is collapsed.
...@@ -12,7 +12,7 @@ void sendFakeKeyEvent(Map<String, dynamic> data) { ...@@ -12,7 +12,7 @@ void sendFakeKeyEvent(Map<String, dynamic> data) {
BinaryMessages.handlePlatformMessage( BinaryMessages.handlePlatformMessage(
SystemChannels.keyEvent.name, SystemChannels.keyEvent.name,
SystemChannels.keyEvent.codec.encodeMessage(data), SystemChannels.keyEvent.codec.encodeMessage(data),
(ByteData data) {}, (ByteData data) { },
); );
} }
...@@ -29,15 +29,13 @@ void main() { ...@@ -29,15 +29,13 @@ void main() {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
await tester.pumpWidget( await tester.pumpWidget(RawKeyboardListener(
RawKeyboardListener( focusNode: focusNode,
focusNode: focusNode, onKey: events.add,
onKey: events.add, child: Container(),
child: Container(), ));
),
);
focusNode.requestFocus(); tester.binding.focusManager.rootScope.requestFocus(focusNode);
await tester.idle(); await tester.idle();
sendFakeKeyEvent(<String, dynamic>{ sendFakeKeyEvent(<String, dynamic>{
...@@ -67,15 +65,13 @@ void main() { ...@@ -67,15 +65,13 @@ void main() {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
await tester.pumpWidget( await tester.pumpWidget(RawKeyboardListener(
RawKeyboardListener( focusNode: focusNode,
focusNode: focusNode, onKey: events.add,
onKey: events.add, child: Container(),
child: Container(), ));
),
);
focusNode.requestFocus(); tester.binding.focusManager.rootScope.requestFocus(focusNode);
await tester.idle(); await tester.idle();
sendFakeKeyEvent(<String, dynamic>{ sendFakeKeyEvent(<String, dynamic>{
......
...@@ -692,7 +692,6 @@ abstract class TestWidgetsFlutterBinding extends BindingBase ...@@ -692,7 +692,6 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
FlutterError.onError = _oldExceptionHandler; FlutterError.onError = _oldExceptionHandler;
_pendingExceptionDetails = null; _pendingExceptionDetails = null;
_parentZone = null; _parentZone = null;
buildOwner.focusManager = FocusManager();
} }
} }
......
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