Commit f08b51b8 authored by Adam Barth's avatar Adam Barth

Merge pull request #2858 from abarth/physics_with_changing_behavior

Scrollable physics should be reasonable when sizes change
parents 1db09ffe f7f1259b
......@@ -124,6 +124,12 @@ class AnimationController extends Animation<double>
_checkStatusChanged();
}
/// The amount of time that has passed between the time the animation started and the most recent tick of the animation.
///
/// If the controller is not animating, the last elapsed duration is null;
Duration get lastElapsedDuration => _lastElapsedDuration;
Duration _lastElapsedDuration;
/// Whether this animation is currently animating in either the forward or reverse direction.
bool get isAnimating => _ticker.isTicking;
......@@ -205,6 +211,7 @@ class AnimationController extends Animation<double>
assert(simulation != null);
assert(!isAnimating);
_simulation = simulation;
_lastElapsedDuration = const Duration();
_value = simulation.x(0.0).clamp(lowerBound, upperBound);
Future<Null> result = _ticker.start();
_checkStatusChanged();
......@@ -214,6 +221,7 @@ class AnimationController extends Animation<double>
/// Stops running this animation.
void stop() {
_simulation = null;
_lastElapsedDuration = null;
_ticker.stop();
}
......@@ -233,6 +241,7 @@ class AnimationController extends Animation<double>
}
void _tick(Duration elapsed) {
_lastElapsedDuration = elapsed;
double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND;
_value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound);
if (_simulation.isDone(elapsedInSeconds))
......
......@@ -768,7 +768,7 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
}
void _updateScrollBehavior() {
scrollTo(scrollBehavior.updateExtents(
didUpdateScrollBehavior(scrollBehavior.updateExtents(
containerExtent: config.scrollDirection == Axis.vertical ? _viewportSize.height : _viewportSize.width,
contentExtent: _tabWidths.reduce((double sum, double width) => sum + width),
scrollOffset: scrollOffset
......@@ -943,11 +943,11 @@ class _TabBarViewState<T> extends PageableListState<TabBarView<T>> implements Ta
void _updateScrollBehaviorForSelectedIndex(int selectedIndex) {
if (selectedIndex == 0) {
scrollTo(scrollBehavior.updateExtents(contentExtent: 2.0, containerExtent: 1.0, scrollOffset: 0.0));
didUpdateScrollBehavior(scrollBehavior.updateExtents(contentExtent: 2.0, containerExtent: 1.0, scrollOffset: 0.0));
} else if (selectedIndex == _tabCount - 1) {
scrollTo(scrollBehavior.updateExtents(contentExtent: 2.0, containerExtent: 1.0, scrollOffset: 1.0));
didUpdateScrollBehavior(scrollBehavior.updateExtents(contentExtent: 2.0, containerExtent: 1.0, scrollOffset: 1.0));
} else {
scrollTo(scrollBehavior.updateExtents(contentExtent: 3.0, containerExtent: 1.0, scrollOffset: 1.0));
didUpdateScrollBehavior(scrollBehavior.updateExtents(contentExtent: 3.0, containerExtent: 1.0, scrollOffset: 1.0));
}
}
......
......@@ -235,7 +235,7 @@ class RawInputLineState extends ScrollableState<RawInputLine> {
// render object via our return value.
_containerWidth = dimensions.containerSize.width;
_contentWidth = dimensions.contentSize.width;
scrollTo(scrollBehavior.updateExtents(
didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: _contentWidth,
containerExtent: _containerWidth,
// Set the scroll offset to match the content width so that the
......
......@@ -147,7 +147,7 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> {
void _updateScrollBehavior() {
config.scrollableListPainter?.contentExtent = _itemCount.toDouble();
scrollTo(scrollBehavior.updateExtents(
didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: _itemCount.toDouble(),
containerExtent: 1.0,
scrollOffset: scrollOffset
......
......@@ -226,10 +226,12 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
_scrollOffset = PageStorage.of(context)?.readState(context) ?? config.initialScrollOffset ?? 0.0;
}
Simulation _simulation;
AnimationController _controller;
@override
void dispose() {
_simulation = null;
_controller.stop();
super.dispose();
}
......@@ -358,6 +360,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return new Future<Null>.value();
if (duration == null) {
_simulation = null;
_controller.stop();
_setScrollOffset(newScrollOffset);
return new Future<Null>.value();
......@@ -368,12 +371,27 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
}
Future<Null> _animateTo(double newScrollOffset, Duration duration, Curve curve) {
_simulation = null;
_controller.stop();
_controller.value = scrollOffset;
_startScroll();
return _controller.animateTo(newScrollOffset, duration: duration, curve: curve).then(_endScroll);
}
void didUpdateScrollBehavior(double newScrollOffset) {
if (newScrollOffset == _scrollOffset)
return;
if (_numberOfInProgressScrolls > 0) {
if (_simulation != null) {
double dx = _simulation.dx(_controller.lastElapsedDuration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND);
// TODO(abarth): We should be consistent about the units we use for velocity (i.e., per second).
_startToEndAnimation(dx / Duration.MILLISECONDS_PER_SECOND);
}
return;
}
scrollTo(newScrollOffset);
}
/// Fling the scroll offset with the given velocity.
///
/// Calling this function starts a physics-based animation of the scroll
......@@ -390,17 +408,18 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
/// Calling this function starts a physics-based animation of the scroll
/// offset either to a snap point or to within the scrolling bounds. The
/// physics simulation used is determined by the scroll behavior.
Future<Null> settleScrollOffset() {
Future<Null> settleScrollOffset() {
return _startToEndAnimation(0.0);
}
Future<Null> _startToEndAnimation(double scrollVelocity) {
_simulation = null;
_controller.stop();
Simulation simulation = _createSnapSimulation(scrollVelocity) ?? _createFlingSimulation(scrollVelocity);
if (simulation == null)
_simulation = _createSnapSimulation(scrollVelocity) ?? _createFlingSimulation(scrollVelocity);
if (_simulation == null)
return new Future<Null>.value();
_startScroll();
return _controller.animateWith(simulation).then(_endScroll);
return _controller.animateWith(_simulation).then(_endScroll);
}
/// Whether this scrollable should attempt to snap scroll offsets.
......@@ -471,6 +490,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
}
void _handleDragDown(_) {
_simulation = null;
_controller.stop();
}
......@@ -653,7 +673,7 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
// render object via our return value.
_viewportSize = config.scrollDirection == Axis.vertical ? dimensions.containerSize.height : dimensions.containerSize.width;
_childSize = config.scrollDirection == Axis.vertical ? dimensions.contentSize.height : dimensions.contentSize.width;
scrollTo(scrollBehavior.updateExtents(
didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: _childSize,
containerExtent: _viewportSize,
scrollOffset: scrollOffset
......@@ -819,7 +839,7 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg
// setState() callback because we are called during layout and all
// we're updating is the new offset, which we are providing to the
// render object via our return value.
scrollTo(scrollBehavior.updateExtents(
didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: dimensions.contentSize.height,
containerExtent: dimensions.containerSize.height,
scrollOffset: scrollOffset
......
......@@ -50,7 +50,7 @@ class _ScrollableGridState extends ScrollableState<ScrollableGrid> {
void _handleExtentsChanged(double contentExtent, double containerExtent) {
setState(() {
scrollTo(scrollBehavior.updateExtents(
didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: contentExtent,
containerExtent: containerExtent,
scrollOffset: scrollOffset
......
......@@ -55,7 +55,7 @@ class _ScrollableListState extends ScrollableState<ScrollableList> {
void _handleExtentsChanged(double contentExtent, double containerExtent) {
config.scrollableListPainter?.contentExtent = contentExtent;
setState(() {
scrollTo(scrollBehavior.updateExtents(
didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: config.itemsWrap ? double.INFINITY : contentExtent,
containerExtent: containerExtent,
scrollOffset: scrollOffset
......@@ -338,7 +338,7 @@ class _ScrollableLazyListState extends ScrollableState<ScrollableLazyList> {
void _handleExtentsChanged(double contentExtent, double containerExtent) {
config.scrollableListPainter?.contentExtent = contentExtent;
setState(() {
scrollTo(scrollBehavior.updateExtents(
didUpdateScrollBehavior(scrollBehavior.updateExtents(
contentExtent: contentExtent,
containerExtent: containerExtent,
scrollOffset: scrollOffset
......
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