Commit 06f879b5 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Add ScrollController.initialScrollOffset (#7916)

Also, move the creation of the ScrollPosition to ScrollController.

Finally, remove TestScrollable in favor of CustomScrollView, which is the
production version of this widget.
parent 11532cea
......@@ -10,6 +10,17 @@ import 'package:meta/meta.dart';
import 'scroll_position.dart';
class ScrollController {
ScrollController({
this.initialScrollOffset,
});
/// The initial value to use for [offset].
///
/// If [initialScrollOffset] is non-null, new [ScrollPosition] objects that
/// are created and attached to this controller will have their offset
/// initialized to this value.
final double initialScrollOffset;
final List<ScrollPosition> _positions = <ScrollPosition>[];
double get offset {
......@@ -87,4 +98,21 @@ class ScrollController {
assert(_positions.contains(position));
_positions.remove(position);
}
static ScrollPosition createDefaultScrollPosition(ScrollPhysics physics, AbstractScrollState state, ScrollPosition oldPosition) {
return new ScrollPosition(
physics: physics,
state: state,
oldPosition: oldPosition,
);
}
ScrollPosition createScrollPosition(ScrollPhysics physics, AbstractScrollState state, ScrollPosition oldPosition) {
return new ScrollPosition(
physics: physics,
state: state,
offset: initialScrollOffset,
oldPosition: oldPosition,
);
}
}
......@@ -39,12 +39,6 @@ abstract class ScrollPhysics {
ScrollPhysics applyTo(ScrollPhysics parent);
ScrollPosition createScrollPosition(ScrollPhysics physics, AbstractScrollState state, ScrollPosition oldPosition) {
if (parent == null)
return new ScrollPosition(physics, state, oldPosition);
return parent.createScrollPosition(physics, state, oldPosition);
}
/// Used by [DragScrollActivity] and other user-driven activities to
/// convert an offset in logical pixels as provided by the [DragUpdateDetails]
/// into a delta to apply using [setPixels].
......@@ -101,18 +95,6 @@ abstract class ScrollPhysics {
Tolerance get tolerance => parent?.tolerance ?? _kDefaultTolerance;
@mustCallSuper
bool shouldUpdateScrollPosition(@checked ScrollPhysics other) {
if ((parent == null) != (other.parent == null))
return true;
if (parent == null) {
assert(other.parent == null);
return false;
}
return parent.runtimeType != other.parent.runtimeType
|| parent.shouldUpdateScrollPosition(other.parent);
}
@override
String toString() {
if (parent == null)
......@@ -122,10 +104,16 @@ abstract class ScrollPhysics {
}
class ScrollPosition extends ViewportOffset {
ScrollPosition(this.physics, this.state, ScrollPosition oldPosition) {
ScrollPosition({
@required this.physics,
@required this.state,
double offset: 0.0,
ScrollPosition oldPosition,
}) : _pixels = offset ?? 0.0 {
assert(physics != null);
assert(state != null);
assert(state.vsync != null);
assert(pixels != null);
if (oldPosition != null)
absorb(oldPosition);
if (activity == null)
......@@ -140,7 +128,7 @@ class ScrollPosition extends ViewportOffset {
@override
double get pixels => _pixels;
double _pixels = 0.0;
double _pixels;
Future<Null> ensureVisible(RenderObject object, {
double alignment: 0.0,
......
......@@ -128,17 +128,20 @@ class Scrollable2State extends State<Scrollable2> with TickerProviderStateMixin
ScrollPhysics physics = _configuration.getScrollPhysics(context);
if (config.physics != null)
physics = config.physics.applyTo(physics);
final ScrollController controller = config.controller;
final ScrollPosition oldPosition = position;
if (oldPosition != null) {
config.controller?.detach(oldPosition);
controller?.detach(oldPosition);
// It's important that we not dispose the old position until after the
// viewport has had a chance to unregister its listeners from the old
// position. So, schedule a microtask to do it.
scheduleMicrotask(oldPosition.dispose);
}
_position = physics.createScrollPosition(physics, this, oldPosition);
_position = controller?.createScrollPosition(physics, this, oldPosition)
?? ScrollController.createDefaultScrollPosition(physics, this, oldPosition);
assert(position != null);
config.controller?.attach(position);
controller?.attach(position);
}
@override
......@@ -148,12 +151,8 @@ class Scrollable2State extends State<Scrollable2> with TickerProviderStateMixin
}
bool _shouldUpdatePosition(Scrollable2 oldConfig) {
if (config.physics == oldConfig.physics)
return false;
if ((config.physics == null) != (oldConfig.physics == null))
return true;
return config.physics.runtimeType != oldConfig.physics.runtimeType
|| config.physics.shouldUpdateScrollPosition(oldConfig.physics);
return config.physics?.runtimeType != oldConfig.physics?.runtimeType
|| config.controller?.runtimeType != config.controller?.runtimeType;
}
@override
......
......@@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'test_widgets.dart';
final Key blockKey = new Key('test');
void main() {
......@@ -132,7 +130,7 @@ void main() {
new Container(),
]);
await tester.pumpWidget(new TestScrollable(
await tester.pumpWidget(new CustomScrollView(
slivers: <Widget>[
new SliverList(
delegate: delegate,
......
......@@ -9,7 +9,6 @@ import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
import '../rendering/mock_canvas.dart';
import 'test_widgets.dart';
final Matcher doesNotOverscroll = isNot(paints..circle());
......@@ -25,7 +24,7 @@ Future<Null> slowDrag(WidgetTester tester, Point start, Offset offset) async {
void main() {
testWidgets('Overscroll indicator color', (WidgetTester tester) async {
await tester.pumpWidget(
new TestScrollable(
new CustomScrollView(
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 2000.0)),
],
......@@ -58,7 +57,7 @@ void main() {
testWidgets('Overscroll indicator changes side when you drag on the other side', (WidgetTester tester) async {
await tester.pumpWidget(
new TestScrollable(
new CustomScrollView(
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 2000.0)),
],
......@@ -93,7 +92,7 @@ void main() {
testWidgets('Overscroll indicator changes side when you shift sides', (WidgetTester tester) async {
await tester.pumpWidget(
new TestScrollable(
new CustomScrollView(
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 2000.0)),
],
......@@ -126,8 +125,7 @@ void main() {
group('Flipping direction of scrollable doesn\'t change overscroll behavior', () {
testWidgets('down', (WidgetTester tester) async {
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 20.0)),
],
......@@ -143,8 +141,8 @@ void main() {
testWidgets('up', (WidgetTester tester) async {
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.up,
new CustomScrollView(
reverse: true,
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 20.0)),
],
......@@ -161,8 +159,7 @@ void main() {
testWidgets('Overscroll in both directions', (WidgetTester tester) async {
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 20.0)),
],
......@@ -181,8 +178,8 @@ void main() {
testWidgets('Overscroll horizontally', (WidgetTester tester) async {
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.right,
new CustomScrollView(
scrollDirection: Axis.horizontal,
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 20.0)),
],
......@@ -206,8 +203,9 @@ void main() {
await tester.pumpWidget(
new ScrollConfiguration2(
behavior: new TestScrollBehavior1(),
child: new TestScrollable(
axisDirection: AxisDirection.left,
child: new CustomScrollView(
scrollDirection: Axis.horizontal,
reverse: true,
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 20.0)),
],
......@@ -223,8 +221,8 @@ void main() {
await tester.pumpWidget(
new ScrollConfiguration2(
behavior: new TestScrollBehavior2(),
child: new TestScrollable(
axisDirection: AxisDirection.right,
child: new CustomScrollView(
scrollDirection: Axis.horizontal,
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 20.0)),
],
......
......@@ -108,4 +108,39 @@ void main() {
expect(controller2.offset, equals(432.0));
expect(realOffset(), equals(controller2.offset));
});
testWidgets('ScrollController control test', (WidgetTester tester) async {
ScrollController controller = new ScrollController(
initialScrollOffset: 209.0,
);
await tester.pumpWidget(new GridView.count(
crossAxisCount: 4,
controller: controller,
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
));
double realOffset() {
return tester.state<Scrollable2State>(find.byType(Scrollable2)).position.pixels;
}
expect(controller.offset, equals(209.0));
expect(realOffset(), equals(controller.offset));
controller.jumpTo(105.0);
await tester.pump();
expect(controller.offset, equals(105.0));
expect(realOffset(), equals(controller.offset));
await tester.pumpWidget(new GridView.count(
crossAxisCount: 2,
controller: controller,
children: kStates.map<Widget>((String state) => new Text(state)).toList(),
));
expect(controller.offset, equals(105.0));
expect(realOffset(), equals(controller.offset));
});
}
......@@ -6,14 +6,18 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'test_widgets.dart';
class TestScrollPosition extends ScrollPosition {
TestScrollPosition(
TestScrollPhysics physics,
TestScrollPosition({
ScrollPhysics physics,
AbstractScrollState state,
ScrollPosition oldPosition,
) : _pixels = 100.0, super(physics, state, oldPosition);
}) : _pixels = 100.0, super(
physics: physics,
state: state,
oldPosition: oldPosition,
) {
assert(physics is TestScrollPhysics);
}
@override
TestScrollPhysics get physics => super.physics;
......@@ -80,18 +84,23 @@ class TestScrollPosition extends ScrollPosition {
}
class TestScrollPhysics extends ScrollPhysics {
const TestScrollPhysics({ ScrollPhysics parent, this.extentMultiplier }) : super(parent);
const TestScrollPhysics({ this.extentMultiplier, ScrollPhysics parent }) : super(parent);
final double extentMultiplier;
@override
TestScrollPhysics applyTo(ScrollPhysics parent) {
return new TestScrollPhysics(parent: parent, extentMultiplier: extentMultiplier);
ScrollPhysics applyTo(ScrollPhysics parent) {
return new TestScrollPhysics(
extentMultiplier: extentMultiplier,
parent: parent,
);
}
}
class TestScrollController extends ScrollController {
@override
ScrollPosition createScrollPosition(ScrollPhysics physics, AbstractScrollState state, ScrollPosition oldPosition) {
return new TestScrollPosition(physics, state, oldPosition);
return new TestScrollPosition(physics: physics, state: state, oldPosition: oldPosition);
}
}
......@@ -120,7 +129,8 @@ void main() {
testWidgets('Changing the scroll behavior dynamically', (WidgetTester tester) async {
await tester.pumpWidget(new ScrollConfiguration2(
behavior: new TestScrollBehavior(1.0),
child: new TestScrollable(
child: new CustomScrollView(
controller: new TestScrollController(),
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 2000.0)),
],
......@@ -131,7 +141,8 @@ void main() {
expect(state.position.getMetrics().extentInside, 1.0);
await tester.pumpWidget(new ScrollConfiguration2(
behavior: new TestScrollBehavior(2.0),
child: new TestScrollable(
child: new CustomScrollView(
controller: new TestScrollController(),
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 2000.0)),
],
......
......@@ -6,14 +6,12 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'test_widgets.dart';
Future<Null> pumpTest(WidgetTester tester, TargetPlatform platform) async {
await tester.pumpWidget(new MaterialApp(
theme: new ThemeData(
platform: platform,
),
home: new TestScrollable(
home: new CustomScrollView(
slivers: <Widget>[
new SliverToBoxAdapter(child: new SizedBox(height: 2000.0)),
],
......
......@@ -5,8 +5,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
import 'test_widgets.dart';
void main() {
testWidgets('SliverFillRemaining control test', (WidgetTester tester) async {
List<Widget> children = new List<Widget>.generate(20, (int i) {
......@@ -14,7 +12,7 @@ void main() {
});
await tester.pumpWidget(
new TestScrollable(
new CustomScrollView(
slivers: <Widget>[
new SliverFill(
delegate: new SliverChildListDelegate(children),
......
......@@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'test_widgets.dart';
void verifyPaintPosition(GlobalKey key, Offset ideal, bool visible) {
RenderSliver target = key.currentContext.findRenderObject();
expect(target.parent, new isInstanceOf<RenderViewport2>());
......@@ -28,8 +26,7 @@ void main() {
testWidgets('Sliver appbars - floating - scroll offset doesn\'t change', (WidgetTester tester) async {
const double bigHeight = 1000.0;
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(height: bigHeight),
new SliverPersistentHeader(delegate: new TestDelegate(), floating: true),
......@@ -56,8 +53,7 @@ void main() {
const double bigHeight = 1000.0;
GlobalKey key1, key2, key3;
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(key: key1 = new GlobalKey(), height: bigHeight),
new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: delegate, floating: true),
......@@ -126,8 +122,7 @@ void main() {
const double bigHeight = 1000.0;
GlobalKey key1, key2, key3;
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(key: key1 = new GlobalKey(), height: bigHeight),
new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: delegate, floating: true),
......@@ -159,8 +154,7 @@ void main() {
const double bigHeight = 1000.0;
GlobalKey key1, key2, key3;
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(key: key1 = new GlobalKey(), height: bigHeight),
new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: delegate, floating: true),
......
......@@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'test_widgets.dart';
void verifyPaintPosition(GlobalKey key, Offset ideal, bool visible) {
RenderSliver target = key.currentContext.findRenderObject();
expect(target.parent, new isInstanceOf<RenderViewport2>());
......@@ -29,8 +27,7 @@ void main() {
const double bigHeight = 550.0;
GlobalKey key1, key2, key3, key4, key5;
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(key: key1 = new GlobalKey(), height: bigHeight),
new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: new TestDelegate(), pinned: true),
......@@ -63,8 +60,7 @@ void main() {
const double bigHeight = 550.0;
GlobalKey key1, key2, key3, key4, key5;
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(key: key1 = new GlobalKey(), height: bigHeight),
new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: new TestDelegate(), pinned: true),
......@@ -153,8 +149,7 @@ void main() {
const double bigHeight = 650.0;
GlobalKey key1, key2, key3, key4, key5;
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(key: key1 = new GlobalKey(), height: bigHeight),
new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: new TestDelegate(), pinned: true),
......
......@@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'test_widgets.dart';
void verifyPaintPosition(GlobalKey key, Offset ideal) {
RenderObject target = key.currentContext.findRenderObject();
expect(target.parent, new isInstanceOf<RenderViewport2>());
......@@ -20,8 +18,7 @@ void main() {
testWidgets('Sliver appbars - scrolling', (WidgetTester tester) async {
GlobalKey key1, key2, key3, key4, key5;
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(key: key1 = new GlobalKey()),
new SliverPersistentHeader(key: key2 = new GlobalKey(), delegate: new TestDelegate()),
......@@ -54,8 +51,7 @@ void main() {
GlobalKey key = new GlobalKey();
TestDelegate delegate = new TestDelegate();
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(),
new SliverPersistentHeader(key: key, delegate: delegate),
......
......@@ -8,8 +8,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'test_widgets.dart';
void verifyPaintPosition(GlobalKey key, Offset ideal) {
RenderObject target = key.currentContext.findRenderObject();
expect(target.parent, new isInstanceOf<RenderViewport2>());
......@@ -22,8 +20,7 @@ void main() {
testWidgets('Sliver protocol', (WidgetTester tester) async {
GlobalKey key1, key2, key3, key4, key5;
await tester.pumpWidget(
new TestScrollable(
axisDirection: AxisDirection.down,
new CustomScrollView(
slivers: <Widget>[
new BigSliver(key: key1 = new GlobalKey()),
new OverlappingSliver(key: key2 = new GlobalKey()),
......
......@@ -57,45 +57,3 @@ class FlipWidgetState extends State<FlipWidget> {
void flipStatefulWidget(WidgetTester tester) {
tester.state<FlipWidgetState>(find.byType(FlipWidget)).flip();
}
class TestScrollable extends StatelessWidget {
TestScrollable({
Key key,
this.axisDirection: AxisDirection.down,
this.physics,
this.anchor: 0.0,
this.center,
this.slivers: const <Widget>[],
}) {
assert(slivers != null);
}
final AxisDirection axisDirection;
final ScrollPhysics physics;
final double anchor;
final Key center;
final List<Widget> slivers;
Axis get axis => axisDirectionToAxis(axisDirection);
@override
Widget build(BuildContext context) {
return new Scrollable2(
axisDirection: axisDirection,
physics: physics,
viewportBuilder: (BuildContext context, ViewportOffset offset) {
return new Viewport2(
axisDirection: axisDirection,
anchor: anchor,
offset: offset,
center: center,
slivers: slivers,
);
}
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment