Unverified Commit 99ccff3b authored by Daniel Edrisian's avatar Daniel Edrisian Committed by GitHub

Add stretch property to CupertinoSliverNavigationBar (#71707)

* Add stretch property to CupertinoSliverNavigationBar

* Added tests

* Fix trailling spaces

* xster review: fix tests

* xster review: fix tests

* trim extra spaces
parent 78aa1056
...@@ -551,6 +551,11 @@ class _CupertinoNavigationBarState extends State<CupertinoNavigationBar> { ...@@ -551,6 +551,11 @@ class _CupertinoNavigationBarState extends State<CupertinoNavigationBar> {
/// from the operating system can be retrieved in many ways, such as querying /// from the operating system can be retrieved in many ways, such as querying
/// [MediaQuery.textScaleFactorOf] against [CupertinoApp]'s [BuildContext]. /// [MediaQuery.textScaleFactorOf] against [CupertinoApp]'s [BuildContext].
/// ///
/// The [stretch] parameter determines whether the nav bar should stretch to
/// fill the over-scroll area. The nav bar can still expand and contract as the
/// user scrolls, but it will also stretch when the user over-scrolls if the
/// [stretch] value is `true`. Defaults to `true`.
///
/// See also: /// See also:
/// ///
/// * [CupertinoNavigationBar], an iOS navigation bar for use on non-scrolling /// * [CupertinoNavigationBar], an iOS navigation bar for use on non-scrolling
...@@ -575,6 +580,7 @@ class CupertinoSliverNavigationBar extends StatefulWidget { ...@@ -575,6 +580,7 @@ class CupertinoSliverNavigationBar extends StatefulWidget {
this.actionsForegroundColor, this.actionsForegroundColor,
this.transitionBetweenRoutes = true, this.transitionBetweenRoutes = true,
this.heroTag = _defaultHeroTag, this.heroTag = _defaultHeroTag,
this.stretch = true,
}) : assert(automaticallyImplyLeading != null), }) : assert(automaticallyImplyLeading != null),
assert(automaticallyImplyTitle != null), assert(automaticallyImplyTitle != null),
assert( assert(
...@@ -672,6 +678,13 @@ class CupertinoSliverNavigationBar extends StatefulWidget { ...@@ -672,6 +678,13 @@ class CupertinoSliverNavigationBar extends StatefulWidget {
/// True if the navigation bar's background color has no transparency. /// True if the navigation bar's background color has no transparency.
bool get opaque => backgroundColor?.alpha == 0xFF; bool get opaque => backgroundColor?.alpha == 0xFF;
/// Whether the nav bar should stretch to fill the over-scroll area.
///
/// The nav bar can still expand and contract as the user scrolls, but it will
/// also stretch when the user over-scrolls if the [stretch] value is `true`.
/// Defaults to `true`.
final bool stretch;
@override @override
_CupertinoSliverNavigationBarState createState() => _CupertinoSliverNavigationBarState(); _CupertinoSliverNavigationBarState createState() => _CupertinoSliverNavigationBarState();
} }
...@@ -729,6 +742,7 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation ...@@ -729,6 +742,7 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
heroTag: widget.heroTag, heroTag: widget.heroTag,
persistentHeight: _kNavBarPersistentHeight + MediaQuery.of(context).padding.top, persistentHeight: _kNavBarPersistentHeight + MediaQuery.of(context).padding.top,
alwaysShowMiddle: widget.middle != null, alwaysShowMiddle: widget.middle != null,
stretchConfiguration: widget.stretch ? OverScrollHeaderStretchConfiguration() : null,
), ),
), ),
), ),
...@@ -751,6 +765,7 @@ class _LargeTitleNavigationBarSliverDelegate ...@@ -751,6 +765,7 @@ class _LargeTitleNavigationBarSliverDelegate
required this.heroTag, required this.heroTag,
required this.persistentHeight, required this.persistentHeight,
required this.alwaysShowMiddle, required this.alwaysShowMiddle,
required this.stretchConfiguration,
}) : assert(persistentHeight != null), }) : assert(persistentHeight != null),
assert(alwaysShowMiddle != null), assert(alwaysShowMiddle != null),
assert(transitionBetweenRoutes != null); assert(transitionBetweenRoutes != null);
...@@ -774,6 +789,9 @@ class _LargeTitleNavigationBarSliverDelegate ...@@ -774,6 +789,9 @@ class _LargeTitleNavigationBarSliverDelegate
@override @override
double get maxExtent => persistentHeight + _kNavBarLargeTitleHeightExtension; double get maxExtent => persistentHeight + _kNavBarLargeTitleHeightExtension;
@override
OverScrollHeaderStretchConfiguration? stretchConfiguration;
@override @override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
final bool showLargeTitle = shrinkOffset < maxExtent - minExtent - _kNavBarShowLargeTitleThreshold; final bool showLargeTitle = shrinkOffset < maxExtent - minExtent - _kNavBarShowLargeTitleThreshold;
......
...@@ -1185,6 +1185,113 @@ void main() { ...@@ -1185,6 +1185,113 @@ void main() {
expect(barItems2.length, greaterThan(0)); expect(barItems2.length, greaterThan(0));
expect(barItems2.any((RichText t) => t.textScaleFactor != 1), isFalse); expect(barItems2.any((RichText t) => t.textScaleFactor != 1), isFalse);
}); });
testWidgets(
'CupertinoSliverNavigationBar stretches upon over-scroll and bounces back once over-scroll ends',
(WidgetTester tester) async {
const Text trailingText = Text('Bar Button');
const Text titleText = Text('Large Title');
await tester.pumpWidget(
CupertinoApp(
home: CupertinoPageScaffold(
child: CustomScrollView(
slivers: <Widget>[
const CupertinoSliverNavigationBar(
trailing: trailingText,
largeTitle: titleText,
),
SliverToBoxAdapter(
child: Container(
height: 1200.0,
),
),
],
),
),
),
);
final Finder trailingTextFinder = find.byWidget(trailingText).first;
final Finder titleTextFinder = find.byWidget(titleText).first;
final Offset initialTrailingTextToLargeTitleOffset = tester.getTopLeft(trailingTextFinder) - tester.getTopLeft(titleTextFinder);
// Drag for overscroll
await tester.drag(find.byType(Scrollable), const Offset(0.0, 150.0));
await tester.pump();
final Offset stretchedTrailingTextToLargeTitleOffset = tester.getTopLeft(trailingTextFinder) - tester.getTopLeft(titleTextFinder);
expect(
stretchedTrailingTextToLargeTitleOffset.dy.abs(),
greaterThan(initialTrailingTextToLargeTitleOffset.dy.abs())
);
// Ensure overscroll retracts to original size after releasing gesture
await tester.pumpAndSettle();
final Offset finalTrailingTextToLargeTitleOffset = tester.getTopLeft(trailingTextFinder) - tester.getTopLeft(titleTextFinder);
expect(
finalTrailingTextToLargeTitleOffset.dy.abs(),
initialTrailingTextToLargeTitleOffset.dy.abs(),
);
});
testWidgets(
'CupertinoSliverNavigationBar does not stretch upon over-scroll if stretch parameter is false',
(WidgetTester tester) async {
const Text trailingText = Text('Bar Button');
const Text titleText = Text('Large Title');
await tester.pumpWidget(
CupertinoApp(
home: CupertinoPageScaffold(
child: CustomScrollView(
slivers: <Widget>[
const CupertinoSliverNavigationBar(
trailing: trailingText,
largeTitle: titleText,
stretch: false,
),
SliverToBoxAdapter(
child: Container(
height: 1200.0,
),
),
],
),
),
),
);
final Finder trailingTextFinder = find.byWidget(trailingText).first;
final Finder titleTextFinder = find.byWidget(titleText).first;
final Offset initialTrailingTextToLargeTitleOffset = tester.getTopLeft(trailingTextFinder) - tester.getTopLeft(titleTextFinder);
// Drag for overscroll
await tester.drag(find.byType(Scrollable), const Offset(0.0, 150.0));
await tester.pump();
final Offset stretchedTrailingTextToLargeTitleOffset = tester.getTopLeft(trailingTextFinder) - tester.getTopLeft(titleTextFinder);
expect(
stretchedTrailingTextToLargeTitleOffset.dy.abs(),
initialTrailingTextToLargeTitleOffset.dy.abs(),
);
// Ensure overscroll is zero after releasing gesture
await tester.pumpAndSettle();
final Offset finalTrailingTextToLargeTitleOffset = tester.getTopLeft(trailingTextFinder) - tester.getTopLeft(titleTextFinder);
expect(
finalTrailingTextToLargeTitleOffset.dy.abs(),
initialTrailingTextToLargeTitleOffset.dy.abs(),
);
});
} }
class _ExpectStyles extends StatelessWidget { class _ExpectStyles extends StatelessWidget {
......
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