Commit 1f06dc44 authored by Hans Muller's avatar Hans Muller

ScrollConfiguration (#4026)

parent 10c861d6
...@@ -191,13 +191,11 @@ class ListDemoState extends State<ListDemo> { ...@@ -191,13 +191,11 @@ class ListDemoState extends State<ListDemo> {
) )
] ]
), ),
body: new OverscrollIndicator( body: new Scrollbar(
child: new Scrollbar( child: new MaterialList(
child: new MaterialList( type: _itemType,
type: _itemType, padding: new EdgeInsets.symmetric(vertical: _dense ? 4.0 : 8.0),
padding: new EdgeInsets.symmetric(vertical: _dense ? 4.0 : 8.0), children: listItems
children: listItems
)
) )
) )
); );
......
...@@ -43,17 +43,22 @@ class OverscrollDemoState extends State<OverscrollDemo> { ...@@ -43,17 +43,22 @@ class OverscrollDemoState extends State<OverscrollDemo> {
break; break;
} }
Widget body = new MaterialList( // The default ScrollConfiguration doesn't include the
type: MaterialListType.threeLine, // OverscrollIndicator. That's what we want, since this demo
padding: const EdgeInsets.all(8.0), // adds the OverscrollIndicator itself.
children: _items.map((String item) { Widget body = new ScrollConfiguration(
return new ListItem( child: new MaterialList(
isThreeLine: true, type: MaterialListType.threeLine,
leading: new CircleAvatar(child: new Text(item)), padding: const EdgeInsets.all(8.0),
title: new Text('This item represents $item.'), children: _items.map((String item) {
subtitle: new Text('Even more additional list item information appears on line three.') return new ListItem(
); isThreeLine: true,
}) leading: new CircleAvatar(child: new Text(item)),
title: new Text('This item represents $item.'),
subtitle: new Text('Even more additional list item information appears on line three.')
);
})
)
); );
switch(_type) { switch(_type) {
case IndicatorType.overscroll: case IndicatorType.overscroll:
...@@ -91,5 +96,4 @@ class OverscrollDemoState extends State<OverscrollDemo> { ...@@ -91,5 +96,4 @@ class OverscrollDemoState extends State<OverscrollDemo> {
body: body body: body
); );
} }
} }
...@@ -2,10 +2,13 @@ ...@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:io' show Platform;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'colors.dart'; import 'colors.dart';
import 'overscroll_indicator.dart';
import 'page.dart'; import 'page.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -141,6 +144,13 @@ class MaterialApp extends StatefulWidget { ...@@ -141,6 +144,13 @@ class MaterialApp extends StatefulWidget {
_MaterialAppState createState() => new _MaterialAppState(); _MaterialAppState createState() => new _MaterialAppState();
} }
class _IndicatorScrollConfigurationDelegate extends ScrollConfigurationDelegate {
@override
Widget wrapScrollWidget(Widget scrollWidget) => new OverscrollIndicator(child: scrollWidget);
}
final ScrollConfigurationDelegate _indicatorScroll = new _IndicatorScrollConfigurationDelegate();
final ScrollConfigurationDelegate _bounceScroll = new ScrollConfigurationDelegate();
class _MaterialAppState extends State<MaterialApp> { class _MaterialAppState extends State<MaterialApp> {
final HeroController _heroController = new HeroController(); final HeroController _heroController = new HeroController();
...@@ -190,6 +200,9 @@ class _MaterialAppState extends State<MaterialApp> { ...@@ -190,6 +200,9 @@ class _MaterialAppState extends State<MaterialApp> {
return true; return true;
}); });
return result; return new ScrollConfiguration(
delegate: (Platform.isIOS || Platform.isMacOS) ? _bounceScroll : _indicatorScroll,
child: result
);
} }
} }
...@@ -36,7 +36,7 @@ class FrictionSimulation extends Simulation { ...@@ -36,7 +36,7 @@ class FrictionSimulation extends Simulation {
/// velocity, and the velocities must be in the direction appropriate for the /// velocity, and the velocities must be in the direction appropriate for the
/// particle to start from the start position and reach the end position. /// particle to start from the start position and reach the end position.
factory FrictionSimulation.through(double startPosition, double endPosition, double startVelocity, double endVelocity) { factory FrictionSimulation.through(double startPosition, double endPosition, double startVelocity, double endVelocity) {
assert(startVelocity.sign == endVelocity.sign); assert(startVelocity == 0.0 || endVelocity == 0.0 || startVelocity.sign == endVelocity.sign);
assert(startVelocity.abs() >= endVelocity.abs()); assert(startVelocity.abs() >= endVelocity.abs());
assert((endPosition - startPosition).sign == startVelocity.sign); assert((endPosition - startPosition).sign == startVelocity.sign);
return new FrictionSimulation( return new FrictionSimulation(
......
...@@ -47,6 +47,7 @@ export 'package:flutter/rendering.dart' show ...@@ -47,6 +47,7 @@ export 'package:flutter/rendering.dart' show
SingleChildLayoutDelegate, SingleChildLayoutDelegate,
TextOverflow, TextOverflow,
ValueChanged, ValueChanged,
ValueGetter,
ViewportAnchor, ViewportAnchor,
ViewportDimensions, ViewportDimensions,
ViewportDimensionsChangeCallback; ViewportDimensionsChangeCallback;
......
// Copyright 2016 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 'framework.dart';
import 'scrollable.dart';
typedef Widget ViewportBuilder(BuildContext context, ScrollableState state, double scrollOffset);
/// If true, the ClampOverscroll's [Scrollable] descendant will clamp its
/// viewport's scrollOffsets to the [ScrollBehavior]'s min and max values.
/// In this case the Scrollable's scrollOffset will still over- and undershoot
/// the ScrollBehavior's limits, but the viewport itself will not.
class ClampOverscrolls extends InheritedWidget {
ClampOverscrolls({
Key key,
this.value,
Widget child
}) : super(key: key, child: child) {
assert(value != null);
assert(child != null);
}
/// True if the [Scrollable] descendant should clamp its viewport's scrollOffset
/// values when they are less than the [ScrollBehavior]'s minimum or greater than
/// its maximum.
final bool value;
static bool of(BuildContext context) {
final ClampOverscrolls result = context.inheritFromWidgetOfExactType(ClampOverscrolls);
return result?.value ?? false;
}
/// If ClampOverscrolls is true, clamps the ScrollableState's scrollOffset to the
/// [ScrollBehavior] minimum and maximum values and then constructs the viewport
/// with the clamped scrollOffset. ClampOverscrolls is reset to false for viewport
/// descendants.
///
/// This utility function is typically used by [Scrollable.builder] callbacks.
static Widget buildViewport(BuildContext context, ScrollableState state, ViewportBuilder builder) {
final bool clampOverscrolls = ClampOverscrolls.of(context);
final double clampedScrollOffset = clampOverscrolls
? state.scrollOffset.clamp(state.scrollBehavior.minScrollOffset, state.scrollBehavior.maxScrollOffset)
: state.scrollOffset;
Widget viewport = builder(context, state, clampedScrollOffset);
if (clampOverscrolls)
viewport = new ClampOverscrolls(value: false, child: viewport);
return viewport;
}
@override
bool updateShouldNotify(ClampOverscrolls old) => value != old.value;
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('value: $value');
}
}
...@@ -7,7 +7,9 @@ import 'dart:math' as math; ...@@ -7,7 +7,9 @@ import 'dart:math' as math;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'basic.dart'; import 'basic.dart';
import 'clamp_overscrolls.dart';
import 'framework.dart'; import 'framework.dart';
import 'scroll_configuration.dart';
import 'scrollable.dart'; import 'scrollable.dart';
import 'scrollable_list.dart'; import 'scrollable_list.dart';
import 'scroll_behavior.dart'; import 'scroll_behavior.dart';
...@@ -205,13 +207,9 @@ class LazyBlock extends StatelessWidget { ...@@ -205,13 +207,9 @@ class LazyBlock extends StatelessWidget {
}); });
} }
Widget _buildContent(BuildContext context, ScrollableState state) { Widget _buildViewport(BuildContext context, ScrollableState state, double scrollOffset) {
final bool clampOverscrolls = ClampOverscrolls.of(context); return new LazyBlockViewport(
final double startOffset = clampOverscrolls startOffset: scrollOffset,
? state.scrollOffset.clamp(state.scrollBehavior.minScrollOffset, state.scrollBehavior.maxScrollOffset)
: state.scrollOffset;
Widget viewport = new LazyBlockViewport(
startOffset: startOffset,
mainAxis: scrollDirection, mainAxis: scrollDirection,
padding: padding, padding: padding,
onExtentsChanged: (double contentExtent, double containerExtent, double minScrollOffset) { onExtentsChanged: (double contentExtent, double containerExtent, double minScrollOffset) {
...@@ -219,14 +217,15 @@ class LazyBlock extends StatelessWidget { ...@@ -219,14 +217,15 @@ class LazyBlock extends StatelessWidget {
}, },
delegate: delegate delegate: delegate
); );
if (clampOverscrolls) }
viewport = new ClampOverscrolls(value: false, child: viewport);
return viewport; Widget _buildContent(BuildContext context, ScrollableState state) {
return ClampOverscrolls.buildViewport(context, state, _buildViewport);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Scrollable( final Widget result = new Scrollable(
key: scrollableKey, key: scrollableKey,
initialScrollOffset: initialScrollOffset, initialScrollOffset: initialScrollOffset,
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
...@@ -236,6 +235,7 @@ class LazyBlock extends StatelessWidget { ...@@ -236,6 +235,7 @@ class LazyBlock extends StatelessWidget {
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback,
builder: _buildContent builder: _buildContent
); );
return ScrollConfiguration.wrap(context, result);
} }
} }
......
...@@ -9,6 +9,11 @@ import 'package:flutter/physics.dart'; ...@@ -9,6 +9,11 @@ import 'package:flutter/physics.dart';
const double _kSecondsPerMillisecond = 1000.0; const double _kSecondsPerMillisecond = 1000.0;
const double _kScrollDrag = 0.025; const double _kScrollDrag = 0.025;
// TODO(hansmuller): Simplify these classes. We're no longer using the ScrollBehavior<T, U>
// base class directly. Only LazyBlock uses BoundedBehavior's updateExtents minScrollOffset
// parameter; simpler to move that into ExtentScrollBehavior. All of the classes should
// be called FooScrollBehavior.
/// An interface for controlling the behavior of scrollable widgets. /// An interface for controlling the behavior of scrollable widgets.
/// ///
/// The type argument T is the type that describes the scroll offset. /// The type argument T is the type that describes the scroll offset.
...@@ -220,8 +225,14 @@ class OverscrollWhenScrollableBehavior extends OverscrollBehavior { ...@@ -220,8 +225,14 @@ class OverscrollWhenScrollableBehavior extends OverscrollBehavior {
@override @override
Simulation createScrollSimulation(double position, double velocity) { Simulation createScrollSimulation(double position, double velocity) {
if (isScrollable || position < minScrollOffset || position > maxScrollOffset) if (isScrollable || position < minScrollOffset || position > maxScrollOffset) {
// If the triggering gesture starts at or beyond the contentExtent's limits
// then the simulation only serves to settle the scrollOffset back to its
// minimum or maximum value.
if (position < minScrollOffset || position > maxScrollOffset)
velocity = 0.0;
return super.createScrollSimulation(position, velocity); return super.createScrollSimulation(position, velocity);
}
return null; return null;
} }
......
// Copyright 2016 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 'framework.dart';
import 'scroll_behavior.dart';
class ScrollConfigurationDelegate {
/// Returns the ScrollBehavior to be used by generic scrolling containers like
/// [Block]. Returns a new [OverscrollWhenScrollableBehavior] by default.
ExtentScrollBehavior createScrollBehavior() => new OverscrollWhenScrollableBehavior();
/// Generic scrolling containers like [Block] will apply this function to the
/// Scrollable they create. It can be used to add widgets that wrap the
/// Scrollable, like scrollbars or overscroll indicators. By default the
/// [scrollWidget] parameter is returned unchanged.
Widget wrapScrollWidget(Widget scrollWidget) => scrollWidget;
/// Overrides should return true if the this ScrollConfigurationDelegate has
/// changed in a way that requires rebuilding its scrolling container descendants.
/// Returns false by default.
bool updateShouldNotify(ScrollConfigurationDelegate old) => false;
}
/// Used by descendants to initialize and wrap the [Scrollable] widgets
/// they create.
///
/// Classes that create Scrollables are not required to depend on this
/// Widget. The following general purpose scrolling widgets do depend
/// on ScrollConfiguration: Block, LazyBlock, ScrollableViewport,
/// ScrollableList, ScrollableLazyList. The Scrollable base class uses
/// ScrollConfiguration to create its [ScrollBehavior].
class ScrollConfiguration extends InheritedWidget {
ScrollConfiguration({
Key key,
this.delegate,
Widget child
}) : super(key: key, child: child);
static final ScrollConfigurationDelegate _defaultDelegate = new ScrollConfigurationDelegate();
/// Defines the ScrollBehavior and scrollable wrapper for descendants.
final ScrollConfigurationDelegate delegate;
/// The delegate property of the closest instance of this class that encloses
/// the given context.
///
/// If no such instance exists, returns an instance of the
/// [ScrollConfigurationDelegate] base class.
static ScrollConfigurationDelegate of(BuildContext context) {
ScrollConfiguration configuration = context.inheritFromWidgetOfExactType(ScrollConfiguration);
return configuration?.delegate ?? _defaultDelegate;
}
/// A utility function that calls [ScrollConfigurationDelegate.wrapScrollWidget].
static Widget wrap(BuildContext context, Widget scrollWidget) {
return of(context).wrapScrollWidget(scrollWidget);
}
@override
bool updateShouldNotify(ScrollConfiguration old) {
return delegate?.updateShouldNotify(old.delegate) ?? false;
}
}
...@@ -11,11 +11,13 @@ import 'package:flutter/gestures.dart'; ...@@ -11,11 +11,13 @@ import 'package:flutter/gestures.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'basic.dart'; import 'basic.dart';
import 'clamp_overscrolls.dart';
import 'framework.dart'; import 'framework.dart';
import 'gesture_detector.dart'; import 'gesture_detector.dart';
import 'notification_listener.dart'; import 'notification_listener.dart';
import 'page_storage.dart'; import 'page_storage.dart';
import 'scroll_behavior.dart'; import 'scroll_behavior.dart';
import 'scroll_configuration.dart';
/// The accuracy to which scrolling is computed. /// The accuracy to which scrolling is computed.
final Tolerance kPixelScrollTolerance = new Tolerance( final Tolerance kPixelScrollTolerance = new Tolerance(
...@@ -319,9 +321,15 @@ class ScrollableState<T extends Scrollable> extends State<T> { ...@@ -319,9 +321,15 @@ class ScrollableState<T extends Scrollable> extends State<T> {
} }
ExtentScrollBehavior _scrollBehavior; ExtentScrollBehavior _scrollBehavior;
/// Subclasses should override this function to create the [ScrollBehavior] /// Use the value returned by [ScrollConfiguration.createScrollBehavior].
/// they desire. /// If this widget doesn't have a ScrollConfiguration ancestor,
ExtentScrollBehavior createScrollBehavior() => new OverscrollWhenScrollableBehavior(); /// or its createScrollBehavior callback is null, then return a new instance
/// of [OverscrollWhenScrollableBehavior].
ExtentScrollBehavior createScrollBehavior() {
// TODO(hansmuller): this will not be called when the ScrollConfiguration changes.
// An override of dependenciesChanged() is probably needed.
return ScrollConfiguration.of(context)?.createScrollBehavior();
}
bool _scrollOffsetIsInBounds(double scrollOffset) { bool _scrollOffsetIsInBounds(double scrollOffset) {
if (scrollBehavior is! ExtentScrollBehavior) if (scrollBehavior is! ExtentScrollBehavior)
...@@ -773,9 +781,9 @@ class _ScrollableViewportState extends State<ScrollableViewport> { ...@@ -773,9 +781,9 @@ class _ScrollableViewportState extends State<ScrollableViewport> {
return state.scrollOffsetToPixelDelta(state.scrollOffset); return state.scrollOffsetToPixelDelta(state.scrollOffset);
} }
Widget _buildContent(BuildContext context, ScrollableState state) { Widget _buildViewport(BuildContext context, ScrollableState state, double scrollOffset) {
return new Viewport( return new Viewport(
paintOffset: state.scrollOffsetToPixelDelta(state.scrollOffset), paintOffset: state.scrollOffsetToPixelDelta(scrollOffset),
mainAxis: config.scrollDirection, mainAxis: config.scrollDirection,
anchor: config.scrollAnchor, anchor: config.scrollAnchor,
onPaintOffsetUpdateNeeded: (ViewportDimensions dimensions) { onPaintOffsetUpdateNeeded: (ViewportDimensions dimensions) {
...@@ -785,9 +793,13 @@ class _ScrollableViewportState extends State<ScrollableViewport> { ...@@ -785,9 +793,13 @@ class _ScrollableViewportState extends State<ScrollableViewport> {
); );
} }
Widget _buildContent(BuildContext context, ScrollableState state) {
return ClampOverscrolls.buildViewport(context, state, _buildViewport);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Scrollable( final Widget result = new Scrollable(
key: config.scrollableKey, key: config.scrollableKey,
initialScrollOffset: config.initialScrollOffset, initialScrollOffset: config.initialScrollOffset,
scrollDirection: config.scrollDirection, scrollDirection: config.scrollDirection,
...@@ -798,6 +810,7 @@ class _ScrollableViewportState extends State<ScrollableViewport> { ...@@ -798,6 +810,7 @@ class _ScrollableViewportState extends State<ScrollableViewport> {
snapOffsetCallback: config.snapOffsetCallback, snapOffsetCallback: config.snapOffsetCallback,
builder: _buildContent builder: _buildContent
); );
return ScrollConfiguration.wrap(context, result);
} }
} }
......
...@@ -7,7 +7,9 @@ import 'dart:math' as math; ...@@ -7,7 +7,9 @@ import 'dart:math' as math;
import 'package:collection/collection.dart' show lowerBound; import 'package:collection/collection.dart' show lowerBound;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'clamp_overscrolls.dart';
import 'framework.dart'; import 'framework.dart';
import 'scroll_configuration.dart';
import 'scrollable.dart'; import 'scrollable.dart';
import 'virtual_viewport.dart'; import 'virtual_viewport.dart';
...@@ -74,9 +76,9 @@ class ScrollableGrid extends StatelessWidget { ...@@ -74,9 +76,9 @@ class ScrollableGrid extends StatelessWidget {
}); });
} }
Widget _buildContent(BuildContext context, ScrollableState state) { Widget _buildViewport(BuildContext context, ScrollableState state, double scrollOffset) {
return new GridViewport( return new GridViewport(
startOffset: state.scrollOffset, startOffset: scrollOffset,
delegate: delegate, delegate: delegate,
onExtentsChanged: (double contentExtent, double containerExtent) { onExtentsChanged: (double contentExtent, double containerExtent) {
_handleExtentsChanged(state, contentExtent, containerExtent); _handleExtentsChanged(state, contentExtent, containerExtent);
...@@ -85,9 +87,13 @@ class ScrollableGrid extends StatelessWidget { ...@@ -85,9 +87,13 @@ class ScrollableGrid extends StatelessWidget {
); );
} }
Widget _buildContent(BuildContext context, ScrollableState state) {
return ClampOverscrolls.buildViewport(context, state, _buildViewport);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Scrollable( final Widget result = new Scrollable(
key: scrollableKey, key: scrollableKey,
initialScrollOffset: initialScrollOffset, initialScrollOffset: initialScrollOffset,
// TODO(abarth): Support horizontal offsets. For horizontally scrolling // TODO(abarth): Support horizontal offsets. For horizontally scrolling
...@@ -100,6 +106,7 @@ class ScrollableGrid extends StatelessWidget { ...@@ -100,6 +106,7 @@ class ScrollableGrid extends StatelessWidget {
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback,
builder: _buildContent builder: _buildContent
); );
return ScrollConfiguration.wrap(context, result);
} }
} }
......
...@@ -4,47 +4,14 @@ ...@@ -4,47 +4,14 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'clamp_overscrolls.dart';
import 'framework.dart'; import 'framework.dart';
import 'scroll_behavior.dart'; import 'scroll_configuration.dart';
import 'scrollable.dart'; import 'scrollable.dart';
import 'virtual_viewport.dart'; import 'virtual_viewport.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
/// If true, the ClampOverscroll's [Scrollable] descendant will clamp its
/// viewport's scrollOffsets to the [ScrollBehavior]'s min and max values.
/// In this case the Scrollable's scrollOffset will still over and undershoot
/// the ScrollBehavior's limits, but the viewport itself will not.
class ClampOverscrolls extends InheritedWidget {
ClampOverscrolls({
Key key,
this.value,
Widget child
}) : super(key: key, child: child) {
assert(value != null);
assert(child != null);
}
/// True if the [Scrollable] descendant should clamp its viewport's scrollOffset
/// values when they are less than the [ScrollBehavior]'s minimum or greater than
/// its maximum.
final bool value;
static bool of(BuildContext context) {
final ClampOverscrolls result = context.inheritFromWidgetOfExactType(ClampOverscrolls);
return result?.value ?? false;
}
@override
bool updateShouldNotify(ClampOverscrolls old) => value != old.value;
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('value: $value');
}
}
class ScrollableList extends StatelessWidget { class ScrollableList extends StatelessWidget {
ScrollableList({ ScrollableList({
Key key, Key key,
...@@ -144,16 +111,12 @@ class ScrollableList extends StatelessWidget { ...@@ -144,16 +111,12 @@ class ScrollableList extends StatelessWidget {
}); });
} }
Widget _buildContent(BuildContext context, ScrollableState state) { Widget _buildViewport(BuildContext context, ScrollableState state, double scrollOffset) {
final bool clampOverscrolls = ClampOverscrolls.of(context); return new ListViewport(
final double listScrollOffset = clampOverscrolls
? state.scrollOffset.clamp(state.scrollBehavior.minScrollOffset, state.scrollBehavior.maxScrollOffset)
: state.scrollOffset;
Widget viewport = new ListViewport(
onExtentsChanged: (double contentExtent, double containerExtent) { onExtentsChanged: (double contentExtent, double containerExtent) {
_handleExtentsChanged(state, contentExtent, containerExtent); _handleExtentsChanged(state, contentExtent, containerExtent);
}, },
scrollOffset: listScrollOffset, scrollOffset: scrollOffset,
mainAxis: scrollDirection, mainAxis: scrollDirection,
anchor: scrollAnchor, anchor: scrollAnchor,
itemExtent: itemExtent, itemExtent: itemExtent,
...@@ -161,14 +124,15 @@ class ScrollableList extends StatelessWidget { ...@@ -161,14 +124,15 @@ class ScrollableList extends StatelessWidget {
padding: padding, padding: padding,
children: children children: children
); );
if (clampOverscrolls) }
viewport = new ClampOverscrolls(value: false, child: viewport);
return viewport; Widget _buildContent(BuildContext context, ScrollableState state) {
return ClampOverscrolls.buildViewport(context, state, _buildViewport);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Scrollable( final Widget result = new Scrollable(
key: scrollableKey, key: scrollableKey,
initialScrollOffset: initialScrollOffset, initialScrollOffset: initialScrollOffset,
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
...@@ -179,6 +143,7 @@ class ScrollableList extends StatelessWidget { ...@@ -179,6 +143,7 @@ class ScrollableList extends StatelessWidget {
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback,
builder: _buildContent builder: _buildContent
); );
return ScrollConfiguration.wrap(context, result);
} }
} }
...@@ -477,12 +442,12 @@ class ScrollableLazyList extends StatelessWidget { ...@@ -477,12 +442,12 @@ class ScrollableLazyList extends StatelessWidget {
}); });
} }
Widget _buildContent(BuildContext context, ScrollableState state) { Widget _buildViewport(BuildContext context, ScrollableState state, double scrollOffset) {
return new LazyListViewport( return new LazyListViewport(
onExtentsChanged: (double contentExtent, double containerExtent) { onExtentsChanged: (double contentExtent, double containerExtent) {
_handleExtentsChanged(state, contentExtent, containerExtent); _handleExtentsChanged(state, contentExtent, containerExtent);
}, },
scrollOffset: state.scrollOffset, scrollOffset: scrollOffset,
mainAxis: scrollDirection, mainAxis: scrollDirection,
anchor: scrollAnchor, anchor: scrollAnchor,
itemExtent: itemExtent, itemExtent: itemExtent,
...@@ -492,9 +457,13 @@ class ScrollableLazyList extends StatelessWidget { ...@@ -492,9 +457,13 @@ class ScrollableLazyList extends StatelessWidget {
); );
} }
Widget _buildContent(BuildContext context, ScrollableState state) {
return ClampOverscrolls.buildViewport(context, state, _buildViewport);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Scrollable( final Widget result = new Scrollable(
key: scrollableKey, key: scrollableKey,
initialScrollOffset: initialScrollOffset, initialScrollOffset: initialScrollOffset,
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
...@@ -505,6 +474,7 @@ class ScrollableLazyList extends StatelessWidget { ...@@ -505,6 +474,7 @@ class ScrollableLazyList extends StatelessWidget {
snapOffsetCallback: snapOffsetCallback, snapOffsetCallback: snapOffsetCallback,
builder: _buildContent builder: _buildContent
); );
return ScrollConfiguration.wrap(context, result);
} }
} }
......
...@@ -15,6 +15,7 @@ export 'src/widgets/banner.dart'; ...@@ -15,6 +15,7 @@ export 'src/widgets/banner.dart';
export 'src/widgets/basic.dart'; export 'src/widgets/basic.dart';
export 'src/widgets/binding.dart'; export 'src/widgets/binding.dart';
export 'src/widgets/child_view.dart'; export 'src/widgets/child_view.dart';
export 'src/widgets/clamp_overscrolls.dart';
export 'src/widgets/debug.dart'; export 'src/widgets/debug.dart';
export 'src/widgets/dismissable.dart'; export 'src/widgets/dismissable.dart';
export 'src/widgets/drag_target.dart'; export 'src/widgets/drag_target.dart';
...@@ -43,6 +44,7 @@ export 'src/widgets/placeholder.dart'; ...@@ -43,6 +44,7 @@ export 'src/widgets/placeholder.dart';
export 'src/widgets/raw_keyboard_listener.dart'; export 'src/widgets/raw_keyboard_listener.dart';
export 'src/widgets/routes.dart'; export 'src/widgets/routes.dart';
export 'src/widgets/scroll_behavior.dart'; export 'src/widgets/scroll_behavior.dart';
export 'src/widgets/scroll_configuration.dart';
export 'src/widgets/scrollable_grid.dart'; export 'src/widgets/scrollable_grid.dart';
export 'src/widgets/scrollable_list.dart'; export 'src/widgets/scrollable_list.dart';
export 'src/widgets/scrollable.dart'; export 'src/widgets/scrollable.dart';
......
...@@ -60,7 +60,7 @@ void main() { ...@@ -60,7 +60,7 @@ void main() {
Duration dt = const Duration(seconds: 2); Duration dt = const Duration(seconds: 2);
fling(0.8); fling(1.0);
await tester.pump(); // Start the scheduler at 0.0 await tester.pump(); // Start the scheduler at 0.0
await tester.pump(dt); await tester.pump(dt);
expect(scrollOffset, closeTo(200.0, 1.0)); expect(scrollOffset, closeTo(200.0, 1.0));
......
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