Skip to content

Commit 8703147

Browse files
authored
Correct max height calculation to fade and animate insets on scroll in CupertinoSearchTextField (flutter#166569)
Fixes [CupertinoSliverNavigationBar.search does not fade or animate insets if search view scrolled in automatic mode](flutter#165152) ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent 3d1f041 commit 8703147

File tree

2 files changed

+73
-7
lines changed

2 files changed

+73
-7
lines changed

packages/flutter/lib/src/cupertino/search_field.dart

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:math' as math;
6+
57
import 'package:flutter/services.dart';
68
import 'package:flutter/widgets.dart';
79

@@ -363,8 +365,8 @@ class _CupertinoSearchTextFieldState extends State<CupertinoSearchTextField> wit
363365
TextEditingController get _effectiveController => widget.controller ?? _controller!.value;
364366

365367
ScrollNotificationObserverState? _scrollNotificationObserver;
368+
late double _scaledIconSize;
366369
double _fadeExtent = 0.0;
367-
double? _maxHeight;
368370

369371
@override
370372
void initState() {
@@ -441,12 +443,13 @@ class _CupertinoSearchTextFieldState extends State<CupertinoSearchTextField> wit
441443
}
442444

443445
void _handleScrollNotification(ScrollNotification notification) {
444-
if (_maxHeight == null) {
445-
_maxHeight ??= context.size?.height;
446-
} else if (notification is ScrollUpdateNotification) {
446+
if (notification is ScrollUpdateNotification) {
447447
final double currentHeight = context.size?.height ?? 0.0;
448448
setState(() {
449-
_fadeExtent = _calculateScrollOpacity(currentHeight, _maxHeight!);
449+
_fadeExtent = _calculateScrollOpacity(
450+
currentHeight,
451+
_scaledIconSize + math.max(widget.prefixInsets.vertical, widget.suffixInsets.vertical),
452+
);
450453
});
451454
}
452455
}
@@ -487,7 +490,7 @@ class _CupertinoSearchTextFieldState extends State<CupertinoSearchTextField> wit
487490

488491
// The icon size will be scaled by a factor of the accessibility text scale,
489492
// to follow the behavior of `UISearchTextField`.
490-
final double scaledIconSize = MediaQuery.textScalerOf(context).scale(widget.itemSize);
493+
_scaledIconSize = MediaQuery.textScalerOf(context).scale(widget.itemSize);
491494

492495
// If decoration was not provided, create a decoration with the provided
493496
// background color and border radius.
@@ -500,7 +503,7 @@ class _CupertinoSearchTextFieldState extends State<CupertinoSearchTextField> wit
500503

501504
final IconThemeData iconThemeData = IconThemeData(
502505
color: CupertinoDynamicColor.resolve(widget.itemColor, context),
503-
size: scaledIconSize,
506+
size: _scaledIconSize,
504507
);
505508

506509
final Widget prefix = Opacity(

packages/flutter/test/cupertino/search_field_test.dart

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,4 +749,67 @@ void main() {
749749
lessThan(initialPadding),
750750
);
751751
});
752+
753+
testWidgets('Fades and animates insets on scroll if search field starts out collapsed', (
754+
WidgetTester tester,
755+
) async {
756+
const TextDirection direction = TextDirection.ltr;
757+
const double scrollOffset = 200;
758+
await tester.pumpWidget(
759+
const Directionality(
760+
textDirection: direction,
761+
child: CupertinoApp(
762+
home: CupertinoPageScaffold(
763+
child: CustomScrollView(
764+
slivers: <Widget>[
765+
CupertinoSliverNavigationBar.search(
766+
largeTitle: Text('Large title'),
767+
searchField: CupertinoSearchTextField(),
768+
),
769+
SliverToBoxAdapter(child: SizedBox(height: 1000)),
770+
],
771+
),
772+
),
773+
),
774+
),
775+
);
776+
777+
final Finder searchTextFieldFinder = find.byType(CupertinoSearchTextField);
778+
expect(searchTextFieldFinder, findsOneWidget);
779+
780+
final double searchTextFieldHeight = tester.getSize(searchTextFieldFinder).height;
781+
await tester.tap(find.widgetWithText(CupertinoSearchTextField, 'Search'), warnIfMissed: false);
782+
783+
final TestGesture scrollGesture1 = await tester.startGesture(
784+
tester.getCenter(find.byType(CustomScrollView)),
785+
);
786+
await scrollGesture1.moveBy(const Offset(0, -scrollOffset));
787+
await scrollGesture1.up();
788+
await tester.pumpAndSettle();
789+
790+
expect(find.text('Cancel'), findsOneWidget);
791+
await tester.tap(find.text('Cancel'));
792+
await tester.pumpAndSettle();
793+
794+
final TestGesture scrollGesture2 = await tester.startGesture(
795+
tester.getCenter(find.byType(CustomScrollView)),
796+
);
797+
await scrollGesture2.moveBy(Offset(0, scrollOffset - searchTextFieldHeight / 2));
798+
await scrollGesture2.up();
799+
await tester.pump();
800+
801+
final Finder prefixIconFinder = find.descendant(
802+
of: searchTextFieldFinder,
803+
matching: find.byIcon(CupertinoIcons.search),
804+
);
805+
806+
// The prefix icon has faded.
807+
expect(prefixIconFinder, findsOneWidget);
808+
expect(
809+
tester
810+
.widget<Opacity>(find.ancestor(of: prefixIconFinder, matching: find.byType(Opacity)))
811+
.opacity,
812+
lessThan(1.0),
813+
);
814+
});
752815
}

0 commit comments

Comments
 (0)