diff --git a/packages/flutter/lib/src/material/search_anchor.dart b/packages/flutter/lib/src/material/search_anchor.dart index 70184692b95..90187b84bd3 100644 --- a/packages/flutter/lib/src/material/search_anchor.dart +++ b/packages/flutter/lib/src/material/search_anchor.dart @@ -1118,6 +1118,7 @@ class SearchBar extends StatefulWidget { this.leading, this.trailing, this.onTap, + this.onTapOutside, this.onChanged, this.onSubmitted, this.constraints, @@ -1170,6 +1171,9 @@ class SearchBar extends StatefulWidget { /// Called when the user taps this search bar. final GestureTapCallback? onTap; + /// Called when the user taps outside the search bar. + final TapRegionCallback? onTapOutside; + /// Invoked upon user input. final ValueChanged? onChanged; @@ -1397,6 +1401,7 @@ class _SearchBarState extends State { autofocus: widget.autoFocus, onTap: widget.onTap, onTapAlwaysCalled: true, + onTapOutside: widget.onTapOutside, focusNode: _focusNode, onChanged: widget.onChanged, onSubmitted: widget.onSubmitted, diff --git a/packages/flutter/test/material/search_anchor_test.dart b/packages/flutter/test/material/search_anchor_test.dart index 759e466a0d6..4775e858dcd 100644 --- a/packages/flutter/test/material/search_anchor_test.dart +++ b/packages/flutter/test/material/search_anchor_test.dart @@ -3061,6 +3061,48 @@ void main() { expect(box.size.height, 32); }); + testWidgets('Tapping outside searchbar should unfocus the searchbar on mobile', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(debugLabel: 'Test Node'); + addTearDown(focusNode.dispose); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: SearchAnchor( + builder: (BuildContext context, SearchController controller){ + return SearchBar( + controller: controller, + onTap: () { + controller.openView(); + }, + onTapOutside: (PointerDownEvent event) { + focusNode.unfocus(); + }, + onChanged: (_) { + controller.openView(); + }, + autoFocus: true, + focusNode: focusNode, + ); + }, + suggestionsBuilder: (BuildContext context, SearchController controller){ + return List.generate(5, (int index) { + final String item = 'item $index'; + return ListTile(title: Text(item)); + }); + }, + ) + ), + ), + ); + await tester.pump(); + expect(focusNode.hasPrimaryFocus, isTrue); + + await tester.tapAt(const Offset(50, 50)); + await tester.pump(); + + expect(focusNode.hasPrimaryFocus, isFalse); + }, variant: TargetPlatformVariant.mobile()); + testWidgets('The default clear button only shows when text input is not empty ' 'on the search view', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp(