Move VariableHeightScrollable to sdk/lib/widgets

Added a callback to BlockViewport so that the scrollable can
stay in sync with the viewport's contents.

R=ianh@google.com

Review URL: https://codereview.chromium.org/1227823012 .
This commit is contained in:
Hans Muller 2015-07-10 17:13:26 -07:00
parent 442c55b4c4
commit 63318886a0
4 changed files with 94 additions and 45 deletions

View File

@ -87,6 +87,7 @@ dart_pkg("sky") {
"lib/widgets/theme.dart",
"lib/widgets/toggleable.dart",
"lib/widgets/tool_bar.dart",
"lib/widgets/variable_height_scrollable.dart",
"lib/widgets/widget.dart",
"pubspec.yaml",
]

View File

@ -6,16 +6,14 @@ import 'dart:sky' as sky;
import 'package:vector_math/vector_math.dart';
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/animation/scroll_behavior.dart';
import 'package:sky/base/lerp.dart';
import 'package:sky/painting/text_style.dart';
import 'package:sky/theme/colors.dart';
import 'package:sky/widgets/animation_builder.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/block_viewport.dart';
import 'package:sky/widgets/card.dart';
import 'package:sky/widgets/scaffold.dart';
import 'package:sky/widgets/scrollable.dart';
import 'package:sky/widgets/variable_height_scrollable.dart';
import 'package:sky/widgets/theme.dart';
import 'package:sky/widgets/tool_bar.dart';
import 'package:sky/widgets/widget.dart';
@ -26,50 +24,13 @@ const double _kMinFlingVelocity = 700.0;
const double _kMinFlingVelocityDelta = 400.0;
const double _kDismissCardThreshold = 0.6;
class VariableHeightScrollable extends Scrollable {
VariableHeightScrollable({
String key,
this.builder,
this.token
}) : super(key: key);
IndexedBuilder builder;
Object token;
void syncFields(VariableHeightScrollable source) {
builder = source.builder;
token = source.token;
super.syncFields(source);
}
ScrollBehavior createScrollBehavior() => new OverscrollBehavior();
OverscrollBehavior get scrollBehavior => super.scrollBehavior;
void _handleSizeChanged(Size newSize) {
setState(() {
scrollBehavior.containerSize = newSize.height;
scrollBehavior.contentsSize = 5000.0;
});
}
Widget buildContent() {
return new SizeObserver(
callback: _handleSizeChanged,
child: new BlockViewport(
builder: builder,
startOffset: scrollOffset,
token: token
)
);
}
}
class CardCollectionApp extends App {
final TextStyle cardLabelStyle =
new TextStyle(color: White, fontSize: 18.0, fontWeight: bold);
final List<double> cardHeights = [
48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 64.0, 82.0, 46.0, 60.0, 55.0, 84.0, 96.0, 50.0

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:collection';
import '../rendering/block.dart';
import '../rendering/box.dart';
import '../rendering/object.dart';
@ -10,6 +12,13 @@ import 'widget.dart';
// return null if index is greater than index of last entry
typedef Widget IndexedBuilder(int index);
typedef void LayoutChangedCallback(
int firstVisibleChildIndex,
int visibleChildCount,
UnmodifiableListView<double> childOffsets,
bool didReachLastChild
);
class _Key {
const _Key(this.type, this.key);
factory _Key.fromWidget(Widget widget) => new _Key(widget.runtimeType, widget.key);
@ -20,12 +29,13 @@ class _Key {
}
class BlockViewport extends RenderObjectWrapper {
BlockViewport({ this.builder, this.startOffset, this.token, String key })
BlockViewport({ this.builder, this.startOffset, this.token, this.onLayoutChanged, String key })
: super(key: key);
IndexedBuilder builder;
double startOffset;
Object token;
LayoutChangedCallback onLayoutChanged;
RenderBlockViewport get root => super.root;
RenderBlockViewport createNode() => new RenderBlockViewport();
@ -83,6 +93,7 @@ class BlockViewport extends RenderObjectWrapper {
List<double> _offsets = <double>[0.0];
int _currentStartIndex = 0;
int _currentChildCount = 0;
bool _didReachLastChild = false;
int _findIndexForOffsetBeforeOrAt(double offset) {
int left = 0;
@ -113,6 +124,7 @@ class BlockViewport extends RenderObjectWrapper {
builder = newNode.builder;
token = newNode.token;
_offsets = <double>[0.0];
_didReachLastChild = false;
}
return true;
}
@ -196,6 +208,7 @@ class BlockViewport extends RenderObjectWrapper {
haveChildren = true;
} else {
haveChildren = false;
_didReachLastChild = true;
}
}
} else {
@ -207,8 +220,10 @@ class BlockViewport extends RenderObjectWrapper {
// list is complete (and thus we are overscrolled).
while (true) {
Widget widget = _getWidget(startIndex, innerConstraints);
if (widget == null)
if (widget == null) {
_didReachLastChild = true;
break;
}
_Key widgetKey = new _Key.fromWidget(widget);
if (_offsets.last > startOffset) {
newChildren[widgetKey] = widget;
@ -238,6 +253,7 @@ class BlockViewport extends RenderObjectWrapper {
}
}
assert(haveChildren != null);
assert(haveChildren || _didReachLastChild);
assert(startIndex >= 0);
assert(startIndex < _offsets.length);
@ -249,8 +265,10 @@ class BlockViewport extends RenderObjectWrapper {
while (_offsets[index] < endOffset) {
if (!builtChildren.containsKey(index)) {
Widget widget = _getWidget(index, innerConstraints);
if (widget == null)
break; // reached the end of the list
if (widget == null) {
_didReachLastChild = true;
break;
}
newChildren[new _Key.fromWidget(widget)] = widget;
builtChildren[index] = widget;
}
@ -287,6 +305,15 @@ class BlockViewport extends RenderObjectWrapper {
_childrenByKey = newChildren;
_currentStartIndex = startIndex;
_currentChildCount = _childrenByKey.length;
if (onLayoutChanged != null) {
onLayoutChanged(
_currentStartIndex,
_currentChildCount,
new UnmodifiableListView<double>(_offsets),
_didReachLastChild
);
}
}
}

View File

@ -0,0 +1,60 @@
// Copyright 2015 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 'dart:collection';
import '../animation/scroll_behavior.dart';
import 'basic.dart';
import 'block_viewport.dart';
import 'scrollable.dart';
import 'widget.dart';
class VariableHeightScrollable extends Scrollable {
VariableHeightScrollable({
String key,
this.builder,
this.token
}) : super(key: key);
IndexedBuilder builder;
Object token;
void syncFields(VariableHeightScrollable source) {
builder = source.builder;
token = source.token;
super.syncFields(source);
}
ScrollBehavior createScrollBehavior() => new OverscrollBehavior();
OverscrollBehavior get scrollBehavior => super.scrollBehavior;
void _handleSizeChanged(Size newSize) {
setState(() {
scrollBehavior.containerSize = newSize.height;
});
}
void _handleLayoutChanged(
int firstVisibleChildIndex,
int visibleChildCount,
UnmodifiableListView<double> childOffsets,
bool didReachLastChild) {
assert(childOffsets.length > 0);
scrollBehavior.contentsSize = didReachLastChild ? childOffsets.last : double.INFINITY;
if (didReachLastChild && scrollOffset > scrollBehavior.maxScrollOffset)
settleScrollOffset();
}
Widget buildContent() {
return new SizeObserver(
callback: _handleSizeChanged,
child: new BlockViewport(
builder: builder,
onLayoutChanged: _handleLayoutChanged,
startOffset: scrollOffset,
token: token
)
);
}
}