FocusNode and FocusManager should dispatch creation in constructor. (#133352)

This commit is contained in:
Polina Cherkasova 2023-08-28 09:38:19 -07:00 committed by GitHub
parent cf91262f75
commit 703d60b5e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 20 deletions

View File

@ -471,6 +471,7 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
void dispose() { void dispose() {
_historyEntry?.remove(); _historyEntry?.remove();
_controller.dispose(); _controller.dispose();
_focusScopeNode.dispose();
super.dispose(); super.dispose();
} }

View File

@ -437,6 +437,10 @@ class FocusNode with DiagnosticableTreeMixin, ChangeNotifier {
_descendantsAreTraversable = descendantsAreTraversable { _descendantsAreTraversable = descendantsAreTraversable {
// Set it via the setter so that it does nothing on release builds. // Set it via the setter so that it does nothing on release builds.
this.debugLabel = debugLabel; this.debugLabel = debugLabel;
if (kFlutterMemoryAllocationsEnabled) {
maybeDispatchObjectCreation();
}
} }
/// If true, tells the focus traversal policy to skip over this node for /// If true, tells the focus traversal policy to skip over this node for
@ -1463,6 +1467,9 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
/// handlers, callers must call [registerGlobalHandlers]. See the /// handlers, callers must call [registerGlobalHandlers]. See the
/// documentation in that method for caveats to watch out for. /// documentation in that method for caveats to watch out for.
FocusManager() { FocusManager() {
if (kFlutterMemoryAllocationsEnabled) {
maybeDispatchObjectCreation();
}
rootScope._manager = this; rootScope._manager = this;
} }

View File

@ -1609,6 +1609,41 @@ void main() {
tester.binding.focusManager.removeListener(handleFocusChange); tester.binding.focusManager.removeListener(handleFocusChange);
}); });
test('$FocusManager dispatches object creation in constructor', () {
final List<ObjectEvent> events = <ObjectEvent>[];
void listener(ObjectEvent event) {
if (event.object.runtimeType == FocusManager) {
events.add(event);
}
}
MemoryAllocations.instance.addListener(listener);
final FocusManager focusManager = FocusManager();
expect(events, hasLength(1));
focusManager.dispose();
MemoryAllocations.instance.removeListener(listener);
});
test('$FocusNode dispatches object creation in constructor', () {
final List<ObjectEvent> events = <ObjectEvent>[];
void listener(ObjectEvent event) {
if (event.object.runtimeType == FocusNode) {
events.add(event);
}
}
MemoryAllocations.instance.addListener(listener);
final FocusNode focusManager = FocusNode();
expect(events, hasLength(1));
focusManager.dispose();
MemoryAllocations.instance.removeListener(listener);
});
testWidgets('FocusManager notifies listeners when a widget loses focus because it was removed.', (WidgetTester tester) async { testWidgets('FocusManager notifies listeners when a widget loses focus because it was removed.', (WidgetTester tester) async {
final FocusNode nodeA = FocusNode(debugLabel: 'a'); final FocusNode nodeA = FocusNode(debugLabel: 'a');
final FocusNode nodeB = FocusNode(debugLabel: 'b'); final FocusNode nodeB = FocusNode(debugLabel: 'b');

View File

@ -6,20 +6,26 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
int _creations = 0;
int _disposals = 0;
void main() { void main() {
final MemoryAllocations ma = MemoryAllocations.instance; final MemoryAllocations ma = MemoryAllocations.instance;
setUp(() {
assert(!ma.hasListeners);
});
test('Publishers dispatch events in debug mode', () async { test('Publishers dispatch events in debug mode', () async {
int eventCount = 0; void listener(ObjectEvent event) {
void listener(ObjectEvent event) => eventCount++; if (event is ObjectDisposed) {
_disposals++;
}
if (event is ObjectCreated) {
_creations++;
}
}
ma.addListener(listener); ma.addListener(listener);
final int expectedEventCount = await _activateFlutterObjectsAndReturnCountOfEvents(); final _EventStats actual = await _activateFlutterObjectsAndReturnCountOfEvents();
expect(eventCount, expectedEventCount); expect(actual.creations, _creations);
expect(actual.disposals, _disposals);
ma.removeListener(listener); ma.removeListener(listener);
expect(ma.hasListeners, isFalse); expect(ma.hasListeners, isFalse);
@ -29,6 +35,8 @@ void main() {
bool stateCreated = false; bool stateCreated = false;
bool stateDisposed = false; bool stateDisposed = false;
expect(ma.hasListeners, false);
void listener(ObjectEvent event) { void listener(ObjectEvent event) {
if (event is ObjectCreated && event.object is State) { if (event is ObjectCreated && event.object is State) {
stateCreated = true; stateCreated = true;
@ -47,7 +55,7 @@ void main() {
expect(stateCreated, isTrue); expect(stateCreated, isTrue);
expect(stateDisposed, isTrue); expect(stateDisposed, isTrue);
ma.removeListener(listener); ma.removeListener(listener);
expect(ma.hasListeners, isFalse); expect(ma.hasListeners, false);
}); });
} }
@ -62,7 +70,8 @@ class _TestElement extends RenderObjectElement with RootElementMixin {
_TestElement(): super(_TestLeafRenderObjectWidget()); _TestElement(): super(_TestLeafRenderObjectWidget());
void makeInactive() { void makeInactive() {
assignOwner(BuildOwner(focusManager: FocusManager())); final FocusManager newFocusManager = FocusManager();
assignOwner(BuildOwner(focusManager: newFocusManager));
mount(null, null); mount(null, null);
deactivate(); deactivate();
} }
@ -109,15 +118,22 @@ class _TestStatefulWidgetState extends State<_TestStatefulWidget> {
} }
} }
/// Create and dispose Flutter objects to fire memory allocation events.
Future<int> _activateFlutterObjectsAndReturnCountOfEvents() async {
int count = 0;
final _TestElement element = _TestElement(); count++; class _EventStats {
final RenderObject renderObject = _TestRenderObject(); count++; int creations = 0;
int disposals = 0;
element.makeInactive(); element.unmount(); count += 3; }
renderObject.dispose(); count++;
/// Create and dispose Flutter objects to fire memory allocation events.
return count; Future<_EventStats> _activateFlutterObjectsAndReturnCountOfEvents() async {
final _EventStats result = _EventStats();
final _TestElement element = _TestElement(); result.creations++;
final RenderObject renderObject = _TestRenderObject(); result.creations++;
element.makeInactive(); result.creations += 3; // 1 for the new BuildOwner, 1 for the new FocusManager, 1 for the new FocusScopeNode
element.unmount(); result.disposals += 2; // 1 for the old BuildOwner, 1 for the element
renderObject.dispose(); result.disposals += 1;
return result;
} }