mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Fixes Router transaction to respect operation order (#149763)
fixes https://github.com/flutter/flutter/issues/142393 The issue is that if routerdelegate mutate its currentConfiguration outside of Router's workflow, the change will be propagate back to routeinformationprovider at the end of the frame. However if another things trigger router's dependencies change within the same frame. The dependencies will trigger a reparse of the current route which would end up override the currentConfiguration in routerDelegate. This change introduce a transaction system that each operation will be add to the end of the transaction future so everything will be execute inorder
This commit is contained in:
parent
ff0ab6ba45
commit
c1064da21b
@ -694,8 +694,9 @@ class _RouterState<T> extends State<Router<T>> with RestorationMixin {
|
||||
// The super.didChangeDependencies may have parsed the route information.
|
||||
// This can happen if the didChangeDependencies is triggered by state
|
||||
// restoration or first build.
|
||||
if (widget.routeInformationProvider != null && _routeParsePending) {
|
||||
_processRouteInformation(widget.routeInformationProvider!.value, () => widget.routerDelegate.setNewRoutePath);
|
||||
final RouteInformation? currentRouteInformation = _routeInformation.value ?? widget.routeInformationProvider?.value;
|
||||
if (currentRouteInformation != null && _routeParsePending) {
|
||||
_processRouteInformation(currentRouteInformation, () => widget.routerDelegate.setNewRoutePath);
|
||||
}
|
||||
_routeParsePending = false;
|
||||
_maybeNeedToReportRouteInformation();
|
||||
|
||||
@ -41,6 +41,44 @@ void main() {
|
||||
expect(find.text('update'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Router respects update order', (WidgetTester tester) async {
|
||||
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
|
||||
addTearDown(provider.dispose);
|
||||
provider.value = RouteInformation(
|
||||
uri: Uri.parse('initial'),
|
||||
);
|
||||
|
||||
final MutableRouterDelegate delegate = MutableRouterDelegate();
|
||||
addTearDown(delegate.dispose);
|
||||
|
||||
final ValueNotifier<int> notifier = ValueNotifier<int>(0);
|
||||
await tester.pumpWidget(buildBoilerPlate(
|
||||
IntInheritedNotifier(
|
||||
notifier: notifier,
|
||||
child: Router<RouteInformation>(
|
||||
routeInformationProvider: provider,
|
||||
routeInformationParser: CustomRouteInformationParser(
|
||||
(RouteInformation information, BuildContext context) {
|
||||
IntInheritedNotifier.of(context); // create dependency
|
||||
return information;
|
||||
},
|
||||
),
|
||||
routerDelegate: delegate,
|
||||
),
|
||||
)
|
||||
));
|
||||
expect(find.text('initial'), findsOneWidget);
|
||||
expect(delegate.currentConfiguration!.uri.toString(), 'initial');
|
||||
|
||||
delegate.updateConfiguration(RouteInformation(uri: Uri.parse('update')));
|
||||
notifier.value = 1;
|
||||
|
||||
// The delegate should still retain the update.
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('update'), findsOneWidget);
|
||||
expect(delegate.currentConfiguration!.uri.toString(), 'update');
|
||||
});
|
||||
|
||||
testWidgets('Simple router basic functionality - asynchronized', (WidgetTester tester) async {
|
||||
final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider();
|
||||
addTearDown(provider.dispose);
|
||||
@ -1934,3 +1972,43 @@ class RedirectingInformationParser extends RouteInformationParser<RouteInformati
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
class MutableRouterDelegate extends RouterDelegate<RouteInformation> with ChangeNotifier {
|
||||
MutableRouterDelegate() {
|
||||
if (kFlutterMemoryAllocationsEnabled) {
|
||||
ChangeNotifier.maybeDispatchObjectCreation(this);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
RouteInformation? currentConfiguration;
|
||||
|
||||
@override
|
||||
Future<void> setNewRoutePath(RouteInformation configuration) {
|
||||
currentConfiguration = configuration;
|
||||
return SynchronousFuture<void>(null);
|
||||
}
|
||||
|
||||
void updateConfiguration(RouteInformation newConfig) {
|
||||
currentConfiguration = newConfig;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> popRoute() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(currentConfiguration?.uri.toString() ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
class IntInheritedNotifier extends InheritedNotifier<ValueListenable<int>> {
|
||||
const IntInheritedNotifier({super.key, required super.notifier, required super.child});
|
||||
|
||||
static int of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<IntInheritedNotifier>()!.notifier!.value;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user