@@ -22,16 +22,38 @@ import 'framework.dart';
22
22
/// be logged.
23
23
bool debugFocusChanges = false ;
24
24
25
- bool _focusDebug (String message, [Iterable <String >? details]) {
26
- if (debugFocusChanges) {
27
- debugPrint ('FOCUS: $message ' );
28
- if (details != null && details.isNotEmpty) {
29
- for (final String detail in details) {
30
- debugPrint (' $detail ' );
31
- }
25
+ // When using _focusDebug, always call it like so:
26
+ //
27
+ // assert(_focusDebug(() => 'Blah $foo'));
28
+ //
29
+ // It needs to be inside the assert in order to be removed in release mode, and
30
+ // it needs to use a closure to generate the string in order to avoid string
31
+ // interpolation when debugFocusChanges is false.
32
+ //
33
+ // It will throw a StateError if you try to call it when the app is in release
34
+ // mode.
35
+ bool _focusDebug (
36
+ String Function () messageFunc, [
37
+ Iterable <Object > Function ()? detailsFunc,
38
+ ]) {
39
+ if (kReleaseMode) {
40
+ throw StateError (
41
+ '_focusDebug was called in Release mode. It should always be wrapped in '
42
+ 'an assert. Always call _focusDebug like so:\n '
43
+ r" assert(_focusDebug(() => 'Blah $foo'));"
44
+ );
45
+ }
46
+ if (! debugFocusChanges) {
47
+ return true ;
48
+ }
49
+ debugPrint ('FOCUS: ${messageFunc ()}' );
50
+ final Iterable <Object > details = detailsFunc? .call () ?? const < Object > [];
51
+ if (details.isNotEmpty) {
52
+ for (final Object detail in details) {
53
+ debugPrint (' $detail ' );
32
54
}
33
55
}
34
- // Return true so that it can be easily used inside of an assert.
56
+ // Return true so that it can be used inside of an assert.
35
57
return true ;
36
58
}
37
59
@@ -118,10 +140,10 @@ class _Autofocus {
118
140
&& scope.focusedChild == null
119
141
&& autofocusNode.ancestors.contains (scope);
120
142
if (shouldApply) {
121
- assert (_focusDebug ('Applying autofocus: $autofocusNode ' ));
143
+ assert (_focusDebug (() => 'Applying autofocus: $autofocusNode ' ));
122
144
autofocusNode._doRequestFocus (findFirstFocus: true );
123
145
} else {
124
- assert (_focusDebug ('Autofocus request discarded for node: $autofocusNode .' ));
146
+ assert (_focusDebug (() => 'Autofocus request discarded for node: $autofocusNode .' ));
125
147
}
126
148
}
127
149
}
@@ -169,7 +191,7 @@ class FocusAttachment {
169
191
///
170
192
/// Calling [FocusNode.dispose] will also automatically detach the node.
171
193
void detach () {
172
- assert (_focusDebug ('Detaching node:' , < String > [_node. toString () , 'With enclosing scope ${_node .enclosingScope }' ]));
194
+ assert (_focusDebug (() => 'Detaching node:' , () => < Object > [_node, 'With enclosing scope ${_node .enclosingScope }' ]));
173
195
if (isAttached) {
174
196
if (_node.hasPrimaryFocus || (_node._manager != null && _node._manager! ._markedForFocus == _node)) {
175
197
_node.unfocus (disposition: UnfocusDisposition .previouslyFocusedChild);
@@ -877,7 +899,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
877
899
scope._doRequestFocus (findFirstFocus: true );
878
900
break ;
879
901
}
880
- assert (_focusDebug ('Unfocused node:' , < String > ['primary focus was $this ' , 'next focus will be ${_manager ?._markedForFocus }' ]));
902
+ assert (_focusDebug (() => 'Unfocused node:' , () => < Object > ['primary focus was $this ' , 'next focus will be ${_manager ?._markedForFocus }' ]));
881
903
}
882
904
883
905
/// Removes the keyboard token from this focus node if it has one.
@@ -1063,7 +1085,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
1063
1085
// Note that this is overridden in FocusScopeNode.
1064
1086
void _doRequestFocus ({required bool findFirstFocus}) {
1065
1087
if (! canRequestFocus) {
1066
- assert (_focusDebug ('Node NOT requesting focus because canRequestFocus is false: $this ' ));
1088
+ assert (_focusDebug (() => 'Node NOT requesting focus because canRequestFocus is false: $this ' ));
1067
1089
return ;
1068
1090
}
1069
1091
// If the node isn't part of the tree, then we just defer the focus request
@@ -1078,7 +1100,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
1078
1100
return ;
1079
1101
}
1080
1102
_hasKeyboardToken = true ;
1081
- assert (_focusDebug ('Node requesting focus: $this ' ));
1103
+ assert (_focusDebug (() => 'Node requesting focus: $this ' ));
1082
1104
_markNextFocus (this );
1083
1105
}
1084
1106
@@ -1109,7 +1131,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
1109
1131
FocusNode scopeFocus = this ;
1110
1132
for (final FocusScopeNode ancestor in ancestors.whereType <FocusScopeNode >()) {
1111
1133
assert (scopeFocus != ancestor, 'Somehow made a loop by setting focusedChild to its scope.' );
1112
- assert (_focusDebug ('Setting $scopeFocus as focused child for scope:' , < String > [ancestor. toString () ]));
1134
+ assert (_focusDebug (() => 'Setting $scopeFocus as focused child for scope:' , () => < Object > [ancestor]));
1113
1135
// Remove it anywhere in the focused child history.
1114
1136
ancestor._focusedChildren.remove (scopeFocus);
1115
1137
// Add it to the end of the list, which is also the top of the queue: The
@@ -1276,7 +1298,7 @@ class FocusScopeNode extends FocusNode {
1276
1298
/// tree, the given scope must be a descendant of this scope.
1277
1299
void setFirstFocus (FocusScopeNode scope) {
1278
1300
assert (scope != this , 'Unexpected self-reference in setFirstFocus.' );
1279
- assert (_focusDebug ('Setting scope as first focus in $this to node:' , < String > [scope. toString () ]));
1301
+ assert (_focusDebug (() => 'Setting scope as first focus in $this to node:' , () => < Object > [scope]));
1280
1302
if (scope._parent == null ) {
1281
1303
_reparent (scope);
1282
1304
}
@@ -1306,7 +1328,7 @@ class FocusScopeNode extends FocusNode {
1306
1328
}
1307
1329
1308
1330
assert (_manager != null );
1309
- assert (_focusDebug ('Autofocus scheduled for $node : scope $this ' ));
1331
+ assert (_focusDebug (() => 'Autofocus scheduled for $node : scope $this ' ));
1310
1332
_manager? ._pendingAutofocuses.add (_Autofocus (scope: this , autofocusNode: node));
1311
1333
_manager? ._markNeedsUpdate ();
1312
1334
}
@@ -1542,7 +1564,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
1542
1564
void _markDetached (FocusNode node) {
1543
1565
// The node has been removed from the tree, so it no longer needs to be
1544
1566
// notified of changes.
1545
- assert (_focusDebug ('Node was detached: $node ' ));
1567
+ assert (_focusDebug (() => 'Node was detached: $node ' ));
1546
1568
if (_primaryFocus == node) {
1547
1569
_primaryFocus = null ;
1548
1570
}
@@ -1551,7 +1573,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
1551
1573
1552
1574
void _markPropertiesChanged (FocusNode node) {
1553
1575
_markNeedsUpdate ();
1554
- assert (_focusDebug ('Properties changed for node $node .' ));
1576
+ assert (_focusDebug (() => 'Properties changed for node $node .' ));
1555
1577
_dirtyNodes.add (node);
1556
1578
}
1557
1579
@@ -1575,7 +1597,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
1575
1597
// Request that an update be scheduled, optionally requesting focus for the
1576
1598
// given newFocus node.
1577
1599
void _markNeedsUpdate () {
1578
- assert (_focusDebug ('Scheduling update, current focus is $_primaryFocus , next focus will be $_markedForFocus ' ));
1600
+ assert (_focusDebug (() => 'Scheduling update, current focus is $_primaryFocus , next focus will be $_markedForFocus ' ));
1579
1601
if (_haveScheduledUpdate) {
1580
1602
return ;
1581
1603
}
@@ -1597,7 +1619,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
1597
1619
// then revert to the root scope.
1598
1620
_markedForFocus = rootScope;
1599
1621
}
1600
- assert (_focusDebug ('Refreshing focus state. Next focus will be $_markedForFocus ' ));
1622
+ assert (_focusDebug (() => 'Refreshing focus state. Next focus will be $_markedForFocus ' ));
1601
1623
// A node has requested to be the next focus, and isn't already the primary
1602
1624
// focus.
1603
1625
if (_markedForFocus != null && _markedForFocus != _primaryFocus) {
@@ -1613,7 +1635,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
1613
1635
}
1614
1636
assert (_markedForFocus == null );
1615
1637
if (previousFocus != _primaryFocus) {
1616
- assert (_focusDebug ('Updating focus from $previousFocus to $_primaryFocus ' ));
1638
+ assert (_focusDebug (() => 'Updating focus from $previousFocus to $_primaryFocus ' ));
1617
1639
if (previousFocus != null ) {
1618
1640
_dirtyNodes.add (previousFocus);
1619
1641
}
@@ -1624,7 +1646,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
1624
1646
for (final FocusNode node in _dirtyNodes) {
1625
1647
node._notify ();
1626
1648
}
1627
- assert (_focusDebug ('Notified ${_dirtyNodes .length } dirty nodes:' , _dirtyNodes. toList (). map < String >(( FocusNode node) => node. toString ()) ));
1649
+ assert (_focusDebug (() => 'Notified ${_dirtyNodes .length } dirty nodes:' , () => _dirtyNodes ));
1628
1650
_dirtyNodes.clear ();
1629
1651
if (previousFocus != _primaryFocus) {
1630
1652
notifyListeners ();
@@ -1766,9 +1788,9 @@ class _HighlightModeManager {
1766
1788
_lastInteractionWasTouch = false ;
1767
1789
updateMode ();
1768
1790
1769
- assert (_focusDebug ('Received key event $message ' ));
1791
+ assert (_focusDebug (() => 'Received key event $message ' ));
1770
1792
if (FocusManager .instance.primaryFocus == null ) {
1771
- assert (_focusDebug ('No primary focus for key event, ignored: $message ' ));
1793
+ assert (_focusDebug (() => 'No primary focus for key event, ignored: $message ' ));
1772
1794
return false ;
1773
1795
}
1774
1796
@@ -1794,11 +1816,11 @@ class _HighlightModeManager {
1794
1816
case KeyEventResult .ignored:
1795
1817
continue ;
1796
1818
case KeyEventResult .handled:
1797
- assert (_focusDebug ('Node $node handled key event $message .' ));
1819
+ assert (_focusDebug (() => 'Node $node handled key event $message .' ));
1798
1820
handled = true ;
1799
1821
break ;
1800
1822
case KeyEventResult .skipRemainingHandlers:
1801
- assert (_focusDebug ('Node $node stopped key event propagation: $message .' ));
1823
+ assert (_focusDebug (() => 'Node $node stopped key event propagation: $message .' ));
1802
1824
handled = false ;
1803
1825
break ;
1804
1826
}
@@ -1808,7 +1830,7 @@ class _HighlightModeManager {
1808
1830
break ;
1809
1831
}
1810
1832
if (! handled) {
1811
- assert (_focusDebug ('Key event not handled by anyone: $message .' ));
1833
+ assert (_focusDebug (() => 'Key event not handled by anyone: $message .' ));
1812
1834
}
1813
1835
return handled;
1814
1836
}
0 commit comments