Skip to content

Commit d9c0d3a

Browse files
authored
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
1 parent 3e60999 commit d9c0d3a

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

packages/flutter/lib/src/material/search_anchor.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,11 +673,19 @@ class _ViewContentState extends State<_ViewContent> {
673673
super.initState();
674674
_viewRect = widget.viewRect;
675675
_controller = widget.searchController;
676+
_controller.addListener(updateSuggestions);
677+
676678
if (!_focusNode.hasFocus) {
677679
_focusNode.requestFocus();
678680
}
679681
}
680682

683+
@override
684+
void dispose(){
685+
_controller.removeListener(updateSuggestions);
686+
super.dispose();
687+
}
688+
681689
@override
682690
void didUpdateWidget(covariant _ViewContent oldWidget) {
683691
super.didUpdateWidget(oldWidget);
@@ -737,7 +745,6 @@ class _ViewContentState extends State<_ViewContent> {
737745
icon: const Icon(Icons.close),
738746
onPressed: () {
739747
_controller.clear();
740-
updateSuggestions();
741748
},
742749
),
743750
];

packages/flutter/test/material/search_anchor_test.dart

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,95 @@ void main() {
14901490
expect(controller.value.text, suggestion);
14911491
});
14921492

1493+
testWidgets('SearchAnchor should update suggestions on changes to search controller', (WidgetTester tester) async {
1494+
final SearchController controller = SearchController();
1495+
const List<String> suggestions = <String>['foo','far','bim'];
1496+
addTearDown(controller.dispose);
1497+
1498+
await tester.pumpWidget(MaterialApp(
1499+
home: StatefulBuilder(
1500+
builder: (BuildContext context, StateSetter setState) {
1501+
return Material(
1502+
child: Align(
1503+
alignment: Alignment.topCenter,
1504+
child: SearchAnchor(
1505+
searchController: controller,
1506+
builder: (BuildContext context, SearchController controller) {
1507+
return const Icon(Icons.search);
1508+
},
1509+
suggestionsBuilder: (BuildContext context, SearchController controller) {
1510+
final String searchText = controller.text.toLowerCase();
1511+
if (searchText.isEmpty) {
1512+
return const <Widget>[
1513+
Center(
1514+
child: Text('No Search'),
1515+
),
1516+
];
1517+
}
1518+
final Iterable<String> filterSuggestions = suggestions.where(
1519+
(String suggestion) => suggestion.toLowerCase().contains(searchText),
1520+
);
1521+
return filterSuggestions.map((String suggestion) {
1522+
return ListTile(
1523+
title: Text(suggestion),
1524+
trailing: IconButton(
1525+
icon: const Icon(Icons.call_missed),
1526+
onPressed: () {
1527+
controller.text = suggestion;
1528+
},
1529+
),
1530+
onTap: () {
1531+
controller.closeView(suggestion);
1532+
},
1533+
);
1534+
}).toList();
1535+
},
1536+
),
1537+
),
1538+
);
1539+
}
1540+
),
1541+
));
1542+
1543+
await tester.tap(find.byIcon(Icons.search));
1544+
await tester.pumpAndSettle();
1545+
1546+
final Finder listTile1 = find.widgetWithText(ListTile, 'foo');
1547+
final Finder listTile2 = find.widgetWithText(ListTile, 'far');
1548+
final Finder listTile3 = find.widgetWithText(ListTile, 'bim');
1549+
final Finder textWidget = find.widgetWithText(Center, 'No Search');
1550+
final Finder iconInListTile1 = find.descendant(of: listTile1, matching: find.byIcon(Icons.call_missed));
1551+
1552+
expect(textWidget,findsOneWidget);
1553+
expect(listTile1, findsNothing);
1554+
expect(listTile2, findsNothing);
1555+
expect(listTile3, findsNothing);
1556+
1557+
await tester.enterText(find.byType(SearchBar), 'f');
1558+
await tester.pumpAndSettle();
1559+
expect(textWidget,findsNothing);
1560+
expect(listTile1, findsOneWidget);
1561+
expect(listTile2, findsOneWidget);
1562+
expect(listTile3, findsNothing);
1563+
1564+
await tester.tap(iconInListTile1);
1565+
await tester.pumpAndSettle();
1566+
expect(controller.value.text, 'foo');
1567+
expect(textWidget,findsNothing);
1568+
expect(listTile1, findsOneWidget);
1569+
expect(listTile2, findsNothing);
1570+
expect(listTile3, findsNothing);
1571+
1572+
await tester.tap(listTile1);
1573+
await tester.pumpAndSettle();
1574+
expect(controller.isOpen, false);
1575+
expect(controller.value.text, 'foo');
1576+
expect(textWidget,findsNothing);
1577+
expect(listTile1, findsNothing);
1578+
expect(listTile2, findsNothing);
1579+
expect(listTile3, findsNothing);
1580+
});
1581+
14931582
testWidgets('SearchAnchor suggestionsBuilder property could be async', (WidgetTester tester) async {
14941583
final SearchController controller = SearchController();
14951584
const String suggestion = 'suggestion text';

0 commit comments

Comments
 (0)