Commit bec3fdef authored by Hans Muller's avatar Hans Muller

Add a Scrollable builder, refactor ScrollableList, et al (#3950)

* Add a Scrollable builder, refactor ScrollableList, et al

* Add space between the dialog demo buttons

* removed vestigial code
parent 0e8f26dd
...@@ -197,6 +197,14 @@ class DialogDemoState extends State<DialogDemo> { ...@@ -197,6 +197,14 @@ class DialogDemoState extends State<DialogDemo> {
} }
) )
] ]
// Add a little space between the buttons
.map((Widget button) {
return new Container(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: button
);
})
.toList()
) )
); );
} }
......
...@@ -130,74 +130,109 @@ class LazyBlockChildren extends LazyBlockDelegate { ...@@ -130,74 +130,109 @@ class LazyBlockChildren extends LazyBlockDelegate {
/// Prefer [ScrollableList] when all the children have the same height because /// Prefer [ScrollableList] when all the children have the same height because
/// it can use that property to be more efficient. Prefer [ScrollableViewport] /// it can use that property to be more efficient. Prefer [ScrollableViewport]
/// when there is only one child. /// when there is only one child.
class LazyBlock extends Scrollable { class LazyBlock extends StatelessWidget {
LazyBlock({ LazyBlock({
Key key, Key key,
double initialScrollOffset, this.initialScrollOffset,
Axis scrollDirection: Axis.vertical, this.scrollDirection: Axis.vertical,
ScrollListener onScrollStart, this.onScrollStart,
ScrollListener onScroll, this.onScroll,
ScrollListener onScrollEnd, this.onScrollEnd,
SnapOffsetCallback snapOffsetCallback, this.snapOffsetCallback,
this.delegate, this.scrollableKey,
this.padding this.padding,
}) : super( this.delegate
key: key, }) : super(key: key);
initialScrollOffset: initialScrollOffset,
scrollDirection: scrollDirection,
onScrollStart: onScrollStart,
onScroll: onScroll,
onScrollEnd: onScrollEnd,
snapOffsetCallback: snapOffsetCallback
);
/// Provides children for this widget. /// The scroll offset this widget should use when first created.
final double initialScrollOffset;
/// The axis along which this widget should scroll.
final Axis scrollDirection;
/// Called whenever this widget starts to scroll.
final ScrollListener onScrollStart;
/// Called whenever this widget's scroll offset changes.
final ScrollListener onScroll;
/// Called whenever this widget stops scrolling.
final ScrollListener onScrollEnd;
/// Called to determine the offset to which scrolling should snap,
/// when handling a fling.
/// ///
/// See [LazyBlockDelegate] for details. /// This callback, if set, will be called with the offset that the
final LazyBlockDelegate delegate; /// 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;
/// The key for the Scrollable created by this widget.
final Key scrollableKey;
/// The amount of space by which to inset the children inside the viewport. /// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding; final EdgeInsets padding;
@override /// Provides children for this widget.
ScrollableState<LazyBlock> createState() => new _LazyBlockState(); ///
} /// See [LazyBlockDelegate] for details.
final LazyBlockDelegate delegate;
class _LazyBlockState extends ScrollableState<LazyBlock> {
@override
BoundedBehavior createScrollBehavior() => new OverscrollWhenScrollableBehavior();
@override
BoundedBehavior get scrollBehavior => super.scrollBehavior;
void _handleExtentsChanged(double contentExtent, double containerExtent, double minScrollOffset) { void _handleExtentsChanged(
setState(() { ScrollableState state,
didUpdateScrollBehavior(scrollBehavior.updateExtents( double contentExtent,
double containerExtent,
double minScrollOffset) {
state.setState(() {
final BoundedBehavior scrollBehavior = state.scrollBehavior;
state.didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: contentExtent, contentExtent: contentExtent,
containerExtent: containerExtent, containerExtent: containerExtent,
minScrollOffset: minScrollOffset, minScrollOffset: minScrollOffset,
scrollOffset: scrollOffset scrollOffset: state.scrollOffset
)); ));
}); });
} }
@override Widget _buildContent(BuildContext context, ScrollableState state) {
Widget buildContent(BuildContext context) {
final bool clampOverscrolls = ClampOverscrolls.of(context); final bool clampOverscrolls = ClampOverscrolls.of(context);
final double startOffset = clampOverscrolls final double startOffset = clampOverscrolls
? scrollOffset.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset) ? state.scrollOffset.clamp(state.scrollBehavior.minScrollOffset, state.scrollBehavior.maxScrollOffset)
: scrollOffset; : state.scrollOffset;
Widget viewport = new LazyBlockViewport( Widget viewport = new LazyBlockViewport(
startOffset: startOffset, startOffset: startOffset,
mainAxis: config.scrollDirection, mainAxis: scrollDirection,
padding: config.padding, padding: padding,
onExtentsChanged: _handleExtentsChanged, onExtentsChanged: (double contentExtent, double containerExtent, double minScrollOffset) {
delegate: config.delegate _handleExtentsChanged(state, contentExtent, containerExtent, minScrollOffset);
},
delegate: delegate
); );
if (clampOverscrolls) if (clampOverscrolls)
viewport = new ClampOverscrolls(value: false, child: viewport); viewport = new ClampOverscrolls(value: false, child: viewport);
return viewport; return viewport;
} }
@override
Widget build(BuildContext context) {
return new Scrollable(
key: scrollableKey,
initialScrollOffset: initialScrollOffset,
scrollDirection: scrollDirection,
onScrollStart: onScrollStart,
onScroll: onScroll,
onScrollEnd: onScrollEnd,
snapOffsetCallback: snapOffsetCallback,
builder: _buildContent
);
}
} }
/// Signature used by [LazyBlockViewport] to report its interior and exterior dimensions. /// Signature used by [LazyBlockViewport] to report its interior and exterior dimensions.
......
...@@ -8,67 +8,93 @@ import 'package:collection/collection.dart' show lowerBound; ...@@ -8,67 +8,93 @@ import 'package:collection/collection.dart' show lowerBound;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'framework.dart'; import 'framework.dart';
import 'scroll_behavior.dart';
import 'scrollable.dart'; import 'scrollable.dart';
import 'virtual_viewport.dart'; import 'virtual_viewport.dart';
/// A vertically scrollable grid. /// A vertically scrollable grid.
/// ///
/// Requires that delegate places its children in row-major order. /// Requires that delegate places its children in row-major order.
class ScrollableGrid extends Scrollable { class ScrollableGrid extends StatelessWidget {
ScrollableGrid({ ScrollableGrid({
Key key, Key key,
double initialScrollOffset, this.initialScrollOffset,
ScrollListener onScrollStart, this.onScrollStart,
ScrollListener onScroll, this.onScroll,
ScrollListener onScrollEnd, this.onScrollEnd,
SnapOffsetCallback snapOffsetCallback, this.snapOffsetCallback,
this.scrollableKey,
this.delegate, this.delegate,
this.children this.children
}) : super( }) : super(key: key);
key: key,
initialScrollOffset: initialScrollOffset, /// The scroll offset this widget should use when first created.
// TODO(abarth): Support horizontal offsets. For horizontally scrolling final double initialScrollOffset;
// grids. For horizontally scrolling grids, we'll probably need to use a
// delegate that places children in column-major order. /// Called whenever this widget starts to scroll.
scrollDirection: Axis.vertical, final ScrollListener onScrollStart;
onScrollStart: onScrollStart,
onScroll: onScroll, /// Called whenever this widget's scroll offset changes.
onScrollEnd: onScrollEnd, final ScrollListener onScroll;
snapOffsetCallback: snapOffsetCallback
); /// Called whenever this widget stops scrolling.
final ScrollListener onScrollEnd;
/// 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;
/// The key for the Scrollable created by this widget.
final Key scrollableKey;
final GridDelegate delegate; final GridDelegate delegate;
final Iterable<Widget> children; final Iterable<Widget> children;
@override void _handleExtentsChanged(ScrollableState state, double contentExtent, double containerExtent) {
ScrollableState createState() => new _ScrollableGridState(); state.setState(() {
} state.didUpdateScrollBehavior(state.scrollBehavior.updateExtents(
class _ScrollableGridState extends ScrollableState<ScrollableGrid> {
@override
ExtentScrollBehavior createScrollBehavior() => new OverscrollBehavior();
@override
ExtentScrollBehavior get scrollBehavior => super.scrollBehavior;
void _handleExtentsChanged(double contentExtent, double containerExtent) {
setState(() {
didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: contentExtent, contentExtent: contentExtent,
containerExtent: containerExtent, containerExtent: containerExtent,
scrollOffset: scrollOffset scrollOffset: state.scrollOffset
)); ));
}); });
} }
@override Widget _buildContent(BuildContext context, ScrollableState state) {
Widget buildContent(BuildContext context) {
return new GridViewport( return new GridViewport(
startOffset: scrollOffset, startOffset: state.scrollOffset,
delegate: config.delegate, delegate: delegate,
onExtentsChanged: _handleExtentsChanged, onExtentsChanged: (double contentExtent, double containerExtent) {
children: config.children _handleExtentsChanged(state, contentExtent, containerExtent);
},
children: children
);
}
@override
Widget build(BuildContext context) {
return new Scrollable(
key: scrollableKey,
initialScrollOffset: initialScrollOffset,
// TODO(abarth): Support horizontal offsets. For horizontally scrolling
// grids. For horizontally scrolling grids, we'll probably need to use a
// delegate that places children in column-major order.
scrollDirection: Axis.vertical,
onScrollStart: onScrollStart,
onScroll: onScroll,
onScrollEnd: onScrollEnd,
snapOffsetCallback: snapOffsetCallback,
builder: _buildContent
); );
} }
} }
......
...@@ -46,7 +46,7 @@ void main() { ...@@ -46,7 +46,7 @@ void main() {
expect(find.text('10'), findsNothing); expect(find.text('10'), findsNothing);
expect(find.text('100'), findsNothing); expect(find.text('100'), findsNothing);
ScrollableState targetState = tester.state(find.byType(ScrollableLazyList)); ScrollableState targetState = tester.state(find.byType(Scrollable));
targetState.scrollTo(1000.0); targetState.scrollTo(1000.0);
await tester.pump(new Duration(seconds: 1)); await tester.pump(new Duration(seconds: 1));
......
...@@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart'; ...@@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart';
Widget _buildScroller({Key key, List<String> log}) { Widget _buildScroller({Key key, List<String> log}) {
return new ScrollableViewport( return new ScrollableViewport(
key: key, scrollableKey: key,
onScrollStart: (double scrollOffset) { onScrollStart: (double scrollOffset) {
log.add('scrollstart'); log.add('scrollstart');
}, },
......
...@@ -13,8 +13,8 @@ void main() { ...@@ -13,8 +13,8 @@ void main() {
] ]
)); ));
ScrollableState<ScrollableViewport> scrollable = ScrollableState scrollable =
tester.state/*<ScrollableState<ScrollableViewport>>*/(find.byType(ScrollableViewport)); tester.state/*<ScrollableState>*/(find.byType(Scrollable));
expect(scrollable.scrollOffset, equals(0.0)); expect(scrollable.scrollOffset, equals(0.0));
......
...@@ -75,10 +75,10 @@ void main() { ...@@ -75,10 +75,10 @@ void main() {
return result; return result;
}; };
GlobalKey<ScrollableState<ScrollableLazyList>> scrollableKey = new GlobalKey<ScrollableState<ScrollableLazyList>>(); GlobalKey<ScrollableState> scrollableKey = new GlobalKey<ScrollableState>();
FlipWidget testWidget = new FlipWidget( FlipWidget testWidget = new FlipWidget(
left: new ScrollableLazyList( left: new ScrollableLazyList(
key: scrollableKey, scrollableKey: scrollableKey,
itemBuilder: itemBuilder, itemBuilder: itemBuilder,
itemExtent: 200.0, itemExtent: 200.0,
initialScrollOffset: 300.0 initialScrollOffset: 300.0
...@@ -123,10 +123,10 @@ void main() { ...@@ -123,10 +123,10 @@ void main() {
return result; return result;
}; };
GlobalKey<ScrollableState<ScrollableLazyList>> scrollableKey = new GlobalKey<ScrollableState<ScrollableLazyList>>(); GlobalKey<ScrollableState> scrollableKey = new GlobalKey<ScrollableState>();
FlipWidget testWidget = new FlipWidget( FlipWidget testWidget = new FlipWidget(
left: new ScrollableLazyList( left: new ScrollableLazyList(
key: scrollableKey, scrollableKey: scrollableKey,
itemBuilder: itemBuilder, itemBuilder: itemBuilder,
itemExtent: 200.0, itemExtent: 200.0,
initialScrollOffset: 300.0, initialScrollOffset: 300.0,
...@@ -167,9 +167,9 @@ void main() { ...@@ -167,9 +167,9 @@ void main() {
return result; return result;
}; };
GlobalKey<ScrollableState<ScrollableLazyList>> scrollableKey = new GlobalKey<ScrollableState<ScrollableLazyList>>(); GlobalKey<ScrollableState> scrollableKey = new GlobalKey<ScrollableState>();
Widget testWidget = new ScrollableLazyList( Widget testWidget = new ScrollableLazyList(
key: scrollableKey, scrollableKey: scrollableKey,
itemBuilder: itemBuilder, itemBuilder: itemBuilder,
itemExtent: 300.0, itemExtent: 300.0,
itemCount: 10 itemCount: 10
......
...@@ -29,7 +29,7 @@ Widget buildFrame() { ...@@ -29,7 +29,7 @@ Widget buildFrame() {
child: new Container( child: new Container(
height: itemExtent * 2.0, height: itemExtent * 2.0,
child: new ScrollableList( child: new ScrollableList(
key: scrollableListKey, scrollableKey: scrollableListKey,
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback,
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
itemExtent: itemExtent, itemExtent: 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