SliverFillRemaining accounts for child size when hasScrollBody is false (#35810)
Fixes the hasScrollBody flag not accounting for child size. Adds the ability to specify over-scroll behavior.
This commit is contained in:
parent
252491f8ac
commit
38e41f5aee
@ -7,6 +7,7 @@ import 'dart:math' as math;
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'box.dart';
|
||||
import 'object.dart';
|
||||
import 'sliver.dart';
|
||||
import 'sliver_fixed_extent_list.dart';
|
||||
import 'sliver_multi_box_adaptor.dart';
|
||||
@ -114,34 +115,84 @@ class RenderSliverFillRemaining extends RenderSliverSingleBoxAdapter {
|
||||
RenderSliverFillRemaining({
|
||||
RenderBox child,
|
||||
this.hasScrollBody = true,
|
||||
this.fillOverscroll = false,
|
||||
}) : assert(hasScrollBody != null),
|
||||
super(child: child);
|
||||
|
||||
/// Whether the child has a scrollable body, this value cannot be null.
|
||||
/// Indicates whether the child has a scrollable body, this value cannot be
|
||||
/// null.
|
||||
///
|
||||
/// Defaults to true such that the child will extend beyond the viewport and
|
||||
/// scroll, as seen in [NestedScrollView].
|
||||
///
|
||||
/// Setting this value to false will allow the child to fill the remainder of
|
||||
/// the viewport and not extend further.
|
||||
/// the viewport and not extend further. However, if the
|
||||
/// [precedingScrollExtent] exceeds the size of the viewport, the sliver will
|
||||
/// defer to the child's size rather than overriding it.
|
||||
bool hasScrollBody;
|
||||
|
||||
/// Indicates whether the child should stretch to fill the overscroll area
|
||||
/// created by certain scroll physics, such as iOS' default scroll physics.
|
||||
/// This value cannot be null. This flag is only relevant when the
|
||||
/// [hasScrollBody] value is false.
|
||||
///
|
||||
/// Defaults to false, meaning the default behavior is for the child to
|
||||
/// maintain its size and not extend into the overscroll area.
|
||||
bool fillOverscroll;
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
final double extent = constraints.remainingPaintExtent
|
||||
- math.min(constraints.overlap, 0.0)
|
||||
// Adding the offset for when this SliverFillRemaining is not scrollable,
|
||||
// so it will stretch to fill on overscroll.
|
||||
+ (hasScrollBody ? 0.0 : constraints.scrollOffset);
|
||||
if (child != null)
|
||||
child.layout(constraints.asBoxConstraints(minExtent: extent, maxExtent: extent), parentUsesSize: true);
|
||||
double childExtent;
|
||||
double extent = constraints.viewportMainAxisExtent - constraints.precedingScrollExtent;
|
||||
double maxExtent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0);
|
||||
|
||||
if (hasScrollBody) {
|
||||
extent = maxExtent;
|
||||
if (child != null)
|
||||
child.layout(
|
||||
constraints.asBoxConstraints(
|
||||
minExtent: extent,
|
||||
maxExtent: extent,
|
||||
),
|
||||
parentUsesSize: true,
|
||||
);
|
||||
} else if (child != null) {
|
||||
child.layout(constraints.asBoxConstraints(), parentUsesSize: true);
|
||||
|
||||
switch (constraints.axis) {
|
||||
case Axis.horizontal:
|
||||
childExtent = child.size.width;
|
||||
break;
|
||||
case Axis.vertical:
|
||||
childExtent = child.size.height;
|
||||
break;
|
||||
}
|
||||
if (constraints.precedingScrollExtent > constraints.viewportMainAxisExtent || childExtent > extent)
|
||||
extent = childExtent;
|
||||
if (maxExtent < extent)
|
||||
maxExtent = extent;
|
||||
if ((fillOverscroll ? maxExtent : extent) > childExtent) {
|
||||
child.layout(
|
||||
constraints.asBoxConstraints(
|
||||
minExtent: extent,
|
||||
maxExtent: fillOverscroll ? maxExtent : extent,
|
||||
),
|
||||
parentUsesSize: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assert(extent.isFinite,
|
||||
'The calculated extent for the child of SliverFillRemaining is not finite.'
|
||||
'This can happen if the child is a scrollable, in which case, the'
|
||||
'hasScrollBody property of SliverFillRemaining should not be set to'
|
||||
'false.',
|
||||
);
|
||||
final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent);
|
||||
assert(paintedChildSize.isFinite);
|
||||
assert(paintedChildSize >= 0.0);
|
||||
geometry = SliverGeometry(
|
||||
// 0.0 can be applied here for cases when there is not scroll body since
|
||||
// SliverFillRemaining will not have any slivers following it.
|
||||
scrollExtent: hasScrollBody ? constraints.viewportMainAxisExtent : 0.0,
|
||||
scrollExtent: hasScrollBody ? constraints.viewportMainAxisExtent : extent,
|
||||
paintExtent: paintedChildSize,
|
||||
maxPaintExtent: paintedChildSize,
|
||||
hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
|
||||
|
@ -1369,23 +1369,44 @@ class SliverFillRemaining extends SingleChildRenderObjectWidget {
|
||||
Key key,
|
||||
Widget child,
|
||||
this.hasScrollBody = true,
|
||||
this.fillOverscroll = false,
|
||||
}) : assert(hasScrollBody != null),
|
||||
super(key: key, child: child);
|
||||
|
||||
/// Whether the child has a scrollable body, this value cannot be null.
|
||||
/// Indicates whether the child has a scrollable body, this value cannot be
|
||||
/// null.
|
||||
///
|
||||
/// Defaults to true such that the child will extend beyond the viewport and
|
||||
/// scroll, as seen in [NestedScrollView].
|
||||
///
|
||||
/// Setting this value to false will allow the child to fill the remainder of
|
||||
/// the viewport and not extend further.
|
||||
/// the viewport and not extend further. However, if the
|
||||
/// [precedingScrollExtent] exceeds the size of the viewport, the sliver will
|
||||
/// defer to the child's size rather than overriding it.
|
||||
final bool hasScrollBody;
|
||||
|
||||
@override
|
||||
RenderSliverFillRemaining createRenderObject(BuildContext context) => RenderSliverFillRemaining(hasScrollBody: hasScrollBody);
|
||||
/// Indicates whether the child should stretch to fill the overscroll area
|
||||
/// created by certain scroll physics, such as iOS' default scroll physics.
|
||||
/// This value cannot be null. This flag is only relevant when the
|
||||
/// [hasScrollBody] value is false.
|
||||
///
|
||||
/// Defaults to false, meaning the default behavior is for the child to
|
||||
/// maintain its size and not extend into the overscroll area.
|
||||
final bool fillOverscroll;
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderSliverFillRemaining renderObject) => renderObject.hasScrollBody = hasScrollBody;
|
||||
RenderSliverFillRemaining createRenderObject(BuildContext context) {
|
||||
return RenderSliverFillRemaining(
|
||||
hasScrollBody: hasScrollBody,
|
||||
fillOverscroll: fillOverscroll,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderSliverFillRemaining renderObject) {
|
||||
renderObject.hasScrollBody = hasScrollBody;
|
||||
renderObject.fillOverscroll = fillOverscroll;
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark a child as needing to stay alive even when it's in a lazy list that
|
||||
|
@ -3,6 +3,7 @@
|
||||
// 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';
|
||||
|
||||
@ -64,62 +65,368 @@ void main() {
|
||||
expect(tester.renderObject<RenderBox>(find.byType(Container)).size.height, equals(500.0));
|
||||
});
|
||||
|
||||
testWidgets('SliverFillRemaining does not extend past viewport.', (WidgetTester tester) async {
|
||||
final ScrollController controller = ScrollController();
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: CustomScrollView(
|
||||
controller: controller,
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
color: Colors.red,
|
||||
height: 150.0,
|
||||
),
|
||||
),
|
||||
SliverFillRemaining(
|
||||
child: Container(color: Colors.white),
|
||||
hasScrollBody: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
group('SliverFillRemaining - hasScrollBody', () {
|
||||
final Widget sliverBox = SliverToBoxAdapter(
|
||||
child: Container(
|
||||
color: Colors.amber,
|
||||
height: 150.0,
|
||||
),
|
||||
);
|
||||
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));
|
||||
});
|
||||
Widget boilerplate(List<Widget> slivers, {ScrollController controller}) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: slivers,
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('SliverFillRemaining scrolls beyond viewport by default.', (WidgetTester tester) async {
|
||||
final ScrollController controller = ScrollController();
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: CustomScrollView(
|
||||
controller: controller,
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
color: Colors.red,
|
||||
height: 150.0,
|
||||
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: () {},
|
||||
),
|
||||
),
|
||||
SliverFillRemaining(
|
||||
child: Container(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
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);
|
||||
];
|
||||
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));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user