Merge pull request #2858 from abarth/physics_with_changing_behavior
Scrollable physics should be reasonable when sizes change
This commit is contained in:
commit
f08b51b8ed
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user