scroll_metrics.dart 5.61 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7 8 9 10 11
import 'dart:math' as math;

import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';

12
/// A description of a [Scrollable]'s contents, useful for modeling the state
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/// of its viewport.
///
/// This class defines a current position, [pixels], and a range of values
/// considered "in bounds" for that position. The range has a minimum value at
/// [minScrollExtent] and a maximum value at [maxScrollExtent] (inclusive). The
/// viewport scrolls in the direction and axis described by [axisDirection]
/// and [axis].
///
/// The [outOfRange] getter will return true if [pixels] is outside this defined
/// range. The [atEdge] getter will return true if the [pixels] position equals
/// either the [minScrollExtent] or the [maxScrollExtent].
///
/// The dimensions of the viewport in the given [axis] are described by
/// [viewportDimension].
///
/// The above values are also exposed in terms of [extentBefore],
/// [extentInside], and [extentAfter], which may be more useful for use cases
/// such as scroll bars; for example, see [Scrollbar].
31 32 33 34 35
///
/// See also:
///
///  * [FixedScrollMetrics], which is an immutable object that implements this
///    interface.
36 37 38 39 40
abstract class ScrollMetrics {
  /// Creates a [ScrollMetrics] that has the same properties as this object.
  ///
  /// This is useful if this object is mutable, but you want to get a snapshot
  /// of the current state.
41 42 43 44 45 46 47 48 49 50 51
  ///
  /// The named arguments allow the values to be adjusted in the process. This
  /// is useful to examine hypothetical situations, for example "would applying
  /// this delta unmodified take the position [outOfRange]?".
  ScrollMetrics copyWith({
    double minScrollExtent,
    double maxScrollExtent,
    double pixels,
    double viewportDimension,
    AxisDirection axisDirection,
  }) {
52
    return FixedScrollMetrics(
53 54 55 56 57 58 59
      minScrollExtent: minScrollExtent ?? this.minScrollExtent,
      maxScrollExtent: maxScrollExtent ?? this.maxScrollExtent,
      pixels: pixels ?? this.pixels,
      viewportDimension: viewportDimension ?? this.viewportDimension,
      axisDirection: axisDirection ?? this.axisDirection,
    );
  }
60

61 62 63
  /// The minimum in-range value for [pixels].
  ///
  /// The actual [pixels] value might be [outOfRange].
64
  ///
65 66
  /// This value should typically be non-null and less than or equal to
  /// [maxScrollExtent]. It can be negative infinity, if the scroll is unbounded.
67
  double get minScrollExtent;
68 69 70 71

  /// The maximum in-range value for [pixels].
  ///
  /// The actual [pixels] value might be [outOfRange].
72
  ///
73 74
  /// This value should typically be non-null and greater than or equal to
  /// [minScrollExtent]. It can be infinity, if the scroll is unbounded.
75
  double get maxScrollExtent;
76 77

  /// The current scroll position, in logical pixels along the [axisDirection].
78
  double get pixels;
79 80

  /// The extent of the viewport along the [axisDirection].
81
  double get viewportDimension;
82 83

  /// The direction in which the scroll view scrolls.
84 85
  AxisDirection get axisDirection;

86
  /// The axis in which the scroll view scrolls.
87 88
  Axis get axis => axisDirectionToAxis(axisDirection);

89 90
  /// Whether the [pixels] value is outside the [minScrollExtent] and
  /// [maxScrollExtent].
91 92
  bool get outOfRange => pixels < minScrollExtent || pixels > maxScrollExtent;

93 94
  /// Whether the [pixels] value is exactly at the [minScrollExtent] or the
  /// [maxScrollExtent].
95 96
  bool get atEdge => pixels == minScrollExtent || pixels == maxScrollExtent;

97 98
  /// The quantity of content conceptually "above" the viewport in the scrollable.
  /// This is the content above the content described by [extentInside].
99 100
  double get extentBefore => math.max(pixels - minScrollExtent, 0.0);

101
  /// The quantity of content conceptually "inside" the viewport in the scrollable.
102
  ///
103 104 105 106 107
  /// The value is typically the height of the viewport when [outOfRange] is false.
  /// It could be less if there is less content visible than the size of the
  /// viewport, such as when overscrolling.
  ///
  /// The value is always non-negative, and less than or equal to [viewportDimension].
108
  double get extentInside {
109
    assert(minScrollExtent <= maxScrollExtent);
110 111 112 113 114
    return viewportDimension
      // "above" overscroll value
      - (minScrollExtent - pixels).clamp(0, viewportDimension)
      // "below" overscroll value
      - (pixels - maxScrollExtent).clamp(0, viewportDimension);
115 116
  }

117 118
  /// The quantity of content conceptually "below" the viewport in the scrollable.
  /// This is the content below the content described by [extentInside].
119 120 121
  double get extentAfter => math.max(maxScrollExtent - pixels, 0.0);
}

122 123 124
/// An immutable snapshot of values associated with a [Scrollable] viewport.
///
/// For details, see [ScrollMetrics], which defines this object's interfaces.
125
class FixedScrollMetrics extends ScrollMetrics {
126
  /// Creates an immutable snapshot of values associated with a [Scrollable] viewport.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
  FixedScrollMetrics({
    @required this.minScrollExtent,
    @required this.maxScrollExtent,
    @required this.pixels,
    @required this.viewportDimension,
    @required this.axisDirection,
  });

  @override
  final double minScrollExtent;

  @override
  final double maxScrollExtent;

  @override
  final double pixels;

  @override
  final double viewportDimension;

  @override
  final AxisDirection axisDirection;
149 150 151

  @override
  String toString() {
152
    return '${objectRuntimeType(this, 'FixedScrollMetrics')}(${extentBefore.toStringAsFixed(1)}..[${extentInside.toStringAsFixed(1)}]..${extentAfter.toStringAsFixed(1)})';
153
  }
154
}