Unverified Commit 4c6929d4 authored by Qun Cheng's avatar Qun Cheng Committed by GitHub

Dismiss the docked search view when the window size is changed (#125071)

parent 44738995
...@@ -67,6 +67,11 @@ typedef ViewBuilder = Widget Function(Iterable<Widget> suggestions); ...@@ -67,6 +67,11 @@ typedef ViewBuilder = Widget Function(Iterable<Widget> suggestions);
/// If [builder] returns an Icon, or any un-tappable widgets, we don't have /// If [builder] returns an Icon, or any un-tappable widgets, we don't have
/// to explicitly call [SearchController.openView]. /// to explicitly call [SearchController.openView].
/// ///
/// The search view route will be popped if the window size is changed and the
/// search view route is not in full-screen mode. However, if the search view route
/// is in full-screen mode, changing the window size, such as rotating a mobile
/// device from portrait mode to landscape mode, will not close the search view.
///
/// {@tool dartpad} /// {@tool dartpad}
/// This example shows how to use an IconButton to open a search view in a [SearchAnchor]. /// This example shows how to use an IconButton to open a search view in a [SearchAnchor].
/// It also shows how to use [SearchController] to open or close the search view route. /// It also shows how to use [SearchController] to open or close the search view route.
...@@ -286,6 +291,7 @@ class SearchAnchor extends StatefulWidget { ...@@ -286,6 +291,7 @@ class SearchAnchor extends StatefulWidget {
} }
class _SearchAnchorState extends State<SearchAnchor> { class _SearchAnchorState extends State<SearchAnchor> {
Size? _screenSize;
bool _anchorIsVisible = true; bool _anchorIsVisible = true;
final GlobalKey _anchorKey = GlobalKey(); final GlobalKey _anchorKey = GlobalKey();
bool get _viewIsOpen => !_anchorIsVisible; bool get _viewIsOpen => !_anchorIsVisible;
...@@ -301,6 +307,18 @@ class _SearchAnchorState extends State<SearchAnchor> { ...@@ -301,6 +307,18 @@ class _SearchAnchorState extends State<SearchAnchor> {
_searchController._attach(this); _searchController._attach(this);
} }
@override
void didChangeDependencies() {
super.didChangeDependencies();
final Size updatedScreenSize = MediaQuery.of(context).size;
if (_screenSize != null && _screenSize != updatedScreenSize) {
if (_searchController.isOpen && !getShowFullScreenView()) {
_closeView(null);
}
}
_screenSize = updatedScreenSize;
}
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
...@@ -672,45 +690,9 @@ class _ViewContentState extends State<_ViewContent> { ...@@ -672,45 +690,9 @@ class _ViewContentState extends State<_ViewContent> {
result = widget.suggestionsBuilder(context, _controller); result = widget.suggestionsBuilder(context, _controller);
final Size updatedScreenSize = MediaQuery.of(context).size; final Size updatedScreenSize = MediaQuery.of(context).size;
if (_screenSize != updatedScreenSize) { if (_screenSize != updatedScreenSize && widget.showFullScreenView) {
_screenSize = updatedScreenSize; _screenSize = updatedScreenSize;
setState(() { _viewRect = Offset.zero & _screenSize!;
final Rect anchorRect = widget.getRect() ?? _viewRect;
final BoxConstraints constraints = widget.viewConstraints ?? widget.viewTheme.constraints ?? widget.viewDefaults.constraints!;
final double viewWidth = clampDouble(anchorRect.width, constraints.minWidth, constraints.maxWidth);
final double viewHeight = clampDouble(_screenSize!.height * 2 / 3, constraints.minHeight, constraints.maxHeight);
final Size updatedViewSize = Size(viewWidth, viewHeight);
switch (Directionality.of(context)) {
case TextDirection.ltr:
final double viewLeftToScreenRight = _screenSize!.width - anchorRect.left;
final double viewTopToScreenBottom = _screenSize!.height - anchorRect.top;
// Make sure the search view doesn't go off the screen when the screen
// size is changed. If the search view doesn't fit, move the top-left
// corner of the view to fit the window. If the window is smaller than
// the view, then we resize the view to fit the window.
Offset topLeft = anchorRect.topLeft;
if (viewLeftToScreenRight < viewWidth) {
topLeft = Offset(_screenSize!.width - math.min(viewWidth, _screenSize!.width), anchorRect.top);
}
if (viewTopToScreenBottom < viewHeight) {
topLeft = Offset(topLeft.dx, _screenSize!.height - math.min(viewHeight, _screenSize!.height));
}
_viewRect = topLeft & updatedViewSize;
return;
case TextDirection.rtl:
final double viewTopToScreenBottom = _screenSize!.height - anchorRect.top;
Offset topLeft = Offset(
math.max(anchorRect.right - viewWidth, 0.0),
anchorRect.top,
);
if (viewTopToScreenBottom < viewHeight) {
topLeft = Offset(topLeft.dx, _screenSize!.height - math.min(viewHeight, _screenSize!.height));
}
_viewRect = topLeft & updatedViewSize;
}
});
} }
} }
......
...@@ -1560,6 +1560,84 @@ void main() { ...@@ -1560,6 +1560,84 @@ void main() {
final Rect searchViewRectRTL = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first); final Rect searchViewRectRTL = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
expect(searchViewRectRTL, equals(const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0))); expect(searchViewRectRTL, equals(const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0)));
}); });
testWidgets('Docked search view route is popped if the window size changes', (WidgetTester tester) async {
addTearDown(tester.view.reset);
tester.view.physicalSize = const Size(500.0, 600.0);
tester.view.devicePixelRatio = 1.0;
await tester.pumpWidget(MaterialApp(
home: Material(
child: SearchAnchor(
isFullScreen: false,
builder: (BuildContext context, SearchController controller) {
return Align(
alignment: Alignment.bottomRight,
child: IconButton(
icon: const Icon(Icons.search),
onPressed: () {
controller.openView();
},
),
);
},
suggestionsBuilder: (BuildContext context, SearchController controller) {
return <Widget>[];
},
),
),
));
// Open the search view
await tester.tap(find.byIcon(Icons.search));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
// Change window size
tester.view.physicalSize = const Size(250.0, 200.0);
tester.view.devicePixelRatio = 1.0;
await tester.pumpAndSettle();
expect(find.byIcon(Icons.arrow_back), findsNothing);
});
testWidgets('Full-screen search view route should stay if the window size changes', (WidgetTester tester) async {
addTearDown(tester.view.reset);
tester.view.physicalSize = const Size(500.0, 600.0);
tester.view.devicePixelRatio = 1.0;
await tester.pumpWidget(MaterialApp(
home: Material(
child: SearchAnchor(
isFullScreen: true,
builder: (BuildContext context, SearchController controller) {
return Align(
alignment: Alignment.bottomRight,
child: IconButton(
icon: const Icon(Icons.search),
onPressed: () {
controller.openView();
},
),
);
},
suggestionsBuilder: (BuildContext context, SearchController controller) {
return <Widget>[];
},
),
),
));
// Open a full-screen search view
await tester.tap(find.byIcon(Icons.search));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
// Change window size
tester.view.physicalSize = const Size(250.0, 200.0);
tester.view.devicePixelRatio = 1.0;
await tester.pumpAndSettle();
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
});
} }
TextStyle? _iconStyle(WidgetTester tester, IconData icon) { TextStyle? _iconStyle(WidgetTester tester, IconData icon) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment