| // Copyright 2017 The Chromium 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_test/flutter_test.dart'; |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/widgets.dart'; |
| |
| void main() { |
| testWidgets('SliverFillRemaining - no siblings', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: CustomScrollView( |
| controller: controller, |
| slivers: <Widget>[ |
| SliverFillRemaining(child: Container()), |
| ], |
| ), |
| ), |
| ); |
| expect(tester.renderObject<RenderBox>(find.byType(Container)).size.height, equals(600.0)); |
| |
| controller.jumpTo(50.0); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byType(Container)).size.height, equals(600.0)); |
| |
| controller.jumpTo(-100.0); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byType(Container)).size.height, equals(600.0)); |
| |
| controller.jumpTo(0.0); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byType(Container)).size.height, equals(600.0)); |
| }); |
| |
| testWidgets('SliverFillRemaining - one sibling', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: CustomScrollView( |
| controller: controller, |
| slivers: <Widget>[ |
| const SliverToBoxAdapter(child: SizedBox(height: 100.0)), |
| SliverFillRemaining(child: Container()), |
| ], |
| ), |
| ), |
| ); |
| expect(tester.renderObject<RenderBox>(find.byType(Container)).size.height, equals(500.0)); |
| |
| controller.jumpTo(50.0); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byType(Container)).size.height, equals(550.0)); |
| |
| controller.jumpTo(-100.0); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byType(Container)).size.height, equals(400.0)); // (!) |
| |
| controller.jumpTo(0.0); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byType(Container)).size.height, equals(500.0)); |
| }); |
| |
| group('SliverFillRemaining - hasScrollBody', () { |
| final Widget sliverBox = SliverToBoxAdapter( |
| child: Container( |
| color: Colors.amber, |
| height: 150.0, |
| ), |
| ); |
| Widget boilerplate(List<Widget> slivers, {ScrollController controller}) { |
| return MaterialApp( |
| home: Scaffold( |
| body: CustomScrollView( |
| slivers: slivers, |
| controller: controller, |
| ), |
| ), |
| ); |
| } |
| |
| testWidgets('does not extend past viewport when false', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| final List<Widget> slivers = <Widget>[ |
| sliverBox, |
| SliverFillRemaining( |
| child: Container(color: Colors.white), |
| hasScrollBody: false, |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers, controller: controller)); |
| expect(controller.offset, 0.0); |
| expect(find.byType(Container), findsNWidgets(2)); |
| controller.jumpTo(150.0); |
| await tester.pumpAndSettle(); |
| expect(controller.offset, 0.0); |
| expect(find.byType(Container), findsNWidgets(2)); |
| }); |
| |
| testWidgets('scrolls beyond viewport by default', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| final List<Widget> slivers = <Widget>[ |
| sliverBox, |
| SliverFillRemaining( |
| child: Container(color: Colors.white), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers, controller: controller)); |
| expect(controller.offset, 0.0); |
| expect(find.byType(Container), findsNWidgets(2)); |
| controller.jumpTo(150.0); |
| await tester.pumpAndSettle(); |
| expect(controller.offset, 150.0); |
| expect(find.byType(Container), findsOneWidget); |
| }); |
| |
| // SliverFillRemaining considers child size when hasScrollBody: false |
| testWidgets('child without size is sized by extent when false', (WidgetTester tester) async { |
| final List<Widget> slivers = <Widget>[ |
| sliverBox, |
| SliverFillRemaining( |
| hasScrollBody: false, |
| child: Container(color: Colors.blue), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers)); |
| final RenderBox box = tester.renderObject<RenderBox>(find.byType(Container).last); |
| expect(box.size.height, equals(450)); |
| }); |
| |
| testWidgets('child with size is sized by extent when false', (WidgetTester tester) async { |
| final GlobalKey key = GlobalKey(); |
| final List<Widget> slivers = <Widget>[ |
| sliverBox, |
| SliverFillRemaining( |
| hasScrollBody: false, |
| child: Container( |
| key: key, |
| color: Colors.blue, |
| child: Align( |
| alignment: Alignment.bottomCenter, |
| child: RaisedButton( |
| child: const Text('bottomCenter button'), |
| onPressed: () {}, |
| ), |
| ), |
| ), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers)); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, equals(450)); |
| |
| // Also check that the button alignment is true to expectations |
| final Finder button = find.byType(RaisedButton); |
| expect(tester.getBottomLeft(button).dy, equals(600.0)); |
| expect(tester.getCenter(button).dx, equals(400.0)); |
| }); |
| |
| testWidgets('extent is overridden by child with larger size when false', (WidgetTester tester) async { |
| final List<Widget> slivers = <Widget>[ |
| sliverBox, |
| SliverFillRemaining( |
| hasScrollBody: false, |
| child: Container( |
| color: Colors.blue, |
| height: 600, |
| ), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers)); |
| final RenderBox box = tester.renderObject<RenderBox>(find.byType(Container).last); |
| expect(box.size.height, equals(600)); |
| }); |
| |
| testWidgets('extent is overridden by child size if precedingScrollExtent > viewportMainAxisExtent when false', (WidgetTester tester) async { |
| final GlobalKey key = GlobalKey(); |
| final List<Widget> slivers = <Widget>[ |
| SliverFixedExtentList( |
| itemExtent: 150, |
| delegate: SliverChildBuilderDelegate( |
| (BuildContext context, int index) => Container(color: Colors.amber), |
| childCount: 5, |
| ), |
| ), |
| SliverFillRemaining( |
| hasScrollBody: false, |
| child: Container( |
| key: key, |
| color: Colors.blue[300], |
| child: Align( |
| alignment: Alignment.center, |
| child: Padding( |
| padding: const EdgeInsets.all(50.0), |
| child: RaisedButton( |
| child: const Text('center button'), |
| onPressed: () {}, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers)); |
| await tester.drag(find.byType(Scrollable), const Offset(0.0, -750.0)); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, equals(148.0)); |
| |
| // Also check that the button alignment is true to expectations |
| final Finder button = find.byType(RaisedButton); |
| expect(tester.getBottomLeft(button).dy, equals(550.0)); |
| expect(tester.getCenter(button).dx, equals(400.0)); |
| }); |
| |
| // iOS/Similar scroll physics when hasScrollBody: false & fillOverscroll: true behavior |
| testWidgets('child without size is sized by extent and overscroll', (WidgetTester tester) async { |
| debugDefaultTargetPlatformOverride = TargetPlatform.iOS; |
| final List<Widget> slivers = <Widget>[ |
| sliverBox, |
| SliverFillRemaining( |
| hasScrollBody: false, |
| fillOverscroll: true, |
| child: Container(color: Colors.blue), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers)); |
| final RenderBox box1 = tester.renderObject<RenderBox>(find.byType(Container).last); |
| expect(box1.size.height, equals(450)); |
| |
| await tester.drag(find.byType(Scrollable), const Offset(0.0, -50.0)); |
| await tester.pump(); |
| final RenderBox box2 = tester.renderObject<RenderBox>(find.byType(Container).last); |
| expect(box2.size.height, greaterThan(450)); |
| debugDefaultTargetPlatformOverride = null; |
| }); |
| |
| testWidgets('child with size is overridden and sized by extent and overscroll', (WidgetTester tester) async { |
| debugDefaultTargetPlatformOverride = TargetPlatform.iOS; |
| final GlobalKey key = GlobalKey(); |
| final List<Widget> slivers = <Widget>[ |
| sliverBox, |
| SliverFillRemaining( |
| hasScrollBody: false, |
| fillOverscroll: true, |
| child: Container( |
| key: key, |
| color: Colors.blue, |
| child: Align( |
| alignment: Alignment.bottomCenter, |
| child: RaisedButton( |
| child: const Text('bottomCenter button'), |
| onPressed: () {}, |
| ), |
| ), |
| ), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers)); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, equals(450)); |
| |
| await tester.drag(find.byType(Scrollable), const Offset(0.0, -50.0)); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, greaterThan(450)); |
| |
| // Also check that the button alignment is true to expectations, even with |
| // child stretching to fill overscroll |
| final Finder button = find.byType(RaisedButton); |
| expect(tester.getBottomLeft(button).dy, equals(600.0)); |
| expect(tester.getCenter(button).dx, equals(400.0)); |
| debugDefaultTargetPlatformOverride = null; |
| }); |
| |
| testWidgets('extent is overridden by child size and overscroll if precedingScrollExtent > viewportMainAxisExtent', (WidgetTester tester) async { |
| debugDefaultTargetPlatformOverride = TargetPlatform.iOS; |
| final GlobalKey key = GlobalKey(); |
| final ScrollController controller = ScrollController(); |
| final List<Widget> slivers = <Widget>[ |
| SliverFixedExtentList( |
| itemExtent: 150, |
| delegate: SliverChildBuilderDelegate( |
| (BuildContext context, int index) => Container(color: Colors.amber), |
| childCount: 5, |
| ), |
| ), |
| SliverFillRemaining( |
| hasScrollBody: false, |
| fillOverscroll: true, |
| child: Container( |
| key: key, |
| color: Colors.blue[300], |
| child: Align( |
| alignment: Alignment.center, |
| child: Padding( |
| padding: const EdgeInsets.all(50.0), |
| child: RaisedButton( |
| child: const Text('center button'), |
| onPressed: () {}, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers, controller: controller)); |
| // Scroll to the end |
| controller.jumpTo(controller.position.maxScrollExtent); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, equals(148.0)); |
| // Check that the button alignment is true to expectations |
| final Finder button = find.byType(RaisedButton); |
| expect(tester.getBottomLeft(button).dy, equals(550.0)); |
| expect(tester.getCenter(button).dx, equals(400.0)); |
| debugDefaultTargetPlatformOverride = null; |
| |
| // Drag for overscroll |
| await tester.drag(find.byType(Scrollable), const Offset(0.0, -50.0)); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, greaterThan(148.0)); |
| |
| // Check that the button alignment is still centered in stretched child |
| expect(tester.getBottomLeft(button).dy, lessThan(550.0)); |
| expect(tester.getCenter(button).dx, equals(400.0)); |
| debugDefaultTargetPlatformOverride = null; |
| }); |
| |
| // Android/Other scroll physics when hasScrollBody: false, ignores fillOverscroll: true |
| testWidgets('child without size is sized by extent, fillOverscroll is ignored', (WidgetTester tester) async { |
| final List<Widget> slivers = <Widget>[ |
| sliverBox, |
| SliverFillRemaining( |
| hasScrollBody: false, |
| fillOverscroll: true, |
| child: Container(color: Colors.blue), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers)); |
| final RenderBox box1 = tester.renderObject<RenderBox>(find.byType(Container).last); |
| expect(box1.size.height, equals(450)); |
| |
| await tester.drag(find.byType(Scrollable), const Offset(0.0, -50.0)); |
| await tester.pump(); |
| final RenderBox box2 = tester.renderObject<RenderBox>(find.byType(Container).last); |
| expect(box2.size.height, equals(450)); |
| }); |
| |
| testWidgets('child with size is overridden and sized by extent, fillOverscroll is ignored', (WidgetTester tester) async { |
| final GlobalKey key = GlobalKey(); |
| final List<Widget> slivers = <Widget>[ |
| sliverBox, |
| SliverFillRemaining( |
| hasScrollBody: false, |
| fillOverscroll: true, |
| child: Container( |
| key: key, |
| color: Colors.blue, |
| child: Align( |
| alignment: Alignment.bottomCenter, |
| child: RaisedButton( |
| child: const Text('bottomCenter button'), |
| onPressed: () {}, |
| ), |
| ), |
| ), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers)); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, equals(450)); |
| |
| await tester.drag(find.byType(Scrollable), const Offset(0.0, -50.0)); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, equals(450)); |
| |
| // Also check that the button alignment is true to expectations |
| final Finder button = find.byType(RaisedButton); |
| expect(tester.getBottomLeft(button).dy, equals(600.0)); |
| expect(tester.getCenter(button).dx, equals(400.0)); |
| }); |
| |
| testWidgets('extent is overridden by child size if precedingScrollExtent > viewportMainAxisExtent, fillOverscroll is ignored', (WidgetTester tester) async { |
| final GlobalKey key = GlobalKey(); |
| final ScrollController controller = ScrollController(); |
| final List<Widget> slivers = <Widget>[ |
| SliverFixedExtentList( |
| itemExtent: 150, |
| delegate: SliverChildBuilderDelegate( |
| (BuildContext context, int index) => Container(color: Colors.amber), |
| childCount: 5, |
| ), |
| ), |
| SliverFillRemaining( |
| hasScrollBody: false, |
| fillOverscroll: true, |
| child: Container( |
| key: key, |
| color: Colors.blue[300], |
| child: Align( |
| alignment: Alignment.center, |
| child: Padding( |
| padding: const EdgeInsets.all(50.0), |
| child: RaisedButton( |
| child: const Text('center button'), |
| onPressed: () {}, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ]; |
| await tester.pumpWidget(boilerplate(slivers, controller: controller)); |
| // Scroll to the end |
| controller.jumpTo(controller.position.maxScrollExtent); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, equals(148.0)); |
| // Check that the button alignment is true to expectations |
| final Finder button = find.byType(RaisedButton); |
| expect(tester.getBottomLeft(button).dy, equals(550.0)); |
| expect(tester.getCenter(button).dx, equals(400.0)); |
| debugDefaultTargetPlatformOverride = null; |
| |
| await tester.drag(find.byType(Scrollable), const Offset(0.0, -50.0)); |
| await tester.pump(); |
| expect(tester.renderObject<RenderBox>(find.byKey(key)).size.height, equals(148.0)); |
| |
| // Check that the button alignment is still centered in stretched child |
| expect(tester.getBottomLeft(button).dy, equals(550.0)); |
| expect(tester.getCenter(button).dx, equals(400.0)); |
| }); |
| }); |
| } |