Commit c21fcf62 authored by Hans Muller's avatar Hans Muller

Support ScrollableLists that wrap

Adds itemsWrap:bool (default false) to ScrollableList and PageableList. If itemsWrap is true then scrolling past the last item wraps around to the first. Similarly, scrolling before the first item wraps around to the last.

Added abstract ExtentScrollBehavior of ScrollBehavior. Renamed fields called contentsExtents to contentExtent, containerExtents to containerExtent, contentSize to contentExtent, etc.

BoundedBehavior is now a subclass of ExtentScrollBehavior.

Added UnboundedBehavior subclass of ExtentScrollBehvaior; contentExtent and maxScrollOffset are double.INFINITY, minScrollExtent is double.NEGATIVE_INFINITY.
parent d7ed623e
...@@ -26,8 +26,6 @@ class PageableListApp extends App { ...@@ -26,8 +26,6 @@ class PageableListApp extends App {
void initState() { void initState() {
List<Size> cardSizes = [ List<Size> cardSizes = [
[100.0, 300.0], [300.0, 100.0], [200.0, 400.0], [400.0, 400.0], [300.0, 400.0],
[100.0, 300.0], [300.0, 100.0], [200.0, 400.0], [400.0, 400.0], [300.0, 400.0],
[100.0, 300.0], [300.0, 100.0], [200.0, 400.0], [400.0, 400.0], [300.0, 400.0] [100.0, 300.0], [300.0, 100.0], [200.0, 400.0], [400.0, 400.0], [300.0, 400.0]
] ]
.map((args) => new Size(args[0], args[1])) .map((args) => new Size(args[0], args[1]))
...@@ -47,15 +45,6 @@ class PageableListApp extends App { ...@@ -47,15 +45,6 @@ class PageableListApp extends App {
}); });
} }
EventDisposition handleToolbarTap(_) {
setState(() {
scrollDirection = (scrollDirection == ScrollDirection.vertical)
? ScrollDirection.horizontal
: ScrollDirection.vertical;
});
return EventDisposition.processed;
}
Widget buildCard(CardModel cardModel) { Widget buildCard(CardModel cardModel) {
Widget card = new Card( Widget card = new Card(
color: cardModel.color, color: cardModel.color,
...@@ -78,16 +67,88 @@ class PageableListApp extends App { ...@@ -78,16 +67,88 @@ class PageableListApp extends App {
); );
} }
Widget build() { EventDisposition switchScrollDirection() {
setState(() {
scrollDirection = (scrollDirection == ScrollDirection.vertical)
? ScrollDirection.horizontal
: ScrollDirection.vertical;
});
return EventDisposition.processed;
}
bool _drawerShowing = false;
AnimationStatus _drawerStatus = AnimationStatus.dismissed;
void _handleOpenDrawer() {
setState(() {
_drawerShowing = true;
_drawerStatus = AnimationStatus.forward;
});
}
void _handleDrawerDismissed() {
setState(() {
_drawerStatus = AnimationStatus.dismissed;
});
}
Drawer buildDrawer() {
if (_drawerStatus == AnimationStatus.dismissed)
return null;
return new Drawer(
level: 3,
showing: _drawerShowing,
onDismissed: _handleDrawerDismissed,
children: [
new DrawerHeader(child: new Text('Options')),
new DrawerItem(
icon: 'navigation/more_horiz',
selected: scrollDirection == ScrollDirection.horizontal,
child: new Text('Horizontal Layout'),
onPressed: switchScrollDirection
),
new DrawerItem(
icon: 'navigation/more_vert',
selected: scrollDirection == ScrollDirection.vertical,
child: new Text('Vertical Layout'),
onPressed: switchScrollDirection
)
]
);
}
Widget buildToolBar() {
return new ToolBar(
left: new IconButton(icon: "navigation/menu", onPressed: _handleOpenDrawer),
center: new Text('PageableList'),
right: [
new Text(scrollDirection == ScrollDirection.horizontal ? "horizontal" : "vertical")
]
);
}
Widget buildBody() {
Widget list = new PageableList<CardModel>( Widget list = new PageableList<CardModel>(
items: cardModels, items: cardModels,
itemsWrap: true,
itemBuilder: buildCard, itemBuilder: buildCard,
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
itemExtent: (scrollDirection == ScrollDirection.vertical) itemExtent: (scrollDirection == ScrollDirection.vertical)
? pageSize.height ? pageSize.height
: pageSize.width : pageSize.width
); );
return new SizeObserver(
callback: updatePageSize,
child: new Container(
child: list,
decoration: new BoxDecoration(backgroundColor: Theme.of(this).primarySwatch[50])
)
);
}
Widget build() {
return new IconTheme( return new IconTheme(
data: const IconThemeData(color: IconThemeColor.white), data: const IconThemeData(color: IconThemeColor.white),
child: new Theme( child: new Theme(
...@@ -99,17 +160,9 @@ class PageableListApp extends App { ...@@ -99,17 +160,9 @@ class PageableListApp extends App {
child: new Title( child: new Title(
title: 'PageableList', title: 'PageableList',
child: new Scaffold( child: new Scaffold(
toolbar: new Listener( drawer: buildDrawer(),
onGestureTap: handleToolbarTap, toolbar: buildToolBar(),
child: new ToolBar(center: new Text('PageableList: ${scrollDirection}')) body: buildBody()
),
body: new SizeObserver(
callback: updatePageSize,
child: new Container(
child: list,
decoration: new BoxDecoration(backgroundColor: Theme.of(this).primarySwatch[50])
)
)
) )
) )
) )
......
...@@ -7,6 +7,7 @@ import 'dart:math' as math; ...@@ -7,6 +7,7 @@ import 'dart:math' as math;
import 'package:newton/newton.dart'; import 'package:newton/newton.dart';
const double _kSecondsPerMillisecond = 1000.0; const double _kSecondsPerMillisecond = 1000.0;
const double _kScrollDrag = 0.025;
abstract class ScrollBehavior { abstract class ScrollBehavior {
Simulation release(double position, double velocity) => null; Simulation release(double position, double velocity) => null;
...@@ -15,49 +16,74 @@ abstract class ScrollBehavior { ...@@ -15,49 +16,74 @@ abstract class ScrollBehavior {
double applyCurve(double scrollOffset, double scrollDelta); double applyCurve(double scrollOffset, double scrollDelta);
} }
class BoundedBehavior extends ScrollBehavior { abstract class ExtentScrollBehavior extends ScrollBehavior {
BoundedBehavior({ double contentsSize: 0.0, double containerSize: 0.0 }) ExtentScrollBehavior({ double contentExtent: 0.0, double containerExtent: 0.0 })
: _contentsExtents = contentsSize, : _contentExtent = contentExtent, _containerExtent = containerExtent;
_containerExtents = containerSize;
double _contentsExtents; double _contentExtent;
double get contentsExtents => _contentsExtents; double get contentExtent => _contentExtent;
double _containerExtents; double _containerExtent;
double get containerExtents => _containerExtents; double get containerExtent => _containerExtent;
/// Returns the new scrollOffset. /// Returns the new scrollOffset.
double updateExtents({ double updateExtents({
double contentsExtents, double contentExtent,
double containerExtents, double containerExtent,
double scrollOffset: 0.0 double scrollOffset: 0.0
}) { }) {
if (contentsExtents != null) if (contentExtent != null)
_contentsExtents = contentsExtents; _contentExtent = contentExtent;
if (containerExtents != null) if (containerExtent != null)
_containerExtents = containerExtents; _containerExtent = containerExtent;
return scrollOffset.clamp(minScrollOffset, maxScrollOffset); return scrollOffset.clamp(minScrollOffset, maxScrollOffset);
} }
final double minScrollOffset = 0.0; double get minScrollOffset;
double get maxScrollOffset => math.max(0.0, _contentsExtents - _containerExtents); double get maxScrollOffset;
}
class BoundedBehavior extends ExtentScrollBehavior {
BoundedBehavior({ double contentExtent: 0.0, double containerExtent: 0.0 })
: super(contentExtent: contentExtent, containerExtent: containerExtent);
double minScrollOffset = 0.0;
double get maxScrollOffset => math.max(minScrollOffset, minScrollOffset + _contentExtent - _containerExtent);
double applyCurve(double scrollOffset, double scrollDelta) { double applyCurve(double scrollOffset, double scrollDelta) {
return (scrollOffset + scrollDelta).clamp(minScrollOffset, maxScrollOffset); return (scrollOffset + scrollDelta).clamp(minScrollOffset, maxScrollOffset);
} }
} }
class UnboundedBehavior extends ExtentScrollBehavior {
UnboundedBehavior({ double contentExtent: 0.0, double containerExtent: 0.0 })
: super(contentExtent: contentExtent, containerExtent: containerExtent);
Simulation release(double position, double velocity) {
double velocityPerSecond = velocity * 1000.0;
return new BoundedFrictionSimulation(
_kScrollDrag, position, velocityPerSecond, double.NEGATIVE_INFINITY, double.INFINITY
);
}
double get minScrollOffset => double.NEGATIVE_INFINITY;
double get maxScrollOffset => double.INFINITY;
double applyCurve(double scrollOffset, double scrollDelta) {
return scrollOffset + scrollDelta;
}
}
Simulation createDefaultScrollSimulation(double position, double velocity, double minScrollOffset, double maxScrollOffset) { Simulation createDefaultScrollSimulation(double position, double velocity, double minScrollOffset, double maxScrollOffset) {
double velocityPerSecond = velocity * _kSecondsPerMillisecond; double velocityPerSecond = velocity * _kSecondsPerMillisecond;
SpringDescription spring = new SpringDescription.withDampingRatio( SpringDescription spring = new SpringDescription.withDampingRatio(
mass: 1.0, springConstant: 170.0, ratio: 1.1); mass: 1.0, springConstant: 170.0, ratio: 1.1);
double drag = 0.025; return new ScrollSimulation(position, velocityPerSecond, minScrollOffset, maxScrollOffset, spring, _kScrollDrag);
return new ScrollSimulation(position, velocityPerSecond, minScrollOffset, maxScrollOffset, spring, drag);
} }
class OverscrollBehavior extends BoundedBehavior { class OverscrollBehavior extends BoundedBehavior {
OverscrollBehavior({ double contentsSize: 0.0, double containerSize: 0.0 }) OverscrollBehavior({ double contentExtent: 0.0, double containerExtent: 0.0 })
: super(contentsSize: contentsSize, containerSize: containerSize); : super(contentExtent: contentExtent, containerExtent: containerExtent);
Simulation release(double position, double velocity) { Simulation release(double position, double velocity) {
return createDefaultScrollSimulation(position, velocity, minScrollOffset, maxScrollOffset); return createDefaultScrollSimulation(position, velocity, minScrollOffset, maxScrollOffset);
...@@ -81,7 +107,7 @@ class OverscrollBehavior extends BoundedBehavior { ...@@ -81,7 +107,7 @@ class OverscrollBehavior extends BoundedBehavior {
} }
class OverscrollWhenScrollableBehavior extends OverscrollBehavior { class OverscrollWhenScrollableBehavior extends OverscrollBehavior {
bool get isScrollable => contentsExtents > containerExtents; bool get isScrollable => contentExtent > containerExtent;
Simulation release(double position, double velocity) { Simulation release(double position, double velocity) {
if (isScrollable || position < minScrollOffset || position > maxScrollOffset) if (isScrollable || position < minScrollOffset || position > maxScrollOffset)
......
...@@ -245,7 +245,7 @@ Future ensureWidgetIsVisible(Widget target, { Duration duration, Curve curve }) ...@@ -245,7 +245,7 @@ Future ensureWidgetIsVisible(Widget target, { Duration duration, Curve curve })
double scrollOffsetDelta = scrollable.scrollDirection == ScrollDirection.vertical double scrollOffsetDelta = scrollable.scrollDirection == ScrollDirection.vertical
? targetCenter.y - scrollableCenter.y ? targetCenter.y - scrollableCenter.y
: targetCenter.x - scrollableCenter.x; : targetCenter.x - scrollableCenter.x;
BoundedBehavior scrollBehavior = scrollable.scrollBehavior; ExtentScrollBehavior scrollBehavior = scrollable.scrollBehavior;
double scrollOffset = (scrollable.scrollOffset + scrollOffsetDelta) double scrollOffset = (scrollable.scrollOffset + scrollOffsetDelta)
.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset); .clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
...@@ -286,8 +286,8 @@ class ScrollableViewport extends Scrollable { ...@@ -286,8 +286,8 @@ class ScrollableViewport extends Scrollable {
} }
void _updateScrollBehaviour() { void _updateScrollBehaviour() {
scrollTo(scrollBehavior.updateExtents( scrollTo(scrollBehavior.updateExtents(
contentsExtents: _childSize, contentExtent: _childSize,
containerExtents: _viewportSize, containerExtent: _viewportSize,
scrollOffset: scrollOffset)); scrollOffset: scrollOffset));
} }
...@@ -338,6 +338,7 @@ class Block extends Component { ...@@ -338,6 +338,7 @@ class Block extends Component {
/// widget when you have a large number of children or when you are concerned /// widget when you have a large number of children or when you are concerned
// about offscreen widgets consuming resources. // about offscreen widgets consuming resources.
abstract class ScrollableWidgetList extends Scrollable { abstract class ScrollableWidgetList extends Scrollable {
static const _kEpsilon = .0000001;
ScrollableWidgetList({ ScrollableWidgetList({
Key key, Key key,
...@@ -377,7 +378,7 @@ abstract class ScrollableWidgetList extends Scrollable { ...@@ -377,7 +378,7 @@ abstract class ScrollableWidgetList extends Scrollable {
} }
ScrollBehavior createScrollBehavior() => new OverscrollBehavior(); ScrollBehavior createScrollBehavior() => new OverscrollBehavior();
OverscrollBehavior get scrollBehavior => super.scrollBehavior; ExtentScrollBehavior get scrollBehavior => super.scrollBehavior;
double get _containerExtent { double get _containerExtent {
return scrollDirection == ScrollDirection.vertical return scrollDirection == ScrollDirection.vertical
...@@ -394,14 +395,14 @@ abstract class ScrollableWidgetList extends Scrollable { ...@@ -394,14 +395,14 @@ abstract class ScrollableWidgetList extends Scrollable {
double get _leadingPadding { double get _leadingPadding {
if (scrollDirection == ScrollDirection.vertical) if (scrollDirection == ScrollDirection.vertical)
return padding.top; return padding != null ? padding.top : 0.0;
return padding.left; return padding != null ? padding.left : -.0;
} }
double get _trailingPadding { double get _trailingPadding {
if (scrollDirection == ScrollDirection.vertical) if (scrollDirection == ScrollDirection.vertical)
return padding.bottom; return padding != null ? padding.bottom : 0.0;
return padding.right; return padding != null ? padding.right : 0.0;
} }
EdgeDims get _crossAxisPadding { EdgeDims get _crossAxisPadding {
...@@ -413,13 +414,13 @@ abstract class ScrollableWidgetList extends Scrollable { ...@@ -413,13 +414,13 @@ abstract class ScrollableWidgetList extends Scrollable {
} }
void _updateScrollBehavior() { void _updateScrollBehavior() {
double contentsExtent = itemExtent * itemCount; double contentExtent = itemExtent * itemCount;
if (padding != null) if (padding != null)
contentsExtent += _leadingPadding + _trailingPadding; contentExtent += _leadingPadding + _trailingPadding;
scrollTo(scrollBehavior.updateExtents( scrollTo(scrollBehavior.updateExtents(
contentsExtents: contentsExtent, contentExtent: contentExtent,
containerExtents: _containerExtent, containerExtent: _containerExtent,
scrollOffset: scrollOffset)); scrollOffset: scrollOffset));
} }
...@@ -435,31 +436,33 @@ abstract class ScrollableWidgetList extends Scrollable { ...@@ -435,31 +436,33 @@ abstract class ScrollableWidgetList extends Scrollable {
_updateScrollBehavior(); _updateScrollBehavior();
} }
double paddedScrollOffset = scrollOffset; double paddedScrollOffset = scrollOffset - _leadingPadding;
if (padding != null)
paddedScrollOffset -= _leadingPadding;
int itemShowIndex = 0; int itemShowIndex = 0;
int itemShowCount = 0; int itemShowCount = 0;
Offset viewportOffset = Offset.zero; Offset viewportOffset = Offset.zero;
if (_containerExtent != null && _containerExtent > 0.0) { if (_containerExtent != null && _containerExtent > 0.0) {
if (paddedScrollOffset < 0.0) { if (paddedScrollOffset < scrollBehavior.minScrollOffset) {
double visibleHeight = _containerExtent + paddedScrollOffset; // Underscroll
itemShowCount = (visibleHeight / itemExtent).round() + 1; double visibleExtent = _containerExtent + paddedScrollOffset;
itemShowCount = (visibleExtent / itemExtent).round() + 1;
viewportOffset = _toOffset(paddedScrollOffset); viewportOffset = _toOffset(paddedScrollOffset);
} else { } else {
itemShowCount = (_containerExtent / itemExtent).ceil(); itemShowCount = (_containerExtent / itemExtent).ceil();
double alignmentDelta = -paddedScrollOffset % itemExtent; double alignmentDelta = (-paddedScrollOffset % itemExtent);
double drawStart; double drawStart = paddedScrollOffset;
if (alignmentDelta != 0.0) { if (alignmentDelta != 0.0) {
alignmentDelta -= itemExtent; alignmentDelta -= itemExtent;
itemShowCount += 1; itemShowCount += 1;
drawStart = paddedScrollOffset + alignmentDelta; drawStart += alignmentDelta;
viewportOffset = _toOffset(-alignmentDelta); viewportOffset = _toOffset(-alignmentDelta);
} else {
drawStart = paddedScrollOffset;
} }
itemShowIndex = math.max(0, (drawStart / itemExtent).floor()); if (itemCount > 0) {
// floor(epsilon) = 0, floor(-epsilon) = -1, so:
if (drawStart.abs() < _kEpsilon)
drawStart = 0.0;
itemShowIndex = (drawStart / itemExtent).floor() % itemCount;
}
} }
} }
...@@ -501,26 +504,34 @@ class ScrollableList<T> extends ScrollableWidgetList { ...@@ -501,26 +504,34 @@ class ScrollableList<T> extends ScrollableWidgetList {
ScrollDirection scrollDirection: ScrollDirection.vertical, ScrollDirection scrollDirection: ScrollDirection.vertical,
this.items, this.items,
this.itemBuilder, this.itemBuilder,
this.itemsWrap: false,
double itemExtent, double itemExtent,
EdgeDims padding EdgeDims padding
}) : super(key: key, scrollDirection: scrollDirection, itemExtent: itemExtent, padding: padding); }) : super(key: key, scrollDirection: scrollDirection, itemExtent: itemExtent, padding: padding);
List<T> items; List<T> items;
ItemBuilder<T> itemBuilder; ItemBuilder<T> itemBuilder;
bool itemsWrap;
void syncConstructorArguments(ScrollableList<T> source) { void syncConstructorArguments(ScrollableList<T> source) {
items = source.items; items = source.items;
itemBuilder = source.itemBuilder; itemBuilder = source.itemBuilder;
itemsWrap = source.itemsWrap;
super.syncConstructorArguments(source); super.syncConstructorArguments(source);
} }
ScrollBehavior createScrollBehavior() {
return itemsWrap ? new UnboundedBehavior() : super.createScrollBehavior();
}
int get itemCount => items.length; int get itemCount => items.length;
List<Widget> buildItems(int start, int count) { List<Widget> buildItems(int start, int count) {
List<Widget> result = new List<Widget>(); List<Widget> result = new List<Widget>();
int end = math.min(start + count, items.length); int begin = itemsWrap ? start : math.max(0, start);
for (int i = start; i < end; ++i) int end = itemsWrap ? begin + count : math.min(begin + count, items.length);
result.add(itemBuilder(items[i])); for (int i = begin; i < end; ++i)
result.add(itemBuilder(items[i % itemCount]));
return result; return result;
} }
} }
...@@ -531,6 +542,7 @@ class PageableList<T> extends ScrollableList<T> { ...@@ -531,6 +542,7 @@ class PageableList<T> extends ScrollableList<T> {
ScrollDirection scrollDirection: ScrollDirection.horizontal, ScrollDirection scrollDirection: ScrollDirection.horizontal,
List<T> items, List<T> items,
ItemBuilder<T> itemBuilder, ItemBuilder<T> itemBuilder,
bool itemsWrap: false,
double itemExtent, double itemExtent,
EdgeDims padding, EdgeDims padding,
this.duration: const Duration(milliseconds: 200), this.duration: const Duration(milliseconds: 200),
...@@ -540,6 +552,7 @@ class PageableList<T> extends ScrollableList<T> { ...@@ -540,6 +552,7 @@ class PageableList<T> extends ScrollableList<T> {
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
items: items, items: items,
itemBuilder: itemBuilder, itemBuilder: itemBuilder,
itemsWrap: itemsWrap,
itemExtent: itemExtent, itemExtent: itemExtent,
padding: padding padding: padding
); );
...@@ -600,7 +613,7 @@ class ScrollableMixedWidgetList extends Scrollable { ...@@ -600,7 +613,7 @@ class ScrollableMixedWidgetList extends Scrollable {
// changed. Remember as much so that after the new contents // changed. Remember as much so that after the new contents
// have been laid out we can adjust the scrollOffset so that // have been laid out we can adjust the scrollOffset so that
// the last page of content is still visible. // the last page of content is still visible.
bool _contentsChanged = true; bool _contentChanged = true;
void initState() { void initState() {
assert(layoutState != null); assert(layoutState != null);
...@@ -620,7 +633,7 @@ class ScrollableMixedWidgetList extends Scrollable { ...@@ -620,7 +633,7 @@ class ScrollableMixedWidgetList extends Scrollable {
void syncConstructorArguments(ScrollableMixedWidgetList source) { void syncConstructorArguments(ScrollableMixedWidgetList source) {
builder = source.builder; builder = source.builder;
if (token != source.token) if (token != source.token)
_contentsChanged = true; _contentChanged = true;
token = source.token; token = source.token;
if (layoutState != source.layoutState) { if (layoutState != source.layoutState) {
// Warning: this is unlikely to be what you intended. // Warning: this is unlikely to be what you intended.
...@@ -637,17 +650,17 @@ class ScrollableMixedWidgetList extends Scrollable { ...@@ -637,17 +650,17 @@ class ScrollableMixedWidgetList extends Scrollable {
void _handleSizeChanged(Size newSize) { void _handleSizeChanged(Size newSize) {
scrollBy(scrollBehavior.updateExtents( scrollBy(scrollBehavior.updateExtents(
containerExtents: newSize.height, containerExtent: newSize.height,
scrollOffset: scrollOffset scrollOffset: scrollOffset
)); ));
} }
void _handleLayoutChanged() { void _handleLayoutChanged() {
double newScrollOffset = scrollBehavior.updateExtents( double newScrollOffset = scrollBehavior.updateExtents(
contentsExtents: layoutState.didReachLastChild ? layoutState.contentsSize : double.INFINITY, contentExtent: layoutState.didReachLastChild ? layoutState.contentsSize : double.INFINITY,
scrollOffset: scrollOffset); scrollOffset: scrollOffset);
if (_contentsChanged) { if (_contentChanged) {
_contentsChanged = false; _contentChanged = false;
scrollTo(newScrollOffset); scrollTo(newScrollOffset);
} }
} }
......
...@@ -340,13 +340,13 @@ class Tab extends Component { ...@@ -340,13 +340,13 @@ class Tab extends Component {
} }
Widget build() { Widget build() {
Widget labelContents; Widget labelContent;
if (label.icon == null) { if (label.icon == null) {
labelContents = _buildLabelText(); labelContent = _buildLabelText();
} else if (label.text == null) { } else if (label.text == null) {
labelContents = _buildLabelIcon(); labelContent = _buildLabelIcon();
} else { } else {
labelContents = new Flex( labelContent = new Flex(
<Widget>[ <Widget>[
new Container( new Container(
child: _buildLabelIcon(), child: _buildLabelIcon(),
...@@ -361,7 +361,7 @@ class Tab extends Component { ...@@ -361,7 +361,7 @@ class Tab extends Component {
} }
Container centeredLabel = new Container( Container centeredLabel = new Container(
child: new Center(child: labelContents), child: new Center(child: labelContent),
constraints: new BoxConstraints(minWidth: _kMinTabWidth), constraints: new BoxConstraints(minWidth: _kMinTabWidth),
padding: _kTabLabelPadding padding: _kTabLabelPadding
); );
...@@ -371,8 +371,7 @@ class Tab extends Component { ...@@ -371,8 +371,7 @@ class Tab extends Component {
} }
class _TabsScrollBehavior extends BoundedBehavior { class _TabsScrollBehavior extends BoundedBehavior {
_TabsScrollBehavior({ double contentsSize: 0.0, double containerSize: 0.0 }) _TabsScrollBehavior();
: super(contentsSize: contentsSize, containerSize: containerSize);
bool isScrollable = true; bool isScrollable = true;
...@@ -460,7 +459,7 @@ class TabBar extends Scrollable { ...@@ -460,7 +459,7 @@ class TabBar extends Scrollable {
} }
double _centeredTabScrollOffset(int tabIndex) { double _centeredTabScrollOffset(int tabIndex) {
double viewportWidth = scrollBehavior.containerExtents; double viewportWidth = scrollBehavior.containerExtent;
return (_tabRect(tabIndex).left + _tabWidths[tabIndex] / 2.0 - viewportWidth / 2.0) return (_tabRect(tabIndex).left + _tabWidths[tabIndex] / 2.0 - viewportWidth / 2.0)
.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset); .clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
} }
...@@ -496,8 +495,8 @@ class TabBar extends Scrollable { ...@@ -496,8 +495,8 @@ class TabBar extends Scrollable {
_tabBarSize = tabBarSize; _tabBarSize = tabBarSize;
_tabWidths = tabWidths; _tabWidths = tabWidths;
scrollBehavior.updateExtents( scrollBehavior.updateExtents(
containerExtents: _tabBarSize.width, containerExtent: _tabBarSize.width,
contentsExtents: _tabWidths.reduce((sum, width) => sum + width)); contentExtent: _tabWidths.reduce((sum, width) => sum + width));
}); });
} }
......
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