Skip to content

Commit 4f78684

Browse files
Revert "Add FocusNode.focusabilityListenable (#144280)" since the feature is no longer needed (#145102)
This reverts commit 726e5d2. *Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.* *List which issues are fixed by this PR. You must list at least one issue. An issue is not required if the PR fixes something trivial like a typo.* *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*
1 parent 33fbb75 commit 4f78684

3 files changed

Lines changed: 10 additions & 598 deletions

File tree

packages/flutter/lib/src/widgets/focus_manager.dart

Lines changed: 10 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -517,76 +517,21 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
517517
/// focus traversal policy for a widget subtree.
518518
/// * [FocusTraversalPolicy], a class that can be extended to describe a
519519
/// traversal policy.
520-
bool get canRequestFocus => _canRequestFocus && (_focusabilityListenable?.value ?? _computeAncestorsAllowFocus());
521-
bool _computeAncestorsAllowFocus() => ancestors.every(_allowDescendantsToBeFocused);
520+
bool get canRequestFocus => _canRequestFocus && ancestors.every(_allowDescendantsToBeFocused);
522521
static bool _allowDescendantsToBeFocused(FocusNode ancestor) => ancestor.descendantsAreFocusable;
523522

524523
bool _canRequestFocus;
525524
@mustCallSuper
526525
set canRequestFocus(bool value) {
527-
if (value == _canRequestFocus) {
528-
return;
529-
}
530-
// Have to set this first before unfocusing, since it checks this to cull
531-
// unfocusable, previously-focused children.
532-
_canRequestFocus = value;
533-
if (hasFocus && !value) {
534-
unfocus(disposition: UnfocusDisposition.previouslyFocusedChild);
535-
}
536-
537-
final _FocusabilityListenable? focusabilityListenable = _focusabilityListenable;
538-
if (focusabilityListenable != null && focusabilityListenable.hasListeners) {
539-
final bool ancestorsAllowFocus = focusabilityListenable._ancestorsAllowFocus = _adjustListeningNodeCountForAncestors(value ? 1 : -1);
540-
if (ancestorsAllowFocus) {
541-
focusabilityListenable.notifyListeners();
542-
}
543-
}
544-
_manager?._markPropertiesChanged(this);
545-
}
546-
547-
// The number of descendant focus nodes whose focusability must be
548-
// re-evaluated, when this node's `descentantsAreFocusable` value changes.
549-
// This does not include nodes with `_canRequestFocus` set to false, even when
550-
// their focusability listenable has listeners.
551-
int _focusabilityListeningDescendantCount = 0;
552-
553-
/// A [ValueListenable] that notifies registered listeners when the
554-
/// focusability of this [FocusNode] changes.
555-
///
556-
/// The [ValueListenable]'s `value` indicates whether this [FocusNode] can
557-
/// request primary focus. It's value is always consistent with the return
558-
/// value of the [canRequestFocus] getter, which only returns true when the
559-
/// [FocusNode]'s [canRequestFocus] setter is set to true, and all of its
560-
/// ancestors in the focus tree have [FocusNode.descendantsAreFocusable] set to
561-
/// true.
562-
///
563-
/// Unlike listeners added to the [FocusNode] itself, which won't be notified
564-
/// until focus changes are applied in microtasks, listeners added to
565-
/// [focusabilityListenable] are notified immediately as the [FocusNode]'s
566-
/// focusability changes.
567-
///
568-
/// This can be used to monitor, for example, whether a text field is currently
569-
/// disabled, or in an inactive route, thus isn't receiving user interactions,
570-
/// so that text field can unsubscribe itself from system services such as
571-
/// scribble and autofill when it becomes unfocusable, and re-subscribe when it
572-
/// becomes focusable again.
573-
///
574-
/// This [ValueListenable] is managed by this [FocusNode]. It must not be used
575-
/// after the [FocusNode] itself is disposed.
576-
ValueListenable<bool> get focusabilityListenable => _focusabilityListenable ??= _FocusabilityListenable(this);
577-
_FocusabilityListenable? _focusabilityListenable;
578-
579-
// Returns whether all ancestors have `descendantsAreFocusable` set to true.
580-
bool _adjustListeningNodeCountForAncestors(int delta) {
581-
assert(delta != 0);
582-
for (FocusNode? node = parent; node != null; node = node.parent) {
583-
node._focusabilityListeningDescendantCount += delta;
584-
assert(node._focusabilityListeningDescendantCount >= 0);
585-
if (!node.descendantsAreFocusable) {
586-
return false;
526+
if (value != _canRequestFocus) {
527+
// Have to set this first before unfocusing, since it checks this to cull
528+
// unfocusable, previously-focused children.
529+
_canRequestFocus = value;
530+
if (hasFocus && !value) {
531+
unfocus(disposition: UnfocusDisposition.previouslyFocusedChild);
587532
}
533+
_manager?._markPropertiesChanged(this);
588534
}
589-
return true;
590535
}
591536

592537
/// If false, will disable focus for all of this node's descendants.
@@ -629,47 +574,9 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
629574
if (!value && hasFocus) {
630575
unfocus(disposition: UnfocusDisposition.previouslyFocusedChild);
631576
}
632-
_onDescendantsAreFocusableChanged(value);
633577
_manager?._markPropertiesChanged(this);
634578
}
635579

636-
void _onDescendantsAreFocusableChanged(bool newValue) {
637-
assert(_focusabilityListeningDescendantCount >= 0);
638-
final int ancestorListenerAdjustment = newValue ? _focusabilityListeningDescendantCount : -_focusabilityListeningDescendantCount;
639-
640-
// If there's an ancestor that disallows focus, changing the
641-
// `descendantsAreFocusable` value of this node never affects the
642-
// focusability of the descendants. Notify _focusabilityListenerCount
643-
// listeners only when this is not the case.
644-
final bool notifyChildren = ancestorListenerAdjustment != 0
645-
&& _adjustListeningNodeCountForAncestors(ancestorListenerAdjustment);
646-
if (notifyChildren) {
647-
assert(children.isNotEmpty);
648-
for (final FocusNode child in children) {
649-
child._notifyFocusabilityListenersInSubtree(newValue);
650-
}
651-
}
652-
}
653-
654-
void _notifyFocusabilityListenersInSubtree(bool ancestorsAllowFocus) {
655-
final _FocusabilityListenable? focusabilityListenable = _focusabilityListenable;
656-
if (_canRequestFocus && focusabilityListenable != null && focusabilityListenable.hasListeners) {
657-
assert(ancestorsAllowFocus == _computeAncestorsAllowFocus());
658-
assert(ancestorsAllowFocus != focusabilityListenable._ancestorsAllowFocus);
659-
focusabilityListenable._ancestorsAllowFocus = ancestorsAllowFocus;
660-
focusabilityListenable.notifyListeners();
661-
}
662-
663-
if (_focusabilityListeningDescendantCount > 0 && descendantsAreFocusable) {
664-
// Further propagate to children whose focusability is determined by this
665-
// node's ancestors.
666-
assert(children.isNotEmpty);
667-
for (final FocusNode child in children) {
668-
child._notifyFocusabilityListenersInSubtree(ancestorsAllowFocus);
669-
}
670-
}
671-
}
672-
673580
/// If false, tells the focus traversal policy to skip over for all of this
674581
/// node's descendants for purposes of the traversal algorithm.
675582
///
@@ -1104,8 +1011,7 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
11041011
@mustCallSuper
11051012
void _reparent(FocusNode child) {
11061013
assert(child != this, 'Tried to make a child into a parent of itself.');
1107-
final FocusNode? oldParent = child._parent;
1108-
if (oldParent == this) {
1014+
if (child._parent == this) {
11091015
assert(_children.contains(child), "Found a node that says it's a child, but doesn't appear in the child list.");
11101016
// The child is already a child of this parent.
11111017
return;
@@ -1114,30 +1020,14 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
11141020
assert(!ancestors.contains(child), 'The supplied child is already an ancestor of this node. Loops are not allowed.');
11151021
final FocusScopeNode? oldScope = child.enclosingScope;
11161022
final bool hadFocus = child.hasFocus;
1117-
1118-
final _FocusabilityListenable? childFocusabilityListenable = child._focusabilityListenable;
1119-
final int childSubtreeListenerCount = (child.descendantsAreFocusable ? child._focusabilityListeningDescendantCount : 0)
1120-
+ (child._canRequestFocus && childFocusabilityListenable != null && childFocusabilityListenable.hasListeners ? 1 : 0);
1121-
// If childSubtreeListenerCount == 0, we don't care about focusability since there are no listeners.
1122-
final bool childCouldFocus = childSubtreeListenerCount > 0
1123-
&& child._adjustListeningNodeCountForAncestors(-childSubtreeListenerCount);
1124-
oldParent?._removeChild(child, removeScopeFocus: oldScope != nearestScope);
1125-
1023+
child._parent?._removeChild(child, removeScopeFocus: oldScope != nearestScope);
11261024
_children.add(child);
11271025
child._parent = this;
11281026
child._ancestors = null;
11291027
child._updateManager(_manager);
11301028
for (final FocusNode ancestor in child.ancestors) {
11311029
ancestor._descendants = null;
11321030
}
1133-
1134-
if (childSubtreeListenerCount > 0) {
1135-
final bool childCanFocus = child._adjustListeningNodeCountForAncestors(childSubtreeListenerCount);
1136-
if (childCanFocus != childCouldFocus) {
1137-
child._notifyFocusabilityListenersInSubtree(childCanFocus);
1138-
}
1139-
}
1140-
11411031
if (hadFocus) {
11421032
// Update the focus chain for the current focus without changing it.
11431033
_manager?.primaryFocus?._setAsFocusedChildForScope();
@@ -1183,8 +1073,6 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
11831073

11841074
@override
11851075
void dispose() {
1186-
_focusabilityListenable?.dispose();
1187-
_focusabilityListenable = null;
11881076
// Detaching will also unfocus and clean up the manager's data structures.
11891077
_attachment?.detach();
11901078
super.dispose();
@@ -1383,14 +1271,6 @@ class FocusScopeNode extends FocusNode {
13831271
this.traversalEdgeBehavior = TraversalEdgeBehavior.closedLoop,
13841272
}) : super(descendantsAreFocusable: true);
13851273

1386-
@override
1387-
set canRequestFocus(bool value) {
1388-
if (value != _canRequestFocus) {
1389-
super.canRequestFocus = value;
1390-
_onDescendantsAreFocusableChanged(value);
1391-
}
1392-
}
1393-
13941274
@override
13951275
FocusScopeNode get nearestScope => this;
13961276

@@ -1534,42 +1414,6 @@ class FocusScopeNode extends FocusNode {
15341414
}
15351415
}
15361416

1537-
class _FocusabilityListenable extends ChangeNotifier implements ValueListenable<bool> {
1538-
_FocusabilityListenable(this.node);
1539-
1540-
final FocusNode node;
1541-
1542-
@override
1543-
bool get value {
1544-
assert(!hasListeners || _ancestorsAllowFocus == node._computeAncestorsAllowFocus());
1545-
return node._canRequestFocus && (hasListeners ? _ancestorsAllowFocus : node._computeAncestorsAllowFocus());
1546-
}
1547-
1548-
// True if all ancestors of `node` have `descentantsAreFocusable` set to
1549-
// true. The value is only maintained when there are listeners, and
1550-
// `node._canRequestFocus` is true.
1551-
bool _ancestorsAllowFocus = true;
1552-
1553-
@override
1554-
void addListener(VoidCallback listener) {
1555-
final bool hadListener = hasListeners;
1556-
super.addListener(listener);
1557-
assert(hasListeners);
1558-
if (!hadListener && node._canRequestFocus) {
1559-
_ancestorsAllowFocus = node._adjustListeningNodeCountForAncestors(1);
1560-
}
1561-
}
1562-
1563-
@override
1564-
void removeListener(VoidCallback listener) {
1565-
final bool hadListener = hasListeners;
1566-
super.removeListener(listener);
1567-
if (node._canRequestFocus && hadListener && !hasListeners) {
1568-
_ancestorsAllowFocus = node._adjustListeningNodeCountForAncestors(-1);
1569-
}
1570-
}
1571-
}
1572-
15731417
/// An enum to describe which kind of focus highlight behavior to use when
15741418
/// displaying focus information.
15751419
enum FocusHighlightMode {

0 commit comments

Comments
 (0)