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
This commit is contained in:
parent
78aa105666
commit
99ccff3bb6
@ -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 {
|
|||||||
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 {
|
|||||||
/// 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
|
|||||||
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
|
|||||||
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
|
|||||||
@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() {
|
|||||||
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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user