mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
fix changinternalstate crash when remove local history entry in final… (#52561)
This commit is contained in:
parent
fed6887aa0
commit
2a8e7b7bf1
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue