diff --git a/packages/flutter/lib/src/material/search.dart b/packages/flutter/lib/src/material/search.dart index 60b8f04f4c..6e9a5a7c6c 100644 --- a/packages/flutter/lib/src/material/search.dart +++ b/packages/flutter/lib/src/material/search.dart @@ -26,8 +26,9 @@ import 'theme.dart'; /// to the empty string. When `query` is set to null, `delegate.query` will /// be used as the initial query. /// -/// The method returns the selected search result, which can be set in the -/// [SearchDelegate.close] call. +/// This method returns the selected search result, which can be set in the +/// [SearchDelegate.close] call. If the search page is closed with the system +/// back button, it returns null. /// /// A given [SearchDelegate] can only be associated with one active [showSearch] /// call. Call [SearchDelegate.close] before re-using the same delegate instance @@ -52,14 +53,11 @@ Future showSearch({ }) { assert(delegate != null); assert(context != null); - assert(delegate._result == null || delegate._result.isCompleted); - delegate._result = new Completer(); delegate.query = query ?? delegate.query; delegate._currentBody = _SearchBody.suggestions; - Navigator.of(context).push(new _SearchPageRoute( + return Navigator.of(context).push(new _SearchPageRoute( delegate: delegate, )); - return delegate._result.future; } /// Delegate for [showSearch] to define the content of the search page. @@ -215,7 +213,6 @@ abstract class SearchDelegate { void close(BuildContext context, T result) { _currentBody = null; _focusNode.unfocus(); - _result.complete(result); Navigator.of(context) ..popUntil((Route route) => route == _route) ..pop(result); @@ -242,8 +239,6 @@ abstract class SearchDelegate { _currentBodyNotifier.value = value; } - Completer _result; - _SearchPageRoute _route; } @@ -263,7 +258,7 @@ enum _SearchBody { } -class _SearchPageRoute extends PageRoute { +class _SearchPageRoute extends PageRoute { _SearchPageRoute({ @required this.delegate, }) : assert(delegate != null) { @@ -323,10 +318,11 @@ class _SearchPageRoute extends PageRoute { } @override - void didComplete(void result) { + void didComplete(T result) { super.didComplete(result); assert(delegate._route == this); delegate._route = null; + delegate._currentBody = null; } } diff --git a/packages/flutter/test/material/search_test.dart b/packages/flutter/test/material/search_test.dart index 8f0b11ad4e..27417e2179 100644 --- a/packages/flutter/test/material/search_test.dart +++ b/packages/flutter/test/material/search_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -42,6 +43,51 @@ void main() { expect(selectedResults, ['Result']); }); + testWidgets('Can close search with system back button to return null', (WidgetTester tester) async { + // regression test for https://github.com/flutter/flutter/issues/18145 + + final _TestSearchDelegate delegate = new _TestSearchDelegate(); + final List selectedResults = []; + + await tester.pumpWidget(new TestHomePage( + delegate: delegate, + results: selectedResults, + )); + + // We are on the homepage + expect(find.text('HomeBody'), findsOneWidget); + expect(find.text('HomeTitle'), findsOneWidget); + expect(find.text('Suggestions'), findsNothing); + + // Open search + await tester.tap(find.byTooltip('Search')); + await tester.pumpAndSettle(); + + expect(find.text('HomeBody'), findsNothing); + expect(find.text('HomeTitle'), findsNothing); + expect(find.text('Suggestions'), findsOneWidget); + + // Simulate system back button + final ByteData message = const JSONMethodCodec().encodeMethodCall(const MethodCall('popRoute')); + await BinaryMessages.handlePlatformMessage('flutter/navigation', message, (_) {}); + await tester.pumpAndSettle(); + + expect(selectedResults, [null]); + + // We are on the homepage again + expect(find.text('HomeBody'), findsOneWidget); + expect(find.text('HomeTitle'), findsOneWidget); + expect(find.text('Suggestions'), findsNothing); + + // Open search again + await tester.tap(find.byTooltip('Search')); + await tester.pumpAndSettle(); + + expect(find.text('HomeBody'), findsNothing); + expect(find.text('HomeTitle'), findsNothing); + expect(find.text('Suggestions'), findsOneWidget); + }); + testWidgets('Requests suggestions', (WidgetTester tester) async { final _TestSearchDelegate delegate = new _TestSearchDelegate(); @@ -414,7 +460,7 @@ void main() { expect(find.text('HomeBody'), findsOneWidget); expect(find.text('Suggestions'), findsNothing); expect(find.text('Nested Suggestions'), findsNothing); - expect(nestedSearchResults, hasLength(0)); + expect(nestedSearchResults, [null]); expect(selectedResults, ['Result Foo']); }); }