fix: add parameter to maintainState of SearchDelegate (#152444)

Add parameter to `showSearch`, which is passed to `_SearchPageRoute` to maintain the state of the SearchDelegate.

- fix #43582

_Just contributed this change to start the conversation on how the #43582 can be addressed. Have not added tests yet._
This commit is contained in:
Gazal 2024-08-07 03:53:03 +10:00 committed by GitHub
parent a9b2d8d6d4
commit 6f98b485ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 71 additions and 3 deletions

View File

@ -48,6 +48,10 @@ import 'theme.dart';
/// route created by this method is pushed to the nearest navigator to the /// route created by this method is pushed to the nearest navigator to the
/// given `context`. It can not be `null`. /// given `context`. It can not be `null`.
/// ///
/// The `maintainState` argument is used to determine if the route should remain
/// in memory when it is inactive (see [ModalRoute.maintainState] for more details].
/// By default, `maintainState` is `false`.
///
/// The transition to the search page triggered by this method looks best if the /// The transition to the search page triggered by this method looks best if the
/// screen triggering the transition contains an [AppBar] at the top and the /// screen triggering the transition contains an [AppBar] at the top and the
/// transition is called from an [IconButton] that's part of [AppBar.actions]. /// transition is called from an [IconButton] that's part of [AppBar.actions].
@ -68,11 +72,13 @@ Future<T?> showSearch<T>({
required SearchDelegate<T> delegate, required SearchDelegate<T> delegate,
String? query = '', String? query = '',
bool useRootNavigator = false, bool useRootNavigator = false,
bool maintainState = false,
}) { }) {
delegate.query = query ?? delegate.query; delegate.query = query ?? delegate.query;
delegate._currentBody = _SearchBody.suggestions; delegate._currentBody = _SearchBody.suggestions;
return Navigator.of(context, rootNavigator: useRootNavigator).push(_SearchPageRoute<T>( return Navigator.of(context, rootNavigator: useRootNavigator).push(_SearchPageRoute<T>(
delegate: delegate, delegate: delegate,
maintainState: maintainState
)); ));
} }
@ -417,6 +423,7 @@ enum _SearchBody {
class _SearchPageRoute<T> extends PageRoute<T> { class _SearchPageRoute<T> extends PageRoute<T> {
_SearchPageRoute({ _SearchPageRoute({
required this.delegate, required this.delegate,
required this.maintainState,
}) { }) {
assert( assert(
delegate._route == null, delegate._route == null,
@ -429,6 +436,9 @@ class _SearchPageRoute<T> extends PageRoute<T> {
final SearchDelegate<T> delegate; final SearchDelegate<T> delegate;
@override
final bool maintainState;
@override @override
Color? get barrierColor => null; Color? get barrierColor => null;
@ -438,9 +448,6 @@ class _SearchPageRoute<T> extends PageRoute<T> {
@override @override
Duration get transitionDuration => const Duration(milliseconds: 300); Duration get transitionDuration => const Duration(milliseconds: 300);
@override
bool get maintainState => false;
@override @override
Widget buildTransitions( Widget buildTransitions(
BuildContext context, BuildContext context,

View File

@ -1175,6 +1175,63 @@ void main() {
await tester.pump(); await tester.pump();
expect(textField.controller!.text.length, 15); expect(textField.controller!.text.length, 15);
}, skip: kIsWeb); // [intended] We do not use Flutter-rendered context menu on the Web. }, skip: kIsWeb); // [intended] We do not use Flutter-rendered context menu on the Web.
testWidgets('showSearch with maintainState on the route', (WidgetTester tester) async {
final _MyNavigatorObserver navigationObserver = _MyNavigatorObserver();
final _TestEmptySearchDelegate delegate = _TestEmptySearchDelegate();
addTearDown(delegate.dispose);
await tester.pumpWidget(MaterialApp(
navigatorObservers: <NavigatorObserver>[navigationObserver],
home: Builder(builder: (BuildContext context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
onPressed: () async {
await showSearch(
context: context,
delegate: delegate,
);
},
child: const Text('showSearch'),
),
TextButton(
onPressed: () async {
await showSearch(
context: context,
delegate: delegate,
maintainState: true,
);
},
child: const Text('showSearchWithMaintainState'),
),
],
)),
));
expect(navigationObserver.pushCount, 0);
expect(navigationObserver.maintainState, false);
// showSearch normal and back.
await tester.tap(find.text('showSearch'));
await tester.pumpAndSettle();
final Finder backButtonFinder = find.byType(BackButton);
expect(backButtonFinder, findsWidgets);
await tester.tap(find.byTooltip('Close'));
await tester.pumpAndSettle();
expect(navigationObserver.pushCount, 1);
expect(navigationObserver.maintainState, false);
// showSearch with maintainState.
await tester.tap(find.text('showSearchWithMaintainState'));
await tester.pumpAndSettle();
expect(backButtonFinder, findsWidgets);
await tester.tap(find.byTooltip('Close'));
await tester.pumpAndSettle();
expect(navigationObserver.pushCount, 2);
expect(navigationObserver.maintainState, true);
});
} }
class TestHomePage extends StatelessWidget { class TestHomePage extends StatelessWidget {
@ -1358,6 +1415,7 @@ class _TestEmptySearchDelegate extends SearchDelegate<String> {
} }
class _MyNavigatorObserver extends NavigatorObserver { class _MyNavigatorObserver extends NavigatorObserver {
bool maintainState = false;
int pushCount = 0; int pushCount = 0;
@override @override
@ -1366,6 +1424,9 @@ class _MyNavigatorObserver extends NavigatorObserver {
if (<String>['nested', '/'].contains(route.settings.name)) { if (<String>['nested', '/'].contains(route.settings.name)) {
return; return;
} }
if (route is PageRoute) {
maintainState = route.maintainState;
}
pushCount++; pushCount++;
} }
} }