From 177dbc9484eeececfa83c64e8d3dfe957c3f4040 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Fri, 25 Jun 2021 15:28:37 -0700 Subject: [PATCH] Make _kDebugFocus into a global that can be set (debugFocusChanges) (#85343) This removes the _kDebugFocus private global and replaces it with a global debugFocusChanges --- .../lib/src/widgets/focus_manager.dart | 14 ++++-- .../test/widgets/focus_manager_test.dart | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/packages/flutter/lib/src/widgets/focus_manager.dart b/packages/flutter/lib/src/widgets/focus_manager.dart index abb110c472d..6c442f73a8e 100644 --- a/packages/flutter/lib/src/widgets/focus_manager.dart +++ b/packages/flutter/lib/src/widgets/focus_manager.dart @@ -15,12 +15,15 @@ import 'focus_scope.dart'; import 'focus_traversal.dart'; import 'framework.dart'; -// Used for debugging focus code. Set to true to see highly verbose debug output -// when focus changes occur. -const bool _kDebugFocus = false; +/// Setting to true will cause extensive logging to occur when focus changes occur. +/// +/// Can be used to debug focus issues: each time the focus changes, the focus +/// tree will be printed and requests for focus and other focus operations will +/// be logged. +bool debugFocusChanges = false; bool _focusDebug(String message, [Iterable? details]) { - if (_kDebugFocus) { + if (debugFocusChanges) { debugPrint('FOCUS: $message'); if (details != null && details.isNotEmpty) { for (final String detail in details) { @@ -28,6 +31,7 @@ bool _focusDebug(String message, [Iterable? details]) { } } } + // Return true so that it can be easily used inside of an assert. return true; } @@ -1789,7 +1793,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier { notifyListeners(); } assert(() { - if (_kDebugFocus) { + if (debugFocusChanges) { debugDumpFocusTree(); } return true; diff --git a/packages/flutter/test/widgets/focus_manager_test.dart b/packages/flutter/test/widgets/focus_manager_test.dart index 88828fa88de..881c2038146 100644 --- a/packages/flutter/test/widgets/focus_manager_test.dart +++ b/packages/flutter/test/widgets/focus_manager_test.dart @@ -1351,4 +1351,50 @@ void main() { tester.binding.focusManager.removeListener(handleFocusChange); }); + + testWidgets('debugFocusChanges causes logging of focus changes', (WidgetTester tester) async { + final bool oldDebugFocusChanges = debugFocusChanges; + final DebugPrintCallback oldDebugPrint = debugPrint; + final StringBuffer messages = StringBuffer(); + debugPrint = (String? message, {int? wrapWidth}) { + messages.writeln(message ?? ''); + }; + debugFocusChanges = true; + try { + final BuildContext context = await setupWidget(tester); + final FocusScopeNode parent1 = FocusScopeNode(debugLabel: 'parent1'); + final FocusAttachment parent1Attachment = parent1.attach(context); + final FocusNode child1 = FocusNode(debugLabel: 'child1'); + final FocusAttachment child1Attachment = child1.attach(context); + parent1Attachment.reparent(parent: tester.binding.focusManager.rootScope); + child1Attachment.reparent(parent: parent1); + + int notifyCount = 0; + void handleFocusChange() { + notifyCount++; + } + tester.binding.focusManager.addListener(handleFocusChange); + + parent1.requestFocus(); + expect(notifyCount, equals(0)); + await tester.pump(); + expect(notifyCount, equals(1)); + notifyCount = 0; + + child1.requestFocus(); + await tester.pump(); + expect(notifyCount, equals(1)); + notifyCount = 0; + + tester.binding.focusManager.removeListener(handleFocusChange); + } finally { + debugFocusChanges = oldDebugFocusChanges; + debugPrint = oldDebugPrint; + } + final String messagesStr = messages.toString(); + expect(messagesStr.split('\n').length, equals(58)); + expect(messagesStr, contains(RegExp(r' └─Child 1: FocusScopeNode#[a-f0-9]{5}\(parent1 \[PRIMARY FOCUS\]\)'))); + expect(messagesStr, contains('FOCUS: Notifying 2 dirty nodes')); + expect(messagesStr, contains(RegExp(r'FOCUS: Scheduling update, current focus is null, next focus will be FocusScopeNode#.*parent1'))); + }); }