fix changinternalstate crash when remove local history entry in final… (#52561)

This commit is contained in:
chunhtai 2020-03-23 13:36:01 -07:00 committed by GitHub
parent fed6887aa0
commit 2a8e7b7bf1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 2 deletions

View file

@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'basic.dart';
import 'focus_manager.dart';
@ -606,8 +607,18 @@ mixin LocalHistoryRoute<T> on Route<T> {
_localHistory.remove(entry);
entry._owner = null;
entry._notifyRemoved();
if (_localHistory.isEmpty)
changedInternalState();
if (_localHistory.isEmpty) {
if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) {
// The local history might be removed as a result of disposing inactive
// elements during finalizeTree. The state is locked at this moment, and
// we can only notify state has changed in the next frame.
SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
changedInternalState();
});
} else {
changedInternalState();
}
}
}
@override

View file

@ -1294,6 +1294,28 @@ void main() {
// It should refocus page one after pops.
expect(focusNodeOnPageOne.hasFocus, isTrue);
});
testWidgets('child with local history can be disposed', (WidgetTester tester) async {
// Regression test: https://github.com/flutter/flutter/issues/52478
await tester.pumpWidget(MaterialApp(
home: WidgetWithLocalHistory(),
));
final WidgetWithLocalHistoryState state = tester.state(find.byType(WidgetWithLocalHistory));
state.addLocalHistory();
// Waits for modal route to update its internal state;
await tester.pump();
// Pumps a new widget to dispose WidgetWithLocalHistory. This should cause
// it to remove the local history entry from modal route during
// finalizeTree.
await tester.pumpWidget(const MaterialApp(
home: Text('dummy'),
));
// Waits for modal route to update its internal state;
await tester.pump();
expect(tester.takeException(), null);
});
});
}
@ -1390,3 +1412,29 @@ class _TestDialogRouteWithCustomBarrierCurve<T> extends PopupRoute<T> {
);
}
}
class WidgetWithLocalHistory extends StatefulWidget {
@override
WidgetWithLocalHistoryState createState() => WidgetWithLocalHistoryState();
}
class WidgetWithLocalHistoryState extends State<WidgetWithLocalHistory> {
LocalHistoryEntry _localHistory;
void addLocalHistory() {
final ModalRoute<dynamic> route = ModalRoute.of(context);
_localHistory = LocalHistoryEntry();
route.addLocalHistoryEntry(_localHistory);
}
@override
void dispose() {
super.dispose();
_localHistory.remove();
}
@override
Widget build(BuildContext context) {
return const Text('dummy');
}
}