blob: 0ebae1fad735f2b6b01ee09230dbf2506dc5f415 [file] [log] [blame]
// 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));
});
});
}