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
|
||||
/// [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:
|
||||
///
|
||||
/// * [CupertinoNavigationBar], an iOS navigation bar for use on non-scrolling
|
||||
@ -575,6 +580,7 @@ class CupertinoSliverNavigationBar extends StatefulWidget {
|
||||
this.actionsForegroundColor,
|
||||
this.transitionBetweenRoutes = true,
|
||||
this.heroTag = _defaultHeroTag,
|
||||
this.stretch = true,
|
||||
}) : assert(automaticallyImplyLeading != null),
|
||||
assert(automaticallyImplyTitle != null),
|
||||
assert(
|
||||
@ -672,6 +678,13 @@ class CupertinoSliverNavigationBar extends StatefulWidget {
|
||||
/// True if the navigation bar's background color has no transparency.
|
||||
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
|
||||
_CupertinoSliverNavigationBarState createState() => _CupertinoSliverNavigationBarState();
|
||||
}
|
||||
@ -729,6 +742,7 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
|
||||
heroTag: widget.heroTag,
|
||||
persistentHeight: _kNavBarPersistentHeight + MediaQuery.of(context).padding.top,
|
||||
alwaysShowMiddle: widget.middle != null,
|
||||
stretchConfiguration: widget.stretch ? OverScrollHeaderStretchConfiguration() : null,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -751,6 +765,7 @@ class _LargeTitleNavigationBarSliverDelegate
|
||||
required this.heroTag,
|
||||
required this.persistentHeight,
|
||||
required this.alwaysShowMiddle,
|
||||
required this.stretchConfiguration,
|
||||
}) : assert(persistentHeight != null),
|
||||
assert(alwaysShowMiddle != null),
|
||||
assert(transitionBetweenRoutes != null);
|
||||
@ -774,6 +789,9 @@ class _LargeTitleNavigationBarSliverDelegate
|
||||
@override
|
||||
double get maxExtent => persistentHeight + _kNavBarLargeTitleHeightExtension;
|
||||
|
||||
@override
|
||||
OverScrollHeaderStretchConfiguration? stretchConfiguration;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
final bool showLargeTitle = shrinkOffset < maxExtent - minExtent - _kNavBarShowLargeTitleThreshold;
|
||||
|
@ -1185,6 +1185,113 @@ void main() {
|
||||
expect(barItems2.length, greaterThan(0));
|
||||
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user