blob: 74137bf0105494fad3daf9c042afcdd24fbd817d [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('SliverAppBar - Stretch', () {
testWidgets('fills overscroll', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
stretch: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverScrollingPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
await slowDrag(tester, anchor, const Offset(0.0, 100));
expect(header.child!.size.height, equals(200.0));
});
testWidgets('fills overscroll after reverse direction input - scrolling header', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
title: Text('Test'),
stretch: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverScrollingPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
expect(tester.getCenter(find.text('Test')).dy, 28.0);
// First scroll the header away
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(anchor)));
await gesture.moveBy(const Offset(0.0, -100.0));
await tester.pump(const Duration(milliseconds: 10));
expect(header.child!.size.height, equals(56.0));
expect(tester.getCenter(find.text('Test', skipOffstage: false)).dy, -28.0);
// With the same gesture, scroll back and into overscroll
await gesture.moveBy(const Offset(0.0, 200.0));
await tester.pump(const Duration(milliseconds: 10));
// Header should stretch in overscroll
expect(header.child!.size.height, equals(200.0));
expect(tester.getCenter(find.text('Test')).dy, 28.0);
await gesture.up();
await tester.pumpAndSettle();
});
testWidgets('fills overscroll after reverse direction input - floating header', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
title: Text('Test'),
stretch: true,
floating: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverFloatingPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
expect(tester.getCenter(find.text('Test')).dy, 28.0);
// First scroll the header away
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(anchor)));
await gesture.moveBy(const Offset(0.0, -100.0));
await tester.pump(const Duration(milliseconds: 10));
expect(header.child!.size.height, equals(56.0));
expect(tester.getCenter(find.text('Test', skipOffstage: false)).dy, -28.0);
// With the same gesture, scroll back and into overscroll
await gesture.moveBy(const Offset(0.0, 200.0));
await tester.pump(const Duration(milliseconds: 10));
// Header should stretch in overscroll
expect(header.child!.size.height, equals(200.0));
expect(tester.getCenter(find.text('Test')).dy, 28.0);
await gesture.up();
await tester.pumpAndSettle();
});
testWidgets('does not stretch without overscroll physics', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const ClampingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
stretch: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverScrollingPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
await slowDrag(tester, anchor, const Offset(0.0, 100.0));
expect(header.child!.size.height, equals(100.0));
});
testWidgets('default trigger offset', (WidgetTester tester) async {
bool didTrigger = false;
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
stretch: true,
expandedHeight: 100.0,
onStretchTrigger: () async {
didTrigger = true;
},
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
await slowDrag(tester, anchor, const Offset(0.0, 50.0));
expect(didTrigger, isFalse);
await tester.pumpAndSettle();
await slowDrag(tester, anchor, const Offset(0.0, 150.0));
expect(didTrigger, isTrue);
});
testWidgets('custom trigger offset', (WidgetTester tester) async {
bool didTrigger = false;
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
stretch: true,
expandedHeight: 100.0,
stretchTriggerOffset: 150.0,
onStretchTrigger: () async {
didTrigger = true;
},
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
await slowDrag(tester, anchor, const Offset(0.0, 100.0));
await tester.pumpAndSettle();
expect(didTrigger, isFalse);
await slowDrag(tester, anchor, const Offset(0.0, 300.0));
expect(didTrigger, isTrue);
});
testWidgets('stretch callback not triggered without overscroll physics', (WidgetTester tester) async {
bool didTrigger = false;
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const ClampingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
stretch: true,
expandedHeight: 100.0,
stretchTriggerOffset: 150.0,
onStretchTrigger: () async {
didTrigger = true;
},
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
await slowDrag(tester, anchor, const Offset(0.0, 100.0));
await tester.pumpAndSettle();
expect(didTrigger, isFalse);
await slowDrag(tester, anchor, const Offset(0.0, 300.0));
expect(didTrigger, isFalse);
});
testWidgets('asserts reasonable trigger offset', (WidgetTester tester) async {
expect(
() {
return MaterialApp(
home: CustomScrollView(
physics: const ClampingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
stretch: true,
expandedHeight: 100.0,
stretchTriggerOffset: -150.0,
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
);
},
throwsAssertionError,
);
});
});
group('SliverAppBar - Stretch, Pinned', () {
testWidgets('fills overscroll', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
pinned: true,
stretch: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverPinnedPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
await slowDrag(tester, anchor, const Offset(0.0, 100));
expect(header.child!.size.height, equals(200.0));
});
testWidgets('does not stretch without overscroll physics', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const ClampingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
pinned: true,
stretch: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverPinnedPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
await slowDrag(tester, anchor, const Offset(0.0, 100));
expect(header.child!.size.height, equals(100.0));
});
});
group('SliverAppBar - Stretch, Floating', () {
testWidgets('fills overscroll', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
floating: true,
stretch: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverFloatingPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
await slowDrag(tester, anchor, const Offset(0.0, 100));
expect(header.child!.size.height, equals(200.0));
});
testWidgets('does not fill overscroll without proper physics', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const ClampingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
floating: true,
stretch: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverFloatingPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
await slowDrag(tester, anchor, const Offset(0.0, 100));
expect(header.child!.size.height, equals(100.0));
});
});
group('SliverAppBar - Stretch, Floating, Pinned', () {
testWidgets('fills overscroll', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
floating: true,
pinned: true,
stretch: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverFloatingPinnedPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
await slowDrag(tester, anchor, const Offset(0.0, 100));
expect(header.child!.size.height, equals(200.0));
});
testWidgets('does not fill overscroll without proper physics', (WidgetTester tester) async {
const Key anchor = Key('drag');
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
physics: const ClampingScrollPhysics(),
slivers: <Widget>[
const SliverAppBar(
pinned: true,
floating: true,
stretch: true,
expandedHeight: 100.0,
),
SliverToBoxAdapter(
child: Container(
key: anchor,
height: 800,
),
),
SliverToBoxAdapter(
child: Container(
height: 800,
),
),
],
),
),
);
final RenderSliverFloatingPinnedPersistentHeader header = tester.renderObject(
find.byType(SliverAppBar),
);
expect(header.child!.size.height, equals(100.0));
await slowDrag(tester, anchor, const Offset(0.0, 100));
expect(header.child!.size.height, equals(100.0));
});
});
}
Future<void> slowDrag(WidgetTester tester, Key widget, Offset offset) async {
final Offset target = tester.getCenter(find.byKey(widget));
final TestGesture gesture = await tester.startGesture(target);
await gesture.moveBy(offset);
await tester.pump(const Duration(milliseconds: 10));
await gesture.up();
}