Unverified Commit 4fc78b93 authored by Gregory Conrad's avatar Gregory Conrad Committed by GitHub

Fix a memory leak in `AutomaticKeepAlive` (#124163)

Fix a memory leak in `AutomaticKeepAlive`
parent 509c2dde
......@@ -144,7 +144,8 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
}
VoidCallback _createCallback(Listenable handle) {
return () {
late final VoidCallback callback;
return callback = () {
assert(() {
if (!mounted) {
throw FlutterError(
......@@ -157,6 +158,7 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
return true;
}());
_handles!.remove(handle);
handle.removeListener(callback);
if (_handles!.isEmpty) {
if (SchedulerBinding.instance.schedulerPhase.index < SchedulerPhase.persistentCallbacks.index) {
// Build/layout haven't started yet so let's just schedule this for
......
......@@ -557,6 +557,26 @@ void main() {
expect(alternate.children.length, 1);
});
testWidgets('Keep alive Listenable has its listener removed once called', (WidgetTester tester) async {
final LeakCheckerHandle handle = LeakCheckerHandle();
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: ListView.builder(
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return const KeepAliveListenableLeakChecker(key: GlobalObjectKey<_KeepAliveListenableLeakCheckerState>(0));
},
),
));
final _KeepAliveListenableLeakCheckerState state = const GlobalObjectKey<_KeepAliveListenableLeakCheckerState>(0).currentState!;
expect(handle.hasListeners, false);
state.dispatch(handle);
expect(handle.hasListeners, true);
handle.notifyListeners();
expect(handle.hasListeners, false);
});
}
class _AlwaysKeepAlive extends StatefulWidget {
......@@ -633,3 +653,26 @@ class RenderSliverMultiBoxAdaptorAlt extends RenderSliver with
@override
void performLayout() { }
}
class LeakCheckerHandle with ChangeNotifier {
@override
bool get hasListeners => super.hasListeners;
}
class KeepAliveListenableLeakChecker extends StatefulWidget {
const KeepAliveListenableLeakChecker({super.key});
@override
State<KeepAliveListenableLeakChecker> createState() => _KeepAliveListenableLeakCheckerState();
}
class _KeepAliveListenableLeakCheckerState extends State<KeepAliveListenableLeakChecker> {
void dispatch(Listenable handle) {
KeepAliveNotification(handle).dispatch(context);
}
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment