Commit fd7be69d authored by Hixie's avatar Hixie

SizeObserver crusade: snap alignment

Remove the SizeObserver you need to use snap alignment in lists by
having the lists provide the size themselves in the callback.

Also, remove snapAlignmentOffset since we couldn't figure out how to
explain it and it's not really needed.
parent 6c814335
...@@ -28,6 +28,8 @@ class CardCollectionState extends State<CardCollection> { ...@@ -28,6 +28,8 @@ class CardCollectionState extends State<CardCollection> {
// TODO(hansmuller): need a local image asset // TODO(hansmuller): need a local image asset
static const _sunshineURL = "http://www.walltor.com/images/wallpaper/good-morning-sunshine-58540.jpg"; static const _sunshineURL = "http://www.walltor.com/images/wallpaper/good-morning-sunshine-58540.jpg";
static const kCardMargins = 8.0;
final TextStyle backgroundTextStyle = final TextStyle backgroundTextStyle =
Typography.white.title.copyWith(textAlign: TextAlign.center); Typography.white.title.copyWith(textAlign: TextAlign.center);
...@@ -41,7 +43,6 @@ class CardCollectionState extends State<CardCollection> { ...@@ -41,7 +43,6 @@ class CardCollectionState extends State<CardCollection> {
bool _sunshine = false; bool _sunshine = false;
bool _varyFontSizes = false; bool _varyFontSizes = false;
InvalidatorCallback _invalidator; InvalidatorCallback _invalidator;
Size _cardCollectionSize = new Size(200.0, 200.0);
void _initVariableSizedCardModels() { void _initVariableSizedCardModels() {
List<double> cardHeights = <double>[ List<double> cardHeights = <double>[
...@@ -78,15 +79,14 @@ class CardCollectionState extends State<CardCollection> { ...@@ -78,15 +79,14 @@ class CardCollectionState extends State<CardCollection> {
double _variableSizeToSnapOffset(double scrollOffset) { double _variableSizeToSnapOffset(double scrollOffset) {
double cumulativeHeight = 0.0; double cumulativeHeight = 0.0;
double margins = 8.0;
List<double> cumulativeHeights = _cardModels.map((CardModel card) { List<double> cumulativeHeights = _cardModels.map((CardModel card) {
cumulativeHeight += card.height + margins; cumulativeHeight += card.height + kCardMargins;
return cumulativeHeight; return cumulativeHeight;
}) })
.toList(); .toList();
double offsetForIndex(int i) { double offsetForIndex(int i) {
return 12.0 + (margins + _cardModels[i].height) / 2.0 + ((i == 0) ? 0.0 : cumulativeHeights[i - 1]); return (kCardMargins + _cardModels[i].height) / 2.0 + ((i == 0) ? 0.0 : cumulativeHeights[i - 1]);
} }
for (int i = 0; i < cumulativeHeights.length; i++) { for (int i = 0; i < cumulativeHeights.length; i++) {
...@@ -99,11 +99,14 @@ class CardCollectionState extends State<CardCollection> { ...@@ -99,11 +99,14 @@ class CardCollectionState extends State<CardCollection> {
double _fixedSizeToSnapOffset(double scrollOffset) { double _fixedSizeToSnapOffset(double scrollOffset) {
double cardHeight = _cardModels[0].height; double cardHeight = _cardModels[0].height;
int cardIndex = (scrollOffset.clamp(0.0, cardHeight * (_cardModels.length - 1)) / cardHeight).floor(); int cardIndex = (scrollOffset.clamp(0.0, cardHeight * (_cardModels.length - 1)) / cardHeight).floor();
return 12.0 + cardIndex * cardHeight + cardHeight * 0.5; return cardIndex * cardHeight + cardHeight * 0.5;
} }
double _toSnapOffset(double scrollOffset) { double _toSnapOffset(double scrollOffset, Size containerSize) {
return _fixedSizeCards ? _fixedSizeToSnapOffset(scrollOffset) : _variableSizeToSnapOffset(scrollOffset); double halfHeight = containerSize.height / 2.0;
scrollOffset += halfHeight;
double result = _fixedSizeCards ? _fixedSizeToSnapOffset(scrollOffset) : _variableSizeToSnapOffset(scrollOffset);
return result - halfHeight;
} }
void dismissCard(CardModel card) { void dismissCard(CardModel card) {
...@@ -300,7 +303,7 @@ class CardCollectionState extends State<CardCollection> { ...@@ -300,7 +303,7 @@ class CardCollectionState extends State<CardCollection> {
color: Theme.of(context).primarySwatch[cardModel.color], color: Theme.of(context).primarySwatch[cardModel.color],
child: new Container( child: new Container(
height: cardModel.height, height: cardModel.height,
padding: const EdgeDims.all(8.0), padding: const EdgeDims.all(kCardMargins),
child: _editable ? child: _editable ?
new Center( new Center(
child: new Input( child: new Input(
...@@ -387,12 +390,6 @@ class CardCollectionState extends State<CardCollection> { ...@@ -387,12 +390,6 @@ class CardCollectionState extends State<CardCollection> {
); );
} }
void _updateCardCollectionSize(Size newSize) {
setState(() {
_cardCollectionSize = newSize;
});
}
Shader _createShader(Rect bounds) { Shader _createShader(Rect bounds) {
return new LinearGradient( return new LinearGradient(
begin: bounds.topLeft, begin: bounds.topLeft,
...@@ -408,7 +405,6 @@ class CardCollectionState extends State<CardCollection> { ...@@ -408,7 +405,6 @@ class CardCollectionState extends State<CardCollection> {
if (_fixedSizeCards) { if (_fixedSizeCards) {
cardCollection = new ScrollableList ( cardCollection = new ScrollableList (
snapOffsetCallback: _snapToCenter ? _toSnapOffset : null, snapOffsetCallback: _snapToCenter ? _toSnapOffset : null,
snapAlignmentOffset: _cardCollectionSize.height / 2.0,
itemExtent: _cardModels[0].height, itemExtent: _cardModels[0].height,
children: _cardModels.map((CardModel card) => _buildCard(context, card.value)) children: _cardModels.map((CardModel card) => _buildCard(context, card.value))
); );
...@@ -417,7 +413,6 @@ class CardCollectionState extends State<CardCollection> { ...@@ -417,7 +413,6 @@ class CardCollectionState extends State<CardCollection> {
builder: _buildCard, builder: _buildCard,
token: _cardModels.length, token: _cardModels.length,
snapOffsetCallback: _snapToCenter ? _toSnapOffset : null, snapOffsetCallback: _snapToCenter ? _toSnapOffset : null,
snapAlignmentOffset: _cardCollectionSize.height / 2.0,
onInvalidatorAvailable: (InvalidatorCallback callback) { _invalidator = callback; } onInvalidatorAvailable: (InvalidatorCallback callback) { _invalidator = callback; }
); );
} }
...@@ -431,13 +426,10 @@ class CardCollectionState extends State<CardCollection> { ...@@ -431,13 +426,10 @@ class CardCollectionState extends State<CardCollection> {
); );
} }
Widget body = new SizeObserver( Widget body = new Container(
onSizeChanged: _updateCardCollectionSize, padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
child: new Container( decoration: new BoxDecoration(backgroundColor: Theme.of(context).primarySwatch[50]),
padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0), child: cardCollection
decoration: new BoxDecoration(backgroundColor: Theme.of(context).primarySwatch[50]),
child: cardCollection
)
); );
if (_snapToCenter) { if (_snapToCenter) {
......
...@@ -28,7 +28,6 @@ class PageableList extends Scrollable { ...@@ -28,7 +28,6 @@ class PageableList extends Scrollable {
ScrollListener onScroll, ScrollListener onScroll,
ScrollListener onScrollEnd, ScrollListener onScrollEnd,
SnapOffsetCallback snapOffsetCallback, SnapOffsetCallback snapOffsetCallback,
double snapAlignmentOffset: 0.0,
this.itemsWrap: false, this.itemsWrap: false,
this.itemsSnapAlignment: ItemsSnapAlignment.adjacentItem, this.itemsSnapAlignment: ItemsSnapAlignment.adjacentItem,
this.onPageChanged, this.onPageChanged,
...@@ -44,8 +43,7 @@ class PageableList extends Scrollable { ...@@ -44,8 +43,7 @@ class PageableList extends Scrollable {
onScrollStart: onScrollStart, onScrollStart: onScrollStart,
onScroll: onScroll, onScroll: onScroll,
onScrollEnd: onScrollEnd, onScrollEnd: onScrollEnd,
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback
snapAlignmentOffset: snapAlignmentOffset
); );
final bool itemsWrap; final bool itemsWrap;
......
...@@ -25,7 +25,7 @@ final Tolerance kPixelScrollTolerance = new Tolerance( ...@@ -25,7 +25,7 @@ final Tolerance kPixelScrollTolerance = new Tolerance(
); );
typedef void ScrollListener(double scrollOffset); typedef void ScrollListener(double scrollOffset);
typedef double SnapOffsetCallback(double scrollOffset); typedef double SnapOffsetCallback(double scrollOffset, Size containerSize);
/// A base class for scrollable widgets. /// A base class for scrollable widgets.
/// ///
...@@ -43,12 +43,10 @@ abstract class Scrollable extends StatefulComponent { ...@@ -43,12 +43,10 @@ abstract class Scrollable extends StatefulComponent {
this.onScrollStart, this.onScrollStart,
this.onScroll, this.onScroll,
this.onScrollEnd, this.onScrollEnd,
this.snapOffsetCallback, this.snapOffsetCallback
this.snapAlignmentOffset: 0.0
}) : super(key: key) { }) : super(key: key) {
assert(scrollDirection == Axis.vertical || scrollDirection == Axis.horizontal); assert(scrollDirection == Axis.vertical || scrollDirection == Axis.horizontal);
assert(scrollAnchor == ViewportAnchor.start || scrollAnchor == ViewportAnchor.end); assert(scrollAnchor == ViewportAnchor.start || scrollAnchor == ViewportAnchor.end);
assert(snapAlignmentOffset != null);
} }
/// The scroll offset this widget should use when first created. /// The scroll offset this widget should use when first created.
...@@ -68,11 +66,21 @@ abstract class Scrollable extends StatefulComponent { ...@@ -68,11 +66,21 @@ abstract class Scrollable extends StatefulComponent {
/// Called whenever this widget stops scrolling. /// Called whenever this widget stops scrolling.
final ScrollListener onScrollEnd; final ScrollListener onScrollEnd;
/// Called to determine the offset to which scrolling should snap. /// Called to determine the offset to which scrolling should snap,
/// when handling a fling.
///
/// This callback, if set, will be called with the offset that the
/// Scrollable would have scrolled to in the absence of this
/// callback, and a Size describing the size of the Scrollable
/// itself.
///
/// The callback's return value is used as the new scroll offset to
/// aim for.
///
/// If the callback simply returns its first argument (the offset),
/// then it is as if the callback was null.
final SnapOffsetCallback snapOffsetCallback; final SnapOffsetCallback snapOffsetCallback;
final double snapAlignmentOffset; // What does this do?
/// The state from the closest instance of this class that encloses the given context. /// The state from the closest instance of this class that encloses the given context.
static ScrollableState of(BuildContext context) { static ScrollableState of(BuildContext context) {
return context.ancestorStateOfType(const TypeMatcher<ScrollableState>()); return context.ancestorStateOfType(const TypeMatcher<ScrollableState>());
...@@ -294,7 +302,8 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -294,7 +302,8 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
/// Returns the snapped offset closest to the given scroll offset. /// Returns the snapped offset closest to the given scroll offset.
double snapScrollOffset(double scrollOffset) { double snapScrollOffset(double scrollOffset) {
return config.snapOffsetCallback == null ? scrollOffset : config.snapOffsetCallback(scrollOffset); RenderBox box = context.findRenderObject();
return config.snapOffsetCallback == null ? scrollOffset : config.snapOffsetCallback(scrollOffset, box.size);
} }
/// Whether this scrollable should attempt to snap scroll offsets. /// Whether this scrollable should attempt to snap scroll offsets.
...@@ -312,20 +321,20 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -312,20 +321,20 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
if (endScrollOffset.isNaN) if (endScrollOffset.isNaN)
return null; return null;
final double snappedScrollOffset = snapScrollOffset(endScrollOffset + config.snapAlignmentOffset); final double snappedScrollOffset = snapScrollOffset(endScrollOffset);
final double alignedScrollOffset = snappedScrollOffset - config.snapAlignmentOffset; if (!_scrollOffsetIsInBounds(snappedScrollOffset))
if (!_scrollOffsetIsInBounds(alignedScrollOffset))
return null; return null;
final double snapVelocity = scrollVelocity.abs() * (alignedScrollOffset - scrollOffset).sign; final double snapVelocity = scrollVelocity.abs() * (snappedScrollOffset - scrollOffset).sign;
final double endVelocity = pixelOffsetToScrollOffset(kPixelScrollTolerance.velocity).abs() * scrollVelocity.sign; final double endVelocity = pixelOffsetToScrollOffset(kPixelScrollTolerance.velocity).abs() * scrollVelocity.sign;
Simulation toSnapSimulation = Simulation toSnapSimulation = scrollBehavior.createSnapScrollSimulation(
scrollBehavior.createSnapScrollSimulation(scrollOffset, alignedScrollOffset, snapVelocity, endVelocity); scrollOffset, snappedScrollOffset, snapVelocity, endVelocity
);
if (toSnapSimulation == null) if (toSnapSimulation == null)
return null; return null;
final double scrollOffsetMin = math.min(scrollOffset, alignedScrollOffset); final double scrollOffsetMin = math.min(scrollOffset, snappedScrollOffset);
final double scrollOffsetMax = math.max(scrollOffset, alignedScrollOffset); final double scrollOffsetMax = math.max(scrollOffset, snappedScrollOffset);
return new ClampedSimulation(toSnapSimulation, xMin: scrollOffsetMin, xMax: scrollOffsetMax); return new ClampedSimulation(toSnapSimulation, xMin: scrollOffsetMin, xMax: scrollOffsetMax);
} }
...@@ -631,7 +640,6 @@ class ScrollableMixedWidgetList extends Scrollable { ...@@ -631,7 +640,6 @@ class ScrollableMixedWidgetList extends Scrollable {
double initialScrollOffset, double initialScrollOffset,
ScrollListener onScroll, ScrollListener onScroll,
SnapOffsetCallback snapOffsetCallback, SnapOffsetCallback snapOffsetCallback,
double snapAlignmentOffset: 0.0,
this.builder, this.builder,
this.token, this.token,
this.onInvalidatorAvailable this.onInvalidatorAvailable
...@@ -639,8 +647,7 @@ class ScrollableMixedWidgetList extends Scrollable { ...@@ -639,8 +647,7 @@ class ScrollableMixedWidgetList extends Scrollable {
key: key, key: key,
initialScrollOffset: initialScrollOffset, initialScrollOffset: initialScrollOffset,
onScroll: onScroll, onScroll: onScroll,
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback
snapAlignmentOffset: snapAlignmentOffset
); );
final IndexedBuilder builder; final IndexedBuilder builder;
......
...@@ -20,7 +20,6 @@ class ScrollableGrid extends Scrollable { ...@@ -20,7 +20,6 @@ class ScrollableGrid extends Scrollable {
double initialScrollOffset, double initialScrollOffset,
ScrollListener onScroll, ScrollListener onScroll,
SnapOffsetCallback snapOffsetCallback, SnapOffsetCallback snapOffsetCallback,
double snapAlignmentOffset: 0.0,
this.delegate, this.delegate,
this.children this.children
}) : super( }) : super(
...@@ -31,8 +30,7 @@ class ScrollableGrid extends Scrollable { ...@@ -31,8 +30,7 @@ class ScrollableGrid extends Scrollable {
// delegate that places children in column-major order. // delegate that places children in column-major order.
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
onScroll: onScroll, onScroll: onScroll,
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback
snapAlignmentOffset: snapAlignmentOffset
); );
final GridDelegate delegate; final GridDelegate delegate;
......
...@@ -19,7 +19,6 @@ class ScrollableList extends Scrollable { ...@@ -19,7 +19,6 @@ class ScrollableList extends Scrollable {
ViewportAnchor scrollAnchor: ViewportAnchor.start, ViewportAnchor scrollAnchor: ViewportAnchor.start,
ScrollListener onScroll, ScrollListener onScroll,
SnapOffsetCallback snapOffsetCallback, SnapOffsetCallback snapOffsetCallback,
double snapAlignmentOffset: 0.0,
this.itemExtent, this.itemExtent,
this.itemsWrap: false, this.itemsWrap: false,
this.padding, this.padding,
...@@ -31,8 +30,7 @@ class ScrollableList extends Scrollable { ...@@ -31,8 +30,7 @@ class ScrollableList extends Scrollable {
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
scrollAnchor: scrollAnchor, scrollAnchor: scrollAnchor,
onScroll: onScroll, onScroll: onScroll,
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback
snapAlignmentOffset: snapAlignmentOffset
) { ) {
assert(itemExtent != null); assert(itemExtent != null);
} }
...@@ -269,7 +267,6 @@ class ScrollableLazyList extends Scrollable { ...@@ -269,7 +267,6 @@ class ScrollableLazyList extends Scrollable {
ViewportAnchor scrollAnchor: ViewportAnchor.start, ViewportAnchor scrollAnchor: ViewportAnchor.start,
ScrollListener onScroll, ScrollListener onScroll,
SnapOffsetCallback snapOffsetCallback, SnapOffsetCallback snapOffsetCallback,
double snapAlignmentOffset: 0.0,
this.itemExtent, this.itemExtent,
this.itemCount, this.itemCount,
this.itemBuilder, this.itemBuilder,
...@@ -281,8 +278,7 @@ class ScrollableLazyList extends Scrollable { ...@@ -281,8 +278,7 @@ class ScrollableLazyList extends Scrollable {
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
scrollAnchor: scrollAnchor, scrollAnchor: scrollAnchor,
onScroll: onScroll, onScroll: onScroll,
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback
snapAlignmentOffset: snapAlignmentOffset
) { ) {
assert(itemExtent != null); assert(itemExtent != null);
assert(itemBuilder != null); assert(itemBuilder != null);
......
...@@ -20,7 +20,7 @@ Widget buildItem(int item) { ...@@ -20,7 +20,7 @@ Widget buildItem(int item) {
); );
} }
double snapOffsetCallback(double offset) { double snapOffsetCallback(double offset, Size size) {
return (offset / itemExtent).floor() * itemExtent; return (offset / itemExtent).floor() * itemExtent;
} }
......
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