refactor(focus): centralize focus node ownership

This commit is contained in:
edde746
2026-07-12 08:42:20 +02:00
parent c58013b818
commit df7fa08e40
5 changed files with 123 additions and 26 deletions
@@ -0,0 +1,71 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:plezy/focus/owned_focus_node_binding.dart';
void main() {
test('switching external nodes detaches the previous listener without disposing it', () {
final first = _TrackingFocusNode();
final second = _TrackingFocusNode();
void listener() {}
final binding = OwnedFocusNodeBinding();
binding.bind(externalNode: first, listener: listener);
binding.bind(externalNode: second, listener: listener);
expect(first.addedListeners, 1);
expect(first.removedListeners, 1);
expect(first.disposeCalls, 0);
expect(second.addedListeners, 1);
binding.dispose();
expect(second.removedListeners, 1);
expect(second.disposeCalls, 0);
first.dispose();
second.dispose();
});
test('switching from an internal node disposes the owned node', () {
late _TrackingFocusNode internal;
final external = _TrackingFocusNode();
final binding = OwnedFocusNodeBinding.withFactory(
(debugLabel) => internal = _TrackingFocusNode(debugLabel: debugLabel),
);
binding.bind(externalNode: null, listener: () {}, debugLabel: 'internal');
binding.bind(externalNode: external, listener: () {});
expect(internal.debugLabel, 'internal');
expect(internal.removedListeners, 1);
expect(internal.disposeCalls, 1);
expect(external.disposeCalls, 0);
binding.dispose();
external.dispose();
});
}
class _TrackingFocusNode extends FocusNode {
_TrackingFocusNode({super.debugLabel});
int addedListeners = 0;
int removedListeners = 0;
int disposeCalls = 0;
@override
void addListener(VoidCallback listener) {
addedListeners++;
super.addListener(listener);
}
@override
void removeListener(VoidCallback listener) {
removedListeners++;
super.removeListener(listener);
}
@override
void dispose() {
disposeCalls++;
super.dispose();
}
}