anchor.diff:      if (!needPaint(oldMetrics) && !needPaint(metrics)) {
         return;
       }
  -
       notifyListeners();
     }
   
  
positive.code:     }
    }
  
    double _thumbExtent() {
      // Thumb extent reflects fraction of content visible, as long as this
      // isn't less than the absolute minimum size.
      // _totalContentExtent >= viewportDimension, so (_totalContentExtent - _mainAxisPadding) > 0
      final double fractionVisible = clampDouble(
          (_lastMetrics!.extentInside - _mainAxisPadding) /
              (_totalContentExtent - _mainAxisPadding),
          0.0,
          1.0);
  
      final double thumbExtent = math.max(
        math.min(_traversableTrackExtent, minOverscrollLength),
        _traversableTrackExtent * fractionVisible,
      );
  
      final double fractionOverscrolled = 1.0 - _lastMetrics!.extentInside / _lastMetrics!.viewportDimension;
      final double safeMinLength = math.min(minLength, _traversableTrackExtent);
      final double newMinLength = (_beforeExtent > 0 && _afterExtent > 0)
        // Thumb extent is no smaller than minLength if scrolling normally.
        ? safeMinLength
        // User is overscrolling. Thumb extent can be less than minLength
        // but no smaller than minOverscrollLength. We can't use the
        // fractionVisible to produce intermediate values between minLength and
        // minOverscrollLength when the user is transitioning from regular
        // scrolling to overscrolling, so we instead use the percentage of the
        // content that is still in the viewport to determine the size of the
        // thumb. iOS behavior appears to have the thumb reach its minimum size
        // with ~20% of overscroll. We map the percentage of minLength from
        // [0.8, 1.0] to [0.0, 1.0], so 0% to 20% of overscroll will produce
        // values for the thumb that range between minLength and the smallest
        // possible value, minOverscrollLength.
        : safeMinLength * (1.0 - clampDouble(fractionOverscrolled, 0.0, 0.2) / 0.2);
  
      // The `thumbExtent` should be no greater than `trackSize`, otherwise
      // the scrollbar may scroll towards the wrong direction.
      final double extent = clampDouble(thumbExtent, newMinLength, _traversableTrackExtent);
      return extent;
    }
  
    @override
    void dispose() {
      fadeoutOpacityAnimation.removeListener(notifyListeners);
      super.dispose();
    }
  
    bool get _isVertical => _lastAxisDirection == AxisDirection.down || _lastAxisDirection == AxisDirection.up;
    bool get _isReversed => _lastAxisDirection == AxisDirection.up || _lastAxisDirection == AxisDirection.left;
    // The amount of scroll distance before and after the current position.
    double get _beforeExtent => _isReversed ? _lastMetrics!.extentAfter : _lastMetrics!.extentBefore;
    double get _afterExtent => _isReversed ? _lastMetrics!.extentBefore : _lastMetrics!.extentAfter;
    // Padding of the thumb track.
    double get _mainAxisPadding => _isVertical ? padding.vertical : padding.horizontal;
    // The length of the painted track.
    double get _trackExtent => _lastMetrics!.viewportDimension - _mainAxisPadding;
    // The length of the track that is traversable by the thumb.
    double get _traversableTrackExtent => _trackExtent - (2 * mainAxisMargin);
  
    // The total size of the scrollable content.
    double get _totalContentExtent {
      return _lastMetrics!.maxScrollExtent
        - _lastMetrics!.minScrollExtent
        + _lastMetrics!.viewportDimension;
    }
  
    /// Convert between a thumb track position and the corresponding scroll
    /// position.
    ///
    /// thumbOffsetLocal is a position in the thumb track. Cannot be null.
    double getTrackToScroll(double thumbOffsetLocal) {
      assert(thumbOffsetLocal != null);
      final double scrollableExtent = _lastMetrics!.maxScrollExtent - _lastMetrics!.minScrollExtent;
      final double thumbMovableExtent = _traversableTrackExtent - _thumbExtent();
  
      return scrollableExtent * thumbOffsetLocal / thumbMovableExtent;
    }
  
    // Converts between a scroll position and the corresponding position in the
    // thumb track.
    double _getScrollToTrack(ScrollMetrics metrics, double thumbExtent) {
      final double scrollableExtent = metrics.maxScrollExtent - metrics.minScrollExtent;
  
      final double fractionPast = (scrollableExtent > 0)
        ? clampDouble((metrics.pixels - metrics.minScrollExtent) / scrollableExtent, 0.0, 1.0)
        : 0;
  
      return (_isReversed ? 1 - fractionPast : fractionPast) * (_traversableTrackExtent - thumbExtent);
    }
  
    @override
    void paint(Canvas canvas, Size size) {
      if (_lastAxisDirection == null
  
negative.code:         case TargetPlatform.iOS:
          case TargetPlatform.android:
            // We can only drag the scrollbar into overscroll on mobile
            // platforms, and only then if the physics allow it.
            break;
        }
        position.jumpTo(newPosition);
  