// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:math' as math; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; /// A description of a [Scrollable]'s contents, useful for modeling the state /// 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]. /// /// See also: /// /// * [FixedScrollMetrics], which is an immutable object that implements this /// interface. 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. /// /// 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, }) { return FixedScrollMetrics( minScrollExtent: minScrollExtent ?? this.minScrollExtent, maxScrollExtent: maxScrollExtent ?? this.maxScrollExtent, pixels: pixels ?? this.pixels, viewportDimension: viewportDimension ?? this.viewportDimension, axisDirection: axisDirection ?? this.axisDirection, ); } /// The minimum in-range value for [pixels]. /// /// The actual [pixels] value might be [outOfRange]. /// /// This value should typically be non-null and less than or equal to /// [maxScrollExtent]. It can be negative infinity, if the scroll is unbounded. double get minScrollExtent; /// The maximum in-range value for [pixels]. /// /// The actual [pixels] value might be [outOfRange]. /// /// This value should typically be non-null and greater than or equal to /// [minScrollExtent]. It can be infinity, if the scroll is unbounded. double get maxScrollExtent; /// The current scroll position, in logical pixels along the [axisDirection]. double get pixels; /// The extent of the viewport along the [axisDirection]. double get viewportDimension; /// The direction in which the scroll view scrolls. AxisDirection get axisDirection; /// The axis in which the scroll view scrolls. Axis get axis => axisDirectionToAxis(axisDirection); /// Whether the [pixels] value is outside the [minScrollExtent] and /// [maxScrollExtent]. bool get outOfRange => pixels < minScrollExtent || pixels > maxScrollExtent; /// Whether the [pixels] value is exactly at the [minScrollExtent] or the /// [maxScrollExtent]. bool get atEdge => pixels == minScrollExtent || pixels == maxScrollExtent; /// The quantity of content conceptually "above" the viewport in the scrollable. /// This is the content above the content described by [extentInside]. double get extentBefore => math.max(pixels - minScrollExtent, 0.0); /// The quantity of content conceptually "inside" the viewport in the scrollable. /// /// 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]. double get extentInside { assert(minScrollExtent <= maxScrollExtent); return viewportDimension // "above" overscroll value - (minScrollExtent - pixels).clamp(0, viewportDimension) // "below" overscroll value - (pixels - maxScrollExtent).clamp(0, viewportDimension); } /// The quantity of content conceptually "below" the viewport in the scrollable. /// This is the content below the content described by [extentInside]. double get extentAfter => math.max(maxScrollExtent - pixels, 0.0); } /// An immutable snapshot of values associated with a [Scrollable] viewport. /// /// For details, see [ScrollMetrics], which defines this object's interfaces. class FixedScrollMetrics extends ScrollMetrics { /// Creates an immutable snapshot of values associated with a [Scrollable] viewport. 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; @override String toString() { return '$runtimeType(${extentBefore.toStringAsFixed(1)}..[${extentInside.toStringAsFixed(1)}]..${extentAfter.toStringAsFixed(1)})'; } }