Skip to content

Commit

Permalink
Allows page removal that contains Localhistoryentry (flutter#134757)
Browse files Browse the repository at this point in the history
  • Loading branch information
chunhtai authored and Mairramer committed Oct 10, 2023
1 parent ee797e4 commit 248d66c
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 8 deletions.
18 changes: 18 additions & 0 deletions packages/flutter/lib/src/widgets/navigator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2927,6 +2927,10 @@ class _RouteEntry extends RouteTransitionRecord {
final _RestorationInformation? restorationInformation;
final bool pageBased;

/// The limit this route entry will attempt to pop in the case of route being
/// remove as a result of a page update.
static const int kDebugPopAttemptLimit = 100;

static final Route<dynamic> notAnnounced = _NotAnnounced();

_RouteLifecycle currentState;
Expand Down Expand Up @@ -3268,6 +3272,20 @@ class _RouteEntry extends RouteTransitionRecord {
'This route cannot be marked for pop. Either a decision has already been '
'made or it does not require an explicit decision on how to transition out.',
);
// Remove state that prevents a pop, e.g. LocalHistoryEntry[s].
int attempt = 0;
while (route.willHandlePopInternally) {
assert(
() {
attempt += 1;
return attempt < kDebugPopAttemptLimit;
}(),
'Attempted to pop $route $kDebugPopAttemptLimit times, but still failed',
);
final bool popResult = route.didPop(result);
assert(!popResult);

}
pop<dynamic>(result);
_isWaitingForExitingDecision = false;
}
Expand Down
8 changes: 0 additions & 8 deletions packages/flutter/lib/src/widgets/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1832,14 +1832,6 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
];
}

@override
bool get willHandlePopInternally {
final bool popEntriesCanPop = _popEntries.every((PopEntry popEntry) {
return popEntry.canPopNotifier.value;
});
return !popEntriesCanPop || super.willHandlePopInternally;
}

@override
String toString() => '${objectRuntimeType(this, 'ModalRoute')}($settings, animation: $_animation)';
}
Expand Down
84 changes: 84 additions & 0 deletions packages/flutter/test/widgets/navigator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2910,6 +2910,90 @@ void main() {
);
});

testWidgets('Can pop route with local history entries using page api', (WidgetTester tester) async {
List<Page<void>> myPages = const <Page<void>>[
MaterialPage<void>(child: Text('page1')),
MaterialPage<void>(child: Text('page2')),
];
await tester.pumpWidget(
MediaQuery(
data: MediaQueryData.fromView(tester.view),
child: Localizations(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultMaterialLocalizations.delegate,
DefaultWidgetsLocalizations.delegate,
],
child: Navigator(
pages: myPages,
onPopPage: (_, __) => false,
),
),
),
);
expect(find.text('page2'), findsOneWidget);
final ModalRoute<void> route = ModalRoute.of(tester.element(find.text('page2')))!;
bool entryRemoved = false;
route.addLocalHistoryEntry(LocalHistoryEntry(onRemove: () => entryRemoved = true));
expect(route.willHandlePopInternally, true);

myPages = const <Page<void>>[
MaterialPage<void>(child: Text('page1')),
];

await tester.pumpWidget(
MediaQuery(
data: MediaQueryData.fromView(tester.view),
child: Localizations(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultMaterialLocalizations.delegate,
DefaultWidgetsLocalizations.delegate,
],
child: Navigator(
pages: myPages,
onPopPage: (_, __) => false,
),
),
),
);
expect(find.text('page1'), findsOneWidget);
expect(entryRemoved, isTrue);
});

testWidgets('ModalRoute must comply with willHandlePopInternally when there is a PopScope', (WidgetTester tester) async {
const List<Page<void>> myPages = <Page<void>>[
MaterialPage<void>(child: Text('page1')),
MaterialPage<void>(
child: PopScope(
canPop: false,
child: Text('page2'),
),
),
];
await tester.pumpWidget(
MediaQuery(
data: MediaQueryData.fromView(tester.view),
child: Localizations(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultMaterialLocalizations.delegate,
DefaultWidgetsLocalizations.delegate,
],
child: Navigator(
pages: myPages,
onPopPage: (_, __) => false,
),
),
),
);
final ModalRoute<void> route = ModalRoute.of(tester.element(find.text('page2')))!;
// PopScope only prevents user trigger action, e.g. Navigator.maybePop.
// The page can still be popped by the system if it needs to.
expect(route.willHandlePopInternally, false);
expect(route.didPop(null), true);
});

testWidgets('can push and pop pages using page api', (WidgetTester tester) async {
late Animation<double> secondaryAnimationOfRouteOne;
late Animation<double> primaryAnimationOfRouteOne;
Expand Down

0 comments on commit 248d66c

Please sign in to comment.