Skip to content

Commit f31dae2

Browse files
authored
Clip search view content during animation (#126975)
Fixes #126590 This PR is to clip the view content when the view size is not big enough, such as during animation. Also removes some unused properties in `_ViewContent` class: `getRect` and `viewConstraints`
1 parent 8089a30 commit f31dae2

File tree

2 files changed

+102
-55
lines changed

2 files changed

+102
-55
lines changed

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

Lines changed: 62 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -579,11 +579,10 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
579579
viewHeaderTextStyle: viewHeaderTextStyle,
580580
viewHeaderHintStyle: viewHeaderHintStyle,
581581
dividerColor: dividerColor,
582-
viewConstraints: viewConstraints,
583582
showFullScreenView: showFullScreenView,
584583
animation: curvedAnimation,
585-
getRect: getRect,
586584
topPadding: topPadding,
585+
viewMaxWidth: _rectTween.end!.width,
587586
viewRect: viewRect,
588587
viewDefaults: viewDefaults,
589588
viewTheme: viewTheme,
@@ -616,11 +615,10 @@ class _ViewContent extends StatefulWidget {
616615
this.viewHeaderTextStyle,
617616
this.viewHeaderHintStyle,
618617
this.dividerColor,
619-
this.viewConstraints,
620618
required this.showFullScreenView,
621-
required this.getRect,
622619
required this.topPadding,
623620
required this.animation,
621+
required this.viewMaxWidth,
624622
required this.viewRect,
625623
required this.viewDefaults,
626624
required this.viewTheme,
@@ -641,11 +639,10 @@ class _ViewContent extends StatefulWidget {
641639
final TextStyle? viewHeaderTextStyle;
642640
final TextStyle? viewHeaderHintStyle;
643641
final Color? dividerColor;
644-
final BoxConstraints? viewConstraints;
645642
final bool showFullScreenView;
646-
final ValueGetter<Rect?> getRect;
647643
final double topPadding;
648644
final Animation<double> animation;
645+
final double viewMaxWidth;
649646
final Rect viewRect;
650647
final SearchViewThemeData viewDefaults;
651648
final SearchViewThemeData viewTheme;
@@ -690,9 +687,11 @@ class _ViewContentState extends State<_ViewContent> {
690687
result = widget.suggestionsBuilder(context, _controller);
691688
final Size updatedScreenSize = MediaQuery.of(context).size;
692689

693-
if (_screenSize != updatedScreenSize && widget.showFullScreenView) {
690+
if (_screenSize != updatedScreenSize) {
694691
_screenSize = updatedScreenSize;
695-
_viewRect = Offset.zero & _screenSize!;
692+
if (widget.showFullScreenView) {
693+
_viewRect = Offset.zero & _screenSize!;
694+
}
696695
}
697696
}
698697

@@ -782,56 +781,64 @@ class _ViewContentState extends State<_ViewContent> {
782781
color: effectiveBackgroundColor,
783782
surfaceTintColor: effectiveSurfaceTint,
784783
elevation: effectiveElevation,
785-
child: FadeTransition(
786-
opacity: CurvedAnimation(
787-
parent: widget.animation,
788-
curve: _kViewIconsFadeOnInterval,
789-
reverseCurve: _kViewIconsFadeOnInterval.flipped,
790-
),
791-
child: Column(
792-
crossAxisAlignment: CrossAxisAlignment.stretch,
793-
children: <Widget>[
794-
Padding(
795-
padding: EdgeInsets.only(top: widget.topPadding),
796-
child: SafeArea(
797-
top: false,
798-
bottom: false,
799-
child: SearchBar(
800-
constraints: widget.showFullScreenView ? BoxConstraints(minHeight: _SearchViewDefaultsM3.fullScreenBarHeight) : null,
801-
focusNode: _focusNode,
802-
leading: widget.viewLeading ?? defaultLeading,
803-
trailing: widget.viewTrailing ?? defaultTrailing,
804-
hintText: widget.viewHintText,
805-
backgroundColor: const MaterialStatePropertyAll<Color>(Colors.transparent),
806-
overlayColor: const MaterialStatePropertyAll<Color>(Colors.transparent),
807-
elevation: const MaterialStatePropertyAll<double>(0.0),
808-
textStyle: MaterialStatePropertyAll<TextStyle?>(effectiveTextStyle),
809-
hintStyle: MaterialStatePropertyAll<TextStyle?>(effectiveHintStyle),
810-
controller: _controller,
811-
onChanged: (_) {
812-
updateSuggestions();
813-
},
814-
),
815-
),
784+
child: ClipRect(
785+
clipBehavior: Clip.antiAlias,
786+
child: OverflowBox(
787+
alignment: Alignment.topLeft,
788+
maxWidth: math.min(widget.viewMaxWidth, _screenSize!.width),
789+
minWidth: 0,
790+
child: FadeTransition(
791+
opacity: CurvedAnimation(
792+
parent: widget.animation,
793+
curve: _kViewIconsFadeOnInterval,
794+
reverseCurve: _kViewIconsFadeOnInterval.flipped,
816795
),
817-
FadeTransition(
818-
opacity: CurvedAnimation(
819-
parent: widget.animation,
820-
curve: _kViewDividerFadeOnInterval,
821-
reverseCurve: _kViewFadeOnInterval.flipped,
822-
),
823-
child: viewDivider),
824-
Expanded(
825-
child: FadeTransition(
826-
opacity: CurvedAnimation(
827-
parent: widget.animation,
828-
curve: _kViewListFadeOnInterval,
829-
reverseCurve: _kViewListFadeOnInterval.flipped,
796+
child: Column(
797+
crossAxisAlignment: CrossAxisAlignment.stretch,
798+
children: <Widget>[
799+
Padding(
800+
padding: EdgeInsets.only(top: widget.topPadding),
801+
child: SafeArea(
802+
top: false,
803+
bottom: false,
804+
child: SearchBar(
805+
constraints: widget.showFullScreenView ? BoxConstraints(minHeight: _SearchViewDefaultsM3.fullScreenBarHeight) : null,
806+
focusNode: _focusNode,
807+
leading: widget.viewLeading ?? defaultLeading,
808+
trailing: widget.viewTrailing ?? defaultTrailing,
809+
hintText: widget.viewHintText,
810+
backgroundColor: const MaterialStatePropertyAll<Color>(Colors.transparent),
811+
overlayColor: const MaterialStatePropertyAll<Color>(Colors.transparent),
812+
elevation: const MaterialStatePropertyAll<double>(0.0),
813+
textStyle: MaterialStatePropertyAll<TextStyle?>(effectiveTextStyle),
814+
hintStyle: MaterialStatePropertyAll<TextStyle?>(effectiveHintStyle),
815+
controller: _controller,
816+
onChanged: (_) {
817+
updateSuggestions();
818+
},
819+
),
820+
),
830821
),
831-
child: viewBuilder(result),
832-
),
822+
FadeTransition(
823+
opacity: CurvedAnimation(
824+
parent: widget.animation,
825+
curve: _kViewDividerFadeOnInterval,
826+
reverseCurve: _kViewFadeOnInterval.flipped,
827+
),
828+
child: viewDivider),
829+
Expanded(
830+
child: FadeTransition(
831+
opacity: CurvedAnimation(
832+
parent: widget.animation,
833+
curve: _kViewListFadeOnInterval,
834+
reverseCurve: _kViewListFadeOnInterval.flipped,
835+
),
836+
child: viewBuilder(result),
837+
),
838+
),
839+
],
833840
),
834-
],
841+
),
835842
),
836843
),
837844
),

packages/flutter/test/material/search_anchor_test.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,46 @@ void main() {
16391639
await tester.pumpAndSettle();
16401640
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
16411641
});
1642+
1643+
testWidgets('Search view route does not throw exception during pop animation', (WidgetTester tester) async {
1644+
// regression test for https://github.com/flutter/flutter/issues/126590.
1645+
await tester.pumpWidget(MaterialApp(
1646+
home: Material(
1647+
child: Center(
1648+
child: SearchAnchor(
1649+
builder: (BuildContext context, SearchController controller) {
1650+
return IconButton(
1651+
icon: const Icon(Icons.search),
1652+
onPressed: () {
1653+
controller.openView();
1654+
},
1655+
);
1656+
},
1657+
suggestionsBuilder: (BuildContext context, SearchController controller) {
1658+
return List<Widget>.generate(5, (int index) {
1659+
final String item = 'item $index';
1660+
return ListTile(
1661+
leading: const Icon(Icons.history),
1662+
title: Text(item),
1663+
trailing: const Icon(Icons.chevron_right),
1664+
onTap: () {},
1665+
);
1666+
});
1667+
}),
1668+
),
1669+
),
1670+
));
1671+
1672+
// Open search view
1673+
await tester.tap(find.byIcon(Icons.search));
1674+
await tester.pumpAndSettle();
1675+
1676+
// Pop search view route
1677+
await tester.tap(find.byIcon(Icons.arrow_back));
1678+
await tester.pumpAndSettle();
1679+
1680+
// No exception.
1681+
});
16421682
}
16431683

16441684
TextStyle? _iconStyle(WidgetTester tester, IconData icon) {

0 commit comments

Comments
 (0)