SearchBar should listen to changes to the SearchController and update suggestions on change (#134337)

This PR is adding a listener to the `SearchController`, so that the `suggestionBuilder` can be updated when the `SearchController` changes. 

Another method we can possibly do is  have an update method on `SearchController`, so that the user can manually update the suggestions instead of us doing it automatically.

Fixes #132161
This commit is contained in:
Bryan Olivares 2023-10-11 17:55:38 -07:00 committed by GitHub
parent 3e60999b11
commit d9c0d3ae51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 97 additions and 1 deletions

View File

@ -673,11 +673,19 @@ class _ViewContentState extends State<_ViewContent> {
super.initState();
_viewRect = widget.viewRect;
_controller = widget.searchController;
_controller.addListener(updateSuggestions);
if (!_focusNode.hasFocus) {
_focusNode.requestFocus();
}
}
@override
void dispose(){
_controller.removeListener(updateSuggestions);
super.dispose();
}
@override
void didUpdateWidget(covariant _ViewContent oldWidget) {
super.didUpdateWidget(oldWidget);
@ -737,7 +745,6 @@ class _ViewContentState extends State<_ViewContent> {
icon: const Icon(Icons.close),
onPressed: () {
_controller.clear();
updateSuggestions();
},
),
];

View File

@ -1490,6 +1490,95 @@ void main() {
expect(controller.value.text, suggestion);
});
testWidgets('SearchAnchor should update suggestions on changes to search controller', (WidgetTester tester) async {
final SearchController controller = SearchController();
const List<String> suggestions = <String>['foo','far','bim'];
addTearDown(controller.dispose);
await tester.pumpWidget(MaterialApp(
home: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Align(
alignment: Alignment.topCenter,
child: SearchAnchor(
searchController: controller,
builder: (BuildContext context, SearchController controller) {
return const Icon(Icons.search);
},
suggestionsBuilder: (BuildContext context, SearchController controller) {
final String searchText = controller.text.toLowerCase();
if (searchText.isEmpty) {
return const <Widget>[
Center(
child: Text('No Search'),
),
];
}
final Iterable<String> filterSuggestions = suggestions.where(
(String suggestion) => suggestion.toLowerCase().contains(searchText),
);
return filterSuggestions.map((String suggestion) {
return ListTile(
title: Text(suggestion),
trailing: IconButton(
icon: const Icon(Icons.call_missed),
onPressed: () {
controller.text = suggestion;
},
),
onTap: () {
controller.closeView(suggestion);
},
);
}).toList();
},
),
),
);
}
),
));
await tester.tap(find.byIcon(Icons.search));
await tester.pumpAndSettle();
final Finder listTile1 = find.widgetWithText(ListTile, 'foo');
final Finder listTile2 = find.widgetWithText(ListTile, 'far');
final Finder listTile3 = find.widgetWithText(ListTile, 'bim');
final Finder textWidget = find.widgetWithText(Center, 'No Search');
final Finder iconInListTile1 = find.descendant(of: listTile1, matching: find.byIcon(Icons.call_missed));
expect(textWidget,findsOneWidget);
expect(listTile1, findsNothing);
expect(listTile2, findsNothing);
expect(listTile3, findsNothing);
await tester.enterText(find.byType(SearchBar), 'f');
await tester.pumpAndSettle();
expect(textWidget,findsNothing);
expect(listTile1, findsOneWidget);
expect(listTile2, findsOneWidget);
expect(listTile3, findsNothing);
await tester.tap(iconInListTile1);
await tester.pumpAndSettle();
expect(controller.value.text, 'foo');
expect(textWidget,findsNothing);
expect(listTile1, findsOneWidget);
expect(listTile2, findsNothing);
expect(listTile3, findsNothing);
await tester.tap(listTile1);
await tester.pumpAndSettle();
expect(controller.isOpen, false);
expect(controller.value.text, 'foo');
expect(textWidget,findsNothing);
expect(listTile1, findsNothing);
expect(listTile2, findsNothing);
expect(listTile3, findsNothing);
});
testWidgets('SearchAnchor suggestionsBuilder property could be async', (WidgetTester tester) async {
final SearchController controller = SearchController();
const String suggestion = 'suggestion text';