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();
|
_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.
|
/// Whether this animation is currently animating in either the forward or reverse direction.
|
||||||
bool get isAnimating => _ticker.isTicking;
|
bool get isAnimating => _ticker.isTicking;
|
||||||
|
|
||||||
@ -205,6 +211,7 @@ class AnimationController extends Animation<double>
|
|||||||
assert(simulation != null);
|
assert(simulation != null);
|
||||||
assert(!isAnimating);
|
assert(!isAnimating);
|
||||||
_simulation = simulation;
|
_simulation = simulation;
|
||||||
|
_lastElapsedDuration = const Duration();
|
||||||
_value = simulation.x(0.0).clamp(lowerBound, upperBound);
|
_value = simulation.x(0.0).clamp(lowerBound, upperBound);
|
||||||
Future<Null> result = _ticker.start();
|
Future<Null> result = _ticker.start();
|
||||||
_checkStatusChanged();
|
_checkStatusChanged();
|
||||||
@ -214,6 +221,7 @@ class AnimationController extends Animation<double>
|
|||||||
/// Stops running this animation.
|
/// Stops running this animation.
|
||||||
void stop() {
|
void stop() {
|
||||||
_simulation = null;
|
_simulation = null;
|
||||||
|
_lastElapsedDuration = null;
|
||||||
_ticker.stop();
|
_ticker.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,6 +241,7 @@ class AnimationController extends Animation<double>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _tick(Duration elapsed) {
|
void _tick(Duration elapsed) {
|
||||||
|
_lastElapsedDuration = elapsed;
|
||||||
double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND;
|
double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND;
|
||||||
_value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound);
|
_value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound);
|
||||||
if (_simulation.isDone(elapsedInSeconds))
|
if (_simulation.isDone(elapsedInSeconds))
|
||||||
|
@ -768,7 +768,7 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _updateScrollBehavior() {
|
void _updateScrollBehavior() {
|
||||||
scrollTo(scrollBehavior.updateExtents(
|
didUpdateScrollBehavior(scrollBehavior.updateExtents(
|
||||||
containerExtent: config.scrollDirection == Axis.vertical ? _viewportSize.height : _viewportSize.width,
|
containerExtent: config.scrollDirection == Axis.vertical ? _viewportSize.height : _viewportSize.width,
|
||||||
contentExtent: _tabWidths.reduce((double sum, double width) => sum + width),
|
contentExtent: _tabWidths.reduce((double sum, double width) => sum + width),
|
||||||
scrollOffset: scrollOffset
|
scrollOffset: scrollOffset
|
||||||
@ -943,11 +943,11 @@ class _TabBarViewState<T> extends PageableListState<TabBarView<T>> implements Ta
|
|||||||
|
|
||||||
void _updateScrollBehaviorForSelectedIndex(int selectedIndex) {
|
void _updateScrollBehaviorForSelectedIndex(int selectedIndex) {
|
||||||
if (selectedIndex == 0) {
|
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) {
|
} 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 {
|
} 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.
|
// render object via our return value.
|
||||||
_containerWidth = dimensions.containerSize.width;
|
_containerWidth = dimensions.containerSize.width;
|
||||||
_contentWidth = dimensions.contentSize.width;
|
_contentWidth = dimensions.contentSize.width;
|
||||||
scrollTo(scrollBehavior.updateExtents(
|
didUpdateScrollBehavior(scrollBehavior.updateExtents(
|
||||||
contentExtent: _contentWidth,
|
contentExtent: _contentWidth,
|
||||||
containerExtent: _containerWidth,
|
containerExtent: _containerWidth,
|
||||||
// Set the scroll offset to match the content width so that the
|
// 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() {
|
void _updateScrollBehavior() {
|
||||||
config.scrollableListPainter?.contentExtent = _itemCount.toDouble();
|
config.scrollableListPainter?.contentExtent = _itemCount.toDouble();
|
||||||
scrollTo(scrollBehavior.updateExtents(
|
didUpdateScrollBehavior(scrollBehavior.updateExtents(
|
||||||
contentExtent: _itemCount.toDouble(),
|
contentExtent: _itemCount.toDouble(),
|
||||||
containerExtent: 1.0,
|
containerExtent: 1.0,
|
||||||
scrollOffset: scrollOffset
|
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;
|
_scrollOffset = PageStorage.of(context)?.readState(context) ?? config.initialScrollOffset ?? 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Simulation _simulation;
|
||||||
AnimationController _controller;
|
AnimationController _controller;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_simulation = null;
|
||||||
_controller.stop();
|
_controller.stop();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
@ -358,6 +360,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
|||||||
return new Future<Null>.value();
|
return new Future<Null>.value();
|
||||||
|
|
||||||
if (duration == null) {
|
if (duration == null) {
|
||||||
|
_simulation = null;
|
||||||
_controller.stop();
|
_controller.stop();
|
||||||
_setScrollOffset(newScrollOffset);
|
_setScrollOffset(newScrollOffset);
|
||||||
return new Future<Null>.value();
|
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) {
|
Future<Null> _animateTo(double newScrollOffset, Duration duration, Curve curve) {
|
||||||
|
_simulation = null;
|
||||||
_controller.stop();
|
_controller.stop();
|
||||||
_controller.value = scrollOffset;
|
_controller.value = scrollOffset;
|
||||||
_startScroll();
|
_startScroll();
|
||||||
return _controller.animateTo(newScrollOffset, duration: duration, curve: curve).then(_endScroll);
|
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.
|
/// Fling the scroll offset with the given velocity.
|
||||||
///
|
///
|
||||||
/// Calling this function starts a physics-based animation of the scroll
|
/// 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
|
/// Calling this function starts a physics-based animation of the scroll
|
||||||
/// offset either to a snap point or to within the scrolling bounds. The
|
/// offset either to a snap point or to within the scrolling bounds. The
|
||||||
/// physics simulation used is determined by the scroll behavior.
|
/// physics simulation used is determined by the scroll behavior.
|
||||||
Future<Null> settleScrollOffset() {
|
Future<Null> settleScrollOffset() {
|
||||||
return _startToEndAnimation(0.0);
|
return _startToEndAnimation(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Null> _startToEndAnimation(double scrollVelocity) {
|
Future<Null> _startToEndAnimation(double scrollVelocity) {
|
||||||
|
_simulation = null;
|
||||||
_controller.stop();
|
_controller.stop();
|
||||||
Simulation simulation = _createSnapSimulation(scrollVelocity) ?? _createFlingSimulation(scrollVelocity);
|
_simulation = _createSnapSimulation(scrollVelocity) ?? _createFlingSimulation(scrollVelocity);
|
||||||
if (simulation == null)
|
if (_simulation == null)
|
||||||
return new Future<Null>.value();
|
return new Future<Null>.value();
|
||||||
_startScroll();
|
_startScroll();
|
||||||
return _controller.animateWith(simulation).then(_endScroll);
|
return _controller.animateWith(_simulation).then(_endScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this scrollable should attempt to snap scroll offsets.
|
/// Whether this scrollable should attempt to snap scroll offsets.
|
||||||
@ -471,6 +490,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragDown(_) {
|
void _handleDragDown(_) {
|
||||||
|
_simulation = null;
|
||||||
_controller.stop();
|
_controller.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,7 +673,7 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
|
|||||||
// render object via our return value.
|
// render object via our return value.
|
||||||
_viewportSize = config.scrollDirection == Axis.vertical ? dimensions.containerSize.height : dimensions.containerSize.width;
|
_viewportSize = config.scrollDirection == Axis.vertical ? dimensions.containerSize.height : dimensions.containerSize.width;
|
||||||
_childSize = config.scrollDirection == Axis.vertical ? dimensions.contentSize.height : dimensions.contentSize.width;
|
_childSize = config.scrollDirection == Axis.vertical ? dimensions.contentSize.height : dimensions.contentSize.width;
|
||||||
scrollTo(scrollBehavior.updateExtents(
|
didUpdateScrollBehavior(scrollBehavior.updateExtents(
|
||||||
contentExtent: _childSize,
|
contentExtent: _childSize,
|
||||||
containerExtent: _viewportSize,
|
containerExtent: _viewportSize,
|
||||||
scrollOffset: scrollOffset
|
scrollOffset: scrollOffset
|
||||||
@ -819,7 +839,7 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg
|
|||||||
// setState() callback because we are called during layout and all
|
// setState() callback because we are called during layout and all
|
||||||
// we're updating is the new offset, which we are providing to the
|
// we're updating is the new offset, which we are providing to the
|
||||||
// render object via our return value.
|
// render object via our return value.
|
||||||
scrollTo(scrollBehavior.updateExtents(
|
didUpdateScrollBehavior(scrollBehavior.updateExtents(
|
||||||
contentExtent: dimensions.contentSize.height,
|
contentExtent: dimensions.contentSize.height,
|
||||||
containerExtent: dimensions.containerSize.height,
|
containerExtent: dimensions.containerSize.height,
|
||||||
scrollOffset: scrollOffset
|
scrollOffset: scrollOffset
|
||||||
|
@ -50,7 +50,7 @@ class _ScrollableGridState extends ScrollableState<ScrollableGrid> {
|
|||||||
|
|
||||||
void _handleExtentsChanged(double contentExtent, double containerExtent) {
|
void _handleExtentsChanged(double contentExtent, double containerExtent) {
|
||||||
setState(() {
|
setState(() {
|
||||||
scrollTo(scrollBehavior.updateExtents(
|
didUpdateScrollBehavior(scrollBehavior.updateExtents(
|
||||||
contentExtent: contentExtent,
|
contentExtent: contentExtent,
|
||||||
containerExtent: containerExtent,
|
containerExtent: containerExtent,
|
||||||
scrollOffset: scrollOffset
|
scrollOffset: scrollOffset
|
||||||
|
@ -55,7 +55,7 @@ class _ScrollableListState extends ScrollableState<ScrollableList> {
|
|||||||
void _handleExtentsChanged(double contentExtent, double containerExtent) {
|
void _handleExtentsChanged(double contentExtent, double containerExtent) {
|
||||||
config.scrollableListPainter?.contentExtent = contentExtent;
|
config.scrollableListPainter?.contentExtent = contentExtent;
|
||||||
setState(() {
|
setState(() {
|
||||||
scrollTo(scrollBehavior.updateExtents(
|
didUpdateScrollBehavior(scrollBehavior.updateExtents(
|
||||||
contentExtent: config.itemsWrap ? double.INFINITY : contentExtent,
|
contentExtent: config.itemsWrap ? double.INFINITY : contentExtent,
|
||||||
containerExtent: containerExtent,
|
containerExtent: containerExtent,
|
||||||
scrollOffset: scrollOffset
|
scrollOffset: scrollOffset
|
||||||
@ -338,7 +338,7 @@ class _ScrollableLazyListState extends ScrollableState<ScrollableLazyList> {
|
|||||||
void _handleExtentsChanged(double contentExtent, double containerExtent) {
|
void _handleExtentsChanged(double contentExtent, double containerExtent) {
|
||||||
config.scrollableListPainter?.contentExtent = contentExtent;
|
config.scrollableListPainter?.contentExtent = contentExtent;
|
||||||
setState(() {
|
setState(() {
|
||||||
scrollTo(scrollBehavior.updateExtents(
|
didUpdateScrollBehavior(scrollBehavior.updateExtents(
|
||||||
contentExtent: contentExtent,
|
contentExtent: contentExtent,
|
||||||
containerExtent: containerExtent,
|
containerExtent: containerExtent,
|
||||||
scrollOffset: scrollOffset
|
scrollOffset: scrollOffset
|
||||||
|
Loading…
x
Reference in New Issue
Block a user