| // 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 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 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 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 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 { |
| var didTrigger = false; |
| const 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 { |
| var didTrigger = false; |
| const 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 { |
| var didTrigger = false; |
| const 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 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 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 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 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 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 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(); |
| } |