Condense nav bar large title in landscape mode (#166956)

## Rotation demo



https://github.com/user-attachments/assets/b59d6875-dff7-4b40-9525-565dfd8a2554



### Portrait mode .automatic



https://github.com/user-attachments/assets/88f4f3a2-0f13-4c92-b601-20c20e13f7dc



### Landscape mode .automatic



https://github.com/user-attachments/assets/dd5e2373-82e3-41fc-8e83-4002ce5e848e



### Portrait mode .always



https://github.com/user-attachments/assets/623d131a-f71b-430d-b84c-0b4519919f56



### Landscape mode .always



https://github.com/user-attachments/assets/5980e8fe-a981-482d-9f77-97f9ab7495c7



Fixes [CupertinoSliverNavigationBar doesn't become compact in landscape
mode](https://github.com/flutter/flutter/issues/39254)

<details>
<summary>Sample code</summary>

```dart

import 'package:flutter/cupertino.dart';

void main() => runApp(const NavBarBlueApp());

class NavBarBlueApp extends StatelessWidget {
  const NavBarBlueApp({super.key});

  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      theme: CupertinoThemeData(),
      home: MainPage(),
    );
  }
}

class MainPage extends StatelessWidget {
  const MainPage({super.key});

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      child: SafeArea(
        child: CustomScrollView(
          slivers: [
            CupertinoSliverNavigationBar.search(
              stretch: true,
              searchField: CupertinoSearchTextField(
                  suffixMode: OverlayVisibilityMode.always,
                  suffixIcon: Icon(
                    CupertinoIcons.mic_solid,
                  )),
              largeTitle: Text('Lists'),
              bottomMode: NavigationBarBottomMode.always,
            ),
            SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return CupertinoListTile(
                    title: Text('Entry $index'),
                  );
                },
                childCount: 20,
              ),
            ),
          ],
        ),
      ),
    );
  }
}


```

</details>
This commit is contained in:
Victor Sanni 2025-04-30 21:42:56 -07:00 committed by GitHub
parent 651ff0a8f2
commit 96d1b99211
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 141 additions and 15 deletions

View File

@ -12,6 +12,10 @@ void main() {
// The point is to mainly test the cupertino icons that we don't have a // The point is to mainly test the cupertino icons that we don't have a
// dependency against in the flutter/cupertino package directly. // dependency against in the flutter/cupertino package directly.
// Set window orientation to portrait.
tester.view.physicalSize = const Size(2400.0, 3000.0);
addTearDown(tester.view.reset);
final Future<ByteData> font = rootBundle.load( final Future<ByteData> font = rootBundle.load(
'packages/cupertino_icons/assets/CupertinoIcons.ttf', 'packages/cupertino_icons/assets/CupertinoIcons.ttf',
); );

View File

@ -8,10 +8,16 @@ import 'package:flutter_test/flutter_test.dart';
const Offset dragUp = Offset(0.0, -150.0); const Offset dragUp = Offset(0.0, -150.0);
void setWindowToPortrait(WidgetTester tester, {Size size = const Size(2400.0, 3000.0)}) {
tester.view.physicalSize = size;
addTearDown(tester.view.reset);
}
void main() { void main() {
testWidgets('Collapse and expand CupertinoSliverNavigationBar changes title position', ( testWidgets('Collapse and expand CupertinoSliverNavigationBar changes title position', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Large title is visible and at lower position. // Large title is visible and at lower position.
@ -29,6 +35,7 @@ void main() {
testWidgets('Middle widget is visible in both collapsed and expanded states', ( testWidgets('Middle widget is visible in both collapsed and expanded states', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Navigate to a page that has both middle and large titles. // Navigate to a page that has both middle and large titles.
@ -55,6 +62,7 @@ void main() {
testWidgets('CupertinoSliverNavigationBar with previous route has back button', ( testWidgets('CupertinoSliverNavigationBar with previous route has back button', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Navigate to a page that has back button // Navigate to a page that has back button

View File

@ -9,10 +9,16 @@ import 'package:flutter_test/flutter_test.dart';
const Offset titleDragUp = Offset(0.0, -100.0); const Offset titleDragUp = Offset(0.0, -100.0);
const Offset bottomDragUp = Offset(0.0, -50.0); const Offset bottomDragUp = Offset(0.0, -50.0);
void setWindowToPortrait(WidgetTester tester, {Size size = const Size(2400.0, 3000.0)}) {
tester.view.physicalSize = size;
addTearDown(tester.view.reset);
}
void main() { void main() {
testWidgets('Collapse and expand CupertinoSliverNavigationBar changes title position', ( testWidgets('Collapse and expand CupertinoSliverNavigationBar changes title position', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Large title is visible and at lower position. // Large title is visible and at lower position.
@ -28,6 +34,7 @@ void main() {
}); });
testWidgets('Search field is hidden in bottom automatic mode', (WidgetTester tester) async { testWidgets('Search field is hidden in bottom automatic mode', (WidgetTester tester) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Navigate to a page with bottom automatic mode. // Navigate to a page with bottom automatic mode.
@ -64,6 +71,7 @@ void main() {
}); });
testWidgets('Search field is always shown in bottom always mode', (WidgetTester tester) async { testWidgets('Search field is always shown in bottom always mode', (WidgetTester tester) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Navigate to a page with bottom always mode. // Navigate to a page with bottom always mode.
@ -92,6 +100,7 @@ void main() {
}); });
testWidgets('Opens the search view when the search field is tapped', (WidgetTester tester) async { testWidgets('Opens the search view when the search field is tapped', (WidgetTester tester) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Navigate to a page with a search field. // Navigate to a page with a search field.
@ -131,6 +140,7 @@ void main() {
testWidgets('CupertinoSliverNavigationBar with previous route has back button', ( testWidgets('CupertinoSliverNavigationBar with previous route has back button', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Navigate to the first page. // Navigate to the first page.

View File

@ -8,8 +8,14 @@ import 'package:flutter_test/flutter_test.dart';
const Offset dragUp = Offset(0.0, -150.0); const Offset dragUp = Offset(0.0, -150.0);
void setWindowToPortrait(WidgetTester tester, {Size size = const Size(2400.0, 3000.0)}) {
tester.view.physicalSize = size;
addTearDown(tester.view.reset);
}
void main() { void main() {
testWidgets('CupertinoSliverNavigationBar bottom widget', (WidgetTester tester) async { testWidgets('CupertinoSliverNavigationBar bottom widget', (WidgetTester tester) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
final Finder preferredSize = find.byType(PreferredSize); final Finder preferredSize = find.byType(PreferredSize);
@ -24,6 +30,7 @@ void main() {
testWidgets('Collapse and expand CupertinoSliverNavigationBar changes title position', ( testWidgets('Collapse and expand CupertinoSliverNavigationBar changes title position', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Large title is visible and at lower position. // Large title is visible and at lower position.
@ -41,6 +48,7 @@ void main() {
testWidgets('Middle widget is visible in both collapsed and expanded states', ( testWidgets('Middle widget is visible in both collapsed and expanded states', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Navigate to a page that has both middle and large titles. // Navigate to a page that has both middle and large titles.
@ -67,6 +75,7 @@ void main() {
testWidgets('CupertinoSliverNavigationBar with previous route has back button', ( testWidgets('CupertinoSliverNavigationBar with previous route has back button', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await tester.pumpWidget(const example.SliverNavBarApp()); await tester.pumpWidget(const example.SliverNavBarApp());
// Navigate to a page that has a back button. // Navigate to a page that has a back button.

View File

@ -1149,11 +1149,13 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
late _NavigationBarStaticComponentsKeys keys; late _NavigationBarStaticComponentsKeys keys;
ScrollableState? _scrollableState; ScrollableState? _scrollableState;
_NavigationBarSearchField? preferredSizeSearchField; _NavigationBarSearchField? preferredSizeSearchField;
Widget? effectiveMiddle;
late AnimationController _animationController; late AnimationController _animationController;
late CurvedAnimation _searchAnimation; late CurvedAnimation _searchAnimation;
late Animation<double> persistentHeightAnimation; late Animation<double> persistentHeightAnimation;
late Animation<double> largeTitleHeightAnimation; late Animation<double> largeTitleHeightAnimation;
bool searchIsActive = false; bool searchIsActive = false;
bool isPortrait = true;
@override @override
void initState() { void initState() {
@ -1169,6 +1171,14 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait;
final Tween<double> largeTitleHeightTween = Tween<double>(
begin: isPortrait ? _kNavBarLargeTitleHeightExtension : 0.0,
end: 0.0,
);
largeTitleHeightAnimation = largeTitleHeightTween.animate(_animationController);
effectiveMiddle = widget.middle ?? (isPortrait ? null : widget.largeTitle);
_scrollableState?.position.isScrollingNotifier.removeListener(_handleScrollChange); _scrollableState?.position.isScrollingNotifier.removeListener(_handleScrollChange);
_scrollableState = Scrollable.maybeOf(context); _scrollableState = Scrollable.maybeOf(context);
_scrollableState?.position.isScrollingNotifier.addListener(_handleScrollChange); _scrollableState?.position.isScrollingNotifier.addListener(_handleScrollChange);
@ -1203,11 +1213,6 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
); );
persistentHeightAnimation = persistentHeightTween.animate(_animationController) persistentHeightAnimation = persistentHeightTween.animate(_animationController)
..addStatusListener(_handleSearchFieldStatusChanged); ..addStatusListener(_handleSearchFieldStatusChanged);
final Tween<double> largeTitleHeightTween = Tween<double>(
begin: _kNavBarLargeTitleHeightExtension,
end: 0.0,
);
largeTitleHeightAnimation = largeTitleHeightTween.animate(_animationController);
} }
void _handleScrollChange() { void _handleScrollChange() {
@ -1221,16 +1226,17 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
widget.bottomMode == NavigationBarBottomMode.always ? 0.0 : _bottomHeight; widget.bottomMode == NavigationBarBottomMode.always ? 0.0 : _bottomHeight;
final bool canScrollBottom = final bool canScrollBottom =
(widget._searchable || widget.bottom != null) && bottomScrollOffset > 0.0; (widget._searchable || widget.bottom != null) && bottomScrollOffset > 0.0;
final double effectiveLargeTitleHeight = isPortrait ? _kNavBarLargeTitleHeightExtension : 0.0;
// Snap the scroll view to a target determined by the navigation bar's // Snap the scroll view to a target determined by the navigation bar's
// position. // position.
if (canScrollBottom && position.pixels < bottomScrollOffset) { if (canScrollBottom && position.pixels < bottomScrollOffset) {
target = position.pixels > bottomScrollOffset / 2 ? bottomScrollOffset : 0.0; target = position.pixels > bottomScrollOffset / 2 ? bottomScrollOffset : 0.0;
} else if (position.pixels > bottomScrollOffset && } else if (position.pixels > bottomScrollOffset &&
position.pixels < bottomScrollOffset + _kNavBarLargeTitleHeightExtension) { position.pixels < bottomScrollOffset + effectiveLargeTitleHeight) {
target = target =
position.pixels > bottomScrollOffset + (_kNavBarLargeTitleHeightExtension / 2) position.pixels > bottomScrollOffset + (effectiveLargeTitleHeight / 2)
? bottomScrollOffset + _kNavBarLargeTitleHeightExtension ? bottomScrollOffset + effectiveLargeTitleHeight
: bottomScrollOffset; : bottomScrollOffset;
} }
@ -1280,7 +1286,7 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
automaticallyImplyLeading: widget.automaticallyImplyLeading, automaticallyImplyLeading: widget.automaticallyImplyLeading,
automaticallyImplyTitle: widget.automaticallyImplyTitle, automaticallyImplyTitle: widget.automaticallyImplyTitle,
previousPageTitle: widget.previousPageTitle, previousPageTitle: widget.previousPageTitle,
userMiddle: _animationController.isAnimating ? const Text('') : widget.middle, userMiddle: _animationController.isAnimating ? const Text('') : effectiveMiddle,
userTrailing: userTrailing:
widget.trailing != null widget.trailing != null
? Visibility(visible: !searchIsActive, child: widget.trailing!) ? Visibility(visible: !searchIsActive, child: widget.trailing!)
@ -1304,7 +1310,7 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
: widget.bottom) ?? : widget.bottom) ??
const SizedBox.shrink(), const SizedBox.shrink(),
padding: widget.padding, padding: widget.padding,
large: true, large: isPortrait,
staticBar: false, // This one scrolls. staticBar: false, // This one scrolls.
context: context, context: context,
); );
@ -1318,7 +1324,7 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
delegate: _LargeTitleNavigationBarSliverDelegate( delegate: _LargeTitleNavigationBarSliverDelegate(
keys: keys, keys: keys,
components: components, components: components,
userMiddle: widget.middle, userMiddle: effectiveMiddle,
backgroundColor: backgroundColor:
CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ?? CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ??
CupertinoTheme.of(context).barBackgroundColor, CupertinoTheme.of(context).barBackgroundColor,
@ -1331,7 +1337,7 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
heroTag: widget.heroTag, heroTag: widget.heroTag,
persistentHeight: persistentHeightAnimation.value + MediaQuery.paddingOf(context).top, persistentHeight: persistentHeightAnimation.value + MediaQuery.paddingOf(context).top,
largeTitleHeight: largeTitleHeightAnimation.value, largeTitleHeight: largeTitleHeightAnimation.value,
alwaysShowMiddle: widget.alwaysShowMiddle && widget.middle != null, alwaysShowMiddle: widget.alwaysShowMiddle && effectiveMiddle != null,
stretchConfiguration: stretchConfiguration:
widget.stretch && !searchIsActive ? OverScrollHeaderStretchConfiguration() : null, widget.stretch && !searchIsActive ? OverScrollHeaderStretchConfiguration() : null,
enableBackgroundFilterBlur: widget.enableBackgroundFilterBlur, enableBackgroundFilterBlur: widget.enableBackgroundFilterBlur,

View File

@ -16,6 +16,11 @@ import '../widgets/semantics_tester.dart';
int count = 0; int count = 0;
void setWindowToPortrait(WidgetTester tester, {Size size = const Size(2400.0, 3000.0)}) {
tester.view.physicalSize = size;
addTearDown(tester.view.reset);
}
void main() { void main() {
testWidgets('Middle still in center with asymmetrical actions', (WidgetTester tester) async { testWidgets('Middle still in center with asymmetrical actions', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
@ -305,6 +310,7 @@ void main() {
}); });
testWidgets('Can specify custom brightness', (WidgetTester tester) async { testWidgets('Can specify custom brightness', (WidgetTester tester) async {
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
const CupertinoApp( const CupertinoApp(
home: CupertinoNavigationBar( home: CupertinoNavigationBar(
@ -498,6 +504,7 @@ void main() {
}); });
testWidgets('Large title nav bar scrolls', (WidgetTester tester) async { testWidgets('Large title nav bar scrolls', (WidgetTester tester) async {
setWindowToPortrait(tester);
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
await tester.pumpWidget( await tester.pumpWidget(
@ -573,6 +580,7 @@ void main() {
}); });
testWidgets('User specified middle is always visible in sliver', (WidgetTester tester) async { testWidgets('User specified middle is always visible in sliver', (WidgetTester tester) async {
setWindowToPortrait(tester);
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
final Key segmentedControlsKey = UniqueKey(); final Key segmentedControlsKey = UniqueKey();
@ -629,6 +637,7 @@ void main() {
testWidgets( testWidgets(
'User specified middle is only visible when sliver is collapsed if alwaysShowMiddle is false', 'User specified middle is only visible when sliver is collapsed if alwaysShowMiddle is false',
(WidgetTester tester) async { (WidgetTester tester) async {
setWindowToPortrait(tester);
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
await tester.pumpWidget( await tester.pumpWidget(
@ -682,6 +691,7 @@ void main() {
); );
testWidgets('Small title can be overridden', (WidgetTester tester) async { testWidgets('Small title can be overridden', (WidgetTester tester) async {
setWindowToPortrait(tester);
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
await tester.pumpWidget( await tester.pumpWidget(
@ -1171,6 +1181,7 @@ void main() {
}); });
testWidgets('Sliver large title golden', (WidgetTester tester) async { testWidgets('Sliver large title golden', (WidgetTester tester) async {
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: RepaintBoundary( home: RepaintBoundary(
@ -1736,6 +1747,7 @@ void main() {
(WidgetTester tester) async { (WidgetTester tester) async {
const Text trailingText = Text('Bar Button'); const Text trailingText = Text('Bar Button');
const Text titleText = Text('Large Title'); const Text titleText = Text('Large Title');
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -1874,6 +1886,7 @@ void main() {
testWidgets( testWidgets(
'CupertinoSliverNavigationBar magnifies upon over-scroll and shrinks back once over-scroll ends', 'CupertinoSliverNavigationBar magnifies upon over-scroll and shrinks back once over-scroll ends',
(WidgetTester tester) async { (WidgetTester tester) async {
setWindowToPortrait(tester);
const Text titleText = Text('Large Title'); const Text titleText = Text('Large Title');
await tester.pumpWidget( await tester.pumpWidget(
@ -1985,6 +1998,7 @@ void main() {
const double largeTitleHeight = 44.0; const double largeTitleHeight = 44.0;
const double bottomHeight = 10.0; const double bottomHeight = 10.0;
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -2042,6 +2056,7 @@ void main() {
const double persistentHeight = 44.0; const double persistentHeight = 44.0;
const double largeTitleHeight = 44.0; const double largeTitleHeight = 44.0;
const double bottomHeight = 10.0; const double bottomHeight = 10.0;
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -2119,6 +2134,7 @@ void main() {
) async { ) async {
const double bottomHeight = 10.0; const double bottomHeight = 10.0;
const double bottomDisplacement = 96.0; const double bottomDisplacement = 96.0;
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -2166,6 +2182,7 @@ void main() {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
const double largeTitleHeight = 52.0; const double largeTitleHeight = 52.0;
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -2212,6 +2229,7 @@ void main() {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
const double largeTitleHeight = 52.0; const double largeTitleHeight = 52.0;
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -2258,6 +2276,7 @@ void main() {
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
const double largeTitleHeight = 52.0; const double largeTitleHeight = 52.0;
const double bottomHeight = 100.0; const double bottomHeight = 100.0;
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -2323,6 +2342,7 @@ void main() {
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
const double largeTitleHeight = 52.0; const double largeTitleHeight = 52.0;
const double bottomHeight = 100.0; const double bottomHeight = 100.0;
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -2392,6 +2412,7 @@ void main() {
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
const double largeTitleHeight = 52.0; const double largeTitleHeight = 52.0;
const double bottomHeight = 100.0; const double bottomHeight = 100.0;
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -2459,6 +2480,7 @@ void main() {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
addTearDown(scrollController.dispose); addTearDown(scrollController.dispose);
const double largeTitleHeight = 52.0; const double largeTitleHeight = 52.0;
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
@ -2542,6 +2564,7 @@ void main() {
testWidgets('CupertinoSliverNavigationBar.search field collapses nav bar on tap', ( testWidgets('CupertinoSliverNavigationBar.search field collapses nav bar on tap', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
const CupertinoApp( const CupertinoApp(
home: CustomScrollView( home: CustomScrollView(
@ -2609,8 +2632,7 @@ void main() {
}); });
testWidgets('CupertinoSliverNavigationBar.search golden tests', (WidgetTester tester) async { testWidgets('CupertinoSliverNavigationBar.search golden tests', (WidgetTester tester) async {
await tester.binding.setSurfaceSize(const Size(390, 850)); setWindowToPortrait(tester);
addTearDown(() => tester.binding.setSurfaceSize(null));
await tester.pumpWidget( await tester.pumpWidget(
const CupertinoApp( const CupertinoApp(
home: RepaintBoundary( home: RepaintBoundary(
@ -2672,6 +2694,7 @@ void main() {
}); });
testWidgets('onSearchableBottomTap callback', (WidgetTester tester) async { testWidgets('onSearchableBottomTap callback', (WidgetTester tester) async {
setWindowToPortrait(tester);
const Color activeSearchColor = Color(0x0000000A); const Color activeSearchColor = Color(0x0000000A);
const Color inactiveSearchColor = Color(0x0000000B); const Color inactiveSearchColor = Color(0x0000000B);
bool isSearchActive = false; bool isSearchActive = false;
@ -2750,6 +2773,7 @@ void main() {
testWidgets( testWidgets(
'CupertinoSliverNavigationBar.search large title and cancel buttons fade during search animation', 'CupertinoSliverNavigationBar.search large title and cancel buttons fade during search animation',
(WidgetTester tester) async { (WidgetTester tester) async {
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
const CupertinoApp( const CupertinoApp(
home: CustomScrollView( home: CustomScrollView(
@ -2826,6 +2850,52 @@ void main() {
expect(cancelOpacity.opacity.value, 0.0); expect(cancelOpacity.opacity.value, 0.0);
}, },
); );
testWidgets('Large title is hidden if middle is provided in landscape mode', (
WidgetTester tester,
) async {
const String largeTitle = 'Large title';
const String middle = 'Middle';
await tester.pumpWidget(
const CupertinoApp(
home: CustomScrollView(
slivers: <Widget>[
CupertinoSliverNavigationBar.search(
largeTitle: Text(largeTitle),
middle: Text(middle),
searchField: CupertinoSearchTextField(),
),
SliverFillRemaining(child: SizedBox(height: 1000.0)),
],
),
),
);
expect(find.text(largeTitle), findsNothing);
expect(find.text(middle), findsOneWidget);
expect(find.byType(CupertinoSearchTextField), findsOneWidget);
});
testWidgets('Large title is shown in middle position in landscape mode', (
WidgetTester tester,
) async {
const String largeTitle = 'Large title';
await tester.pumpWidget(
const CupertinoApp(
home: CustomScrollView(
slivers: <Widget>[
CupertinoSliverNavigationBar.search(
largeTitle: Text(largeTitle),
searchField: CupertinoSearchTextField(),
),
SliverFillRemaining(child: SizedBox(height: 1000.0)),
],
),
),
);
expect(find.text(largeTitle), findsOneWidget);
expect(find.byType(CupertinoSearchTextField), findsOneWidget);
});
} }
class _ExpectStyles extends StatelessWidget { class _ExpectStyles extends StatelessWidget {

View File

@ -132,6 +132,11 @@ void checkOpacity(WidgetTester tester, Finder finder, double opacity) {
); );
} }
void setWindowToPortrait(WidgetTester tester, {Size size = const Size(2400.0, 3000.0)}) {
tester.view.physicalSize = size;
addTearDown(tester.view.reset);
}
void main() { void main() {
testWidgets('Bottom middle moves between middle and back label', (WidgetTester tester) async { testWidgets('Bottom middle moves between middle and back label', (WidgetTester tester) async {
await startTransitionBetween(tester, fromTitle: 'Page 1'); await startTransitionBetween(tester, fromTitle: 'Page 1');
@ -666,6 +671,7 @@ void main() {
testWidgets('Middle is not shown if alwaysShowMiddle is false and the nav bar is expanded', ( testWidgets('Middle is not shown if alwaysShowMiddle is false and the nav bar is expanded', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
const Widget userMiddle = Placeholder(); const Widget userMiddle = Placeholder();
await startTransitionBetween( await startTransitionBetween(
tester, tester,
@ -987,6 +993,7 @@ void main() {
}); });
testWidgets('Bottom large title moves to top back label', (WidgetTester tester) async { testWidgets('Bottom large title moves to top back label', (WidgetTester tester) async {
setWindowToPortrait(tester);
await startTransitionBetween( await startTransitionBetween(
tester, tester,
from: const CupertinoSliverNavigationBar(), from: const CupertinoSliverNavigationBar(),
@ -1045,6 +1052,7 @@ void main() {
testWidgets('Bottom CupertinoSliverNavigationBar.bottom fades and slides out from the left', ( testWidgets('Bottom CupertinoSliverNavigationBar.bottom fades and slides out from the left', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await startTransitionBetween( await startTransitionBetween(
tester, tester,
from: const CupertinoSliverNavigationBar( from: const CupertinoSliverNavigationBar(
@ -1081,6 +1089,7 @@ void main() {
testWidgets('Bottom CupertinoNavigationBar.bottom fades and slides out from the left', ( testWidgets('Bottom CupertinoNavigationBar.bottom fades and slides out from the left', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await startTransitionBetween( await startTransitionBetween(
tester, tester,
from: const CupertinoNavigationBar( from: const CupertinoNavigationBar(
@ -1117,6 +1126,7 @@ void main() {
testWidgets( testWidgets(
'CupertinoSliverNavigationBar.bottom clips its contents mid-transition when scrolled', 'CupertinoSliverNavigationBar.bottom clips its contents mid-transition when scrolled',
(WidgetTester tester) async { (WidgetTester tester) async {
setWindowToPortrait(tester);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
builder: (BuildContext context, Widget? navigator) { builder: (BuildContext context, Widget? navigator) {
@ -1233,6 +1243,7 @@ void main() {
); );
testWidgets('Long title turns into the word back mid transition', (WidgetTester tester) async { testWidgets('Long title turns into the word back mid transition', (WidgetTester tester) async {
setWindowToPortrait(tester);
await startTransitionBetween( await startTransitionBetween(
tester, tester,
from: const CupertinoSliverNavigationBar(), from: const CupertinoSliverNavigationBar(),
@ -1289,6 +1300,7 @@ void main() {
testWidgets('Bottom large title and top back label transitions their font', ( testWidgets('Bottom large title and top back label transitions their font', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await startTransitionBetween( await startTransitionBetween(
tester, tester,
from: const CupertinoSliverNavigationBar(), from: const CupertinoSliverNavigationBar(),
@ -1397,6 +1409,7 @@ void main() {
}); });
testWidgets('Top large title fades in and slides in from the right', (WidgetTester tester) async { testWidgets('Top large title fades in and slides in from the right', (WidgetTester tester) async {
setWindowToPortrait(tester);
await startTransitionBetween( await startTransitionBetween(
tester, tester,
to: const CupertinoSliverNavigationBar(), to: const CupertinoSliverNavigationBar(),
@ -1427,6 +1440,7 @@ void main() {
testWidgets('Top large title fades in and slides in from the left in RTL', ( testWidgets('Top large title fades in and slides in from the left in RTL', (
WidgetTester tester, WidgetTester tester,
) async { ) async {
setWindowToPortrait(tester);
await startTransitionBetween( await startTransitionBetween(
tester, tester,
to: const CupertinoSliverNavigationBar(), to: const CupertinoSliverNavigationBar(),
@ -1460,6 +1474,7 @@ void main() {
) async { ) async {
const double horizontalPadding = 16.0; // _kNavBarEdgePadding const double horizontalPadding = 16.0; // _kNavBarEdgePadding
const double height = 30.0; const double height = 30.0;
setWindowToPortrait(tester);
await startTransitionBetween( await startTransitionBetween(
tester, tester,
toTitle: 'Page 2', toTitle: 'Page 2',
@ -1503,7 +1518,7 @@ void main() {
// The nav bar bottom is horizontally aligned to the large title. // The nav bar bottom is horizontally aligned to the large title.
expect( expect(
tester.getTopLeft(flying(tester, find.byType(Placeholder))).dx, tester.getTopLeft(flying(tester, find.byType(Placeholder))).dx,
largeTitleOffset.dx - horizontalPadding, moreOrLessEquals(largeTitleOffset.dx - horizontalPadding, epsilon: 0.01),
); );
}); });
@ -1581,6 +1596,7 @@ void main() {
) async { ) async {
int bottomBuildTimes = 0; int bottomBuildTimes = 0;
int topBuildTimes = 0; int topBuildTimes = 0;
setWindowToPortrait(tester);
await startTransitionBetween( await startTransitionBetween(
tester, tester,
from: CupertinoNavigationBar( from: CupertinoNavigationBar(

View File

@ -51,6 +51,9 @@ void main() {
}); });
testWidgets('Large title auto-populates with title', (WidgetTester tester) async { testWidgets('Large title auto-populates with title', (WidgetTester tester) async {
// Set window orientation to portrait.
tester.view.physicalSize = const Size(2400.0, 3000.0);
addTearDown(tester.view.reset);
await tester.pumpWidget(const CupertinoApp(home: Placeholder())); await tester.pumpWidget(const CupertinoApp(home: Placeholder()));
tester tester