diff --git a/lib/common/widgets/draggable_scrollable_sheet.dart b/lib/common/widgets/draggable_scrollable_sheet.dart index c3595b129..a2e6fb2be 100644 --- a/lib/common/widgets/draggable_scrollable_sheet.dart +++ b/lib/common/widgets/draggable_scrollable_sheet.dart @@ -22,16 +22,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -/// The signature of a method that provides a [BuildContext] and -/// [ScrollController] for building a widget that may overflow the draggable -/// [Axis] of the containing [DraggableScrollableSheet]. -/// -/// Users should apply the [scrollController] to a [ScrollView] subclass, such -/// as a [SingleChildScrollView], [ListView] or [GridView], to have the whole -/// sheet be draggable. -typedef ScrollableWidgetBuilder = Widget Function( - BuildContext context, ScrollController scrollController); - /// Controls a [DraggableScrollableSheet]. /// /// Draggable scrollable controllers are typically stored as member variables in diff --git a/lib/common/widgets/page/scrollable.dart b/lib/common/widgets/page/scrollable.dart index de4d72809..da2e1f475 100644 --- a/lib/common/widgets/page/scrollable.dart +++ b/lib/common/widgets/page/scrollable.dart @@ -30,22 +30,6 @@ import 'package:get/get.dart'; export 'package:flutter/physics.dart' show Tolerance; -// Examples can assume: -// late BuildContext context; - -/// Signature used by [CustomScrollable] to build the viewport through which the -/// scrollable content is displayed. -typedef ViewportBuilder = Widget Function( - BuildContext context, ViewportOffset position); - -/// Signature used by [TwoDimensionalScrollable] to build the viewport through -/// which the scrollable content is displayed. -typedef TwoDimensionalViewportBuilder = Widget Function( - BuildContext context, - ViewportOffset verticalPosition, - ViewportOffset horizontalPosition, -); - // The return type of _performEnsureVisible. // // The list of futures represents each pending ScrollPosition call to @@ -1971,761 +1955,6 @@ enum DiagonalDragBehavior { free, } -/// A widget that manages scrolling in both the vertical and horizontal -/// dimensions and informs the [TwoDimensionalViewport] through which the -/// content is viewed. -/// -/// [TwoDimensionalScrollable] implements the interaction model for a scrollable -/// widget in both the vertical and horizontal axes, including gesture -/// recognition, but does not have an opinion about how the -/// [TwoDimensionalViewport], which actually displays the children, is -/// constructed. -/// -/// It's rare to construct a [TwoDimensionalScrollable] directly. Instead, -/// consider subclassing [TwoDimensionalScrollView], which combines scrolling, -/// viewporting, and a layout model in both dimensions. -/// -/// See also: -/// -/// * [TwoDimensionalScrollView], an abstract base class for displaying a -/// scrolling array of children in both directions. -/// * [TwoDimensionalViewport], which can be used to customize the child layout -/// model. -class TwoDimensionalScrollable extends StatefulWidget { - /// Creates a widget that scrolls in two dimensions. - /// - /// The [horizontalDetails], [verticalDetails], and [viewportBuilder] must not - /// be null. - const TwoDimensionalScrollable({ - super.key, - required this.horizontalDetails, - required this.verticalDetails, - required this.viewportBuilder, - this.incrementCalculator, - this.restorationId, - this.excludeFromSemantics = false, - this.diagonalDragBehavior = DiagonalDragBehavior.none, - this.dragStartBehavior = DragStartBehavior.start, - this.hitTestBehavior = HitTestBehavior.opaque, - }); - - /// How scrolling gestures should lock to one axis, or allow free movement - /// in both axes. - final DiagonalDragBehavior diagonalDragBehavior; - - /// The configuration of the horizontal [CustomScrollable]. - /// - /// These [ScrollableDetails] can be used to set the [AxisDirection], - /// [ScrollController], [ScrollPhysics] and more for the horizontal axis. - final ScrollableDetails horizontalDetails; - - /// The configuration of the vertical [CustomScrollable]. - /// - /// These [ScrollableDetails] can be used to set the [AxisDirection], - /// [ScrollController], [ScrollPhysics] and more for the vertical axis. - final ScrollableDetails verticalDetails; - - /// Builds the viewport through which the scrollable content is displayed. - /// - /// A [TwoDimensionalViewport] uses two given [ViewportOffset]s to determine - /// which part of its content is actually visible through the viewport. - /// - /// See also: - /// - /// * [TwoDimensionalViewport], which is a viewport that displays a span of - /// widgets in both dimensions. - final TwoDimensionalViewportBuilder viewportBuilder; - - /// {@macro flutter.widgets.Scrollable.incrementCalculator} - /// - /// This value applies in both axes. - final ScrollIncrementCalculator? incrementCalculator; - - /// {@macro flutter.widgets.scrollable.restorationId} - /// - /// Internally, the [TwoDimensionalScrollable] will introduce a - /// [RestorationScope] that will be assigned this value. The two [CustomScrollable]s - /// within will then be given unique IDs within this scope. - final String? restorationId; - - /// {@macro flutter.widgets.scrollable.excludeFromSemantics} - /// - /// This value applies to both axes. - final bool excludeFromSemantics; - - /// {@macro flutter.widgets.scrollable.hitTestBehavior} - /// - /// This value applies to both axes. - final HitTestBehavior hitTestBehavior; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - /// - /// This value applies in both axes. - final DragStartBehavior dragStartBehavior; - - @override - State createState() => - TwoDimensionalScrollableState(); - - /// The state from the closest instance of this class that encloses the given - /// context, or null if none is found. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// TwoDimensionalScrollableState? scrollable = TwoDimensionalScrollable.maybeOf(context); - /// ``` - /// - /// Calling this method will create a dependency on the closest - /// [TwoDimensionalScrollable] in the [context]. The internal [CustomScrollable]s - /// can be accessed through [TwoDimensionalScrollableState.verticalScrollable] - /// and [TwoDimensionalScrollableState.horizontalScrollable]. - /// - /// Alternatively, [CustomScrollable.maybeOf] can be used by providing the desired - /// [Axis] to the `axis` parameter. - /// - /// See also: - /// - /// * [TwoDimensionalScrollable.of], which is similar to this method, but - /// asserts if no [CustomScrollable] ancestor is found. - static TwoDimensionalScrollableState? maybeOf(BuildContext context) { - final _TwoDimensionalScrollableScope? widget = context - .dependOnInheritedWidgetOfExactType<_TwoDimensionalScrollableScope>(); - return widget?.twoDimensionalScrollable; - } - - /// The state from the closest instance of this class that encloses the given - /// context. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// TwoDimensionalScrollableState scrollable = TwoDimensionalScrollable.of(context); - /// ``` - /// - /// Calling this method will create a dependency on the closest - /// [TwoDimensionalScrollable] in the [context]. The internal [CustomScrollable]s - /// can be accessed through [TwoDimensionalScrollableState.verticalScrollable] - /// and [TwoDimensionalScrollableState.horizontalScrollable]. - /// - /// If no [TwoDimensionalScrollable] ancestor is found, then this method will - /// assert in debug mode, and throw an exception in release mode. - /// - /// Alternatively, [CustomScrollable.of] can be used by providing the desired [Axis] - /// to the `axis` parameter. - /// - /// See also: - /// - /// * [TwoDimensionalScrollable.maybeOf], which is similar to this method, - /// but returns null if no [TwoDimensionalScrollable] ancestor is found. - static TwoDimensionalScrollableState of(BuildContext context) { - final TwoDimensionalScrollableState? scrollableState = maybeOf(context); - assert(() { - if (scrollableState == null) { - throw FlutterError.fromParts([ - ErrorSummary( - 'TwoDimensionalScrollable.of() was called with a context that does ' - 'not contain a TwoDimensionalScrollable widget.\n', - ), - ErrorDescription( - 'No TwoDimensionalScrollable widget ancestor could be found starting ' - 'from the context that was passed to TwoDimensionalScrollable.of(). ' - 'This can happen because you are using a widget that looks for a ' - 'TwoDimensionalScrollable ancestor, but no such ancestor exists.\n' - 'The context used was:\n' - ' $context', - ), - ]); - } - return true; - }()); - return scrollableState!; - } -} - -/// State object for a [TwoDimensionalScrollable] widget. -/// -/// To manipulate one of the internal [CustomScrollable] widget's scroll position, use -/// the object obtained from the [verticalScrollable] or [horizontalScrollable] -/// property. -/// -/// To be informed of when a [TwoDimensionalScrollable] widget is scrolling, -/// use a [NotificationListener] to listen for [ScrollNotification]s. -/// Both axes will have the same viewport depth since there is only one -/// viewport, and so should be differentiated by the [Axis] of the -/// [ScrollMetrics] provided by the notification. -class TwoDimensionalScrollableState extends State { - ScrollController? _verticalFallbackController; - ScrollController? _horizontalFallbackController; - final GlobalKey _verticalOuterScrollableKey = - GlobalKey(); - final GlobalKey _horizontalInnerScrollableKey = - GlobalKey(); - - /// The [CustomScrollableState] of the vertical axis. - /// - /// Accessible by calling [TwoDimensionalScrollable.of]. - /// - /// Alternatively, [CustomScrollable.of] can be used by providing [Axis.vertical] - /// to the `axis` parameter. - CustomScrollableState get verticalScrollable { - assert(_verticalOuterScrollableKey.currentState != null); - return _verticalOuterScrollableKey.currentState!; - } - - /// The [CustomScrollableState] of the horizontal axis. - /// - /// Accessible by calling [TwoDimensionalScrollable.of]. - /// - /// Alternatively, [CustomScrollable.of] can be used by providing [Axis.horizontal] - /// to the `axis` parameter. - CustomScrollableState get horizontalScrollable { - assert(_horizontalInnerScrollableKey.currentState != null); - return _horizontalInnerScrollableKey.currentState!; - } - - @protected - @override - void initState() { - if (widget.verticalDetails.controller == null) { - _verticalFallbackController = ScrollController(); - } - if (widget.horizontalDetails.controller == null) { - _horizontalFallbackController = ScrollController(); - } - super.initState(); - } - - @protected - @override - void didUpdateWidget(TwoDimensionalScrollable oldWidget) { - super.didUpdateWidget(oldWidget); - // Handle changes in the provided/fallback scroll controllers - - // Vertical - if (oldWidget.verticalDetails.controller != - widget.verticalDetails.controller) { - if (oldWidget.verticalDetails.controller == null) { - // The old controller was null, meaning the fallback cannot be null. - // Dispose of the fallback. - assert(_verticalFallbackController != null); - assert(widget.verticalDetails.controller != null); - _verticalFallbackController!.dispose(); - _verticalFallbackController = null; - } else if (widget.verticalDetails.controller == null) { - // If the new controller is null, we need to set up the fallback - // ScrollController. - assert(_verticalFallbackController == null); - _verticalFallbackController = ScrollController(); - } - } - - // Horizontal - if (oldWidget.horizontalDetails.controller != - widget.horizontalDetails.controller) { - if (oldWidget.horizontalDetails.controller == null) { - // The old controller was null, meaning the fallback cannot be null. - // Dispose of the fallback. - assert(_horizontalFallbackController != null); - assert(widget.horizontalDetails.controller != null); - _horizontalFallbackController!.dispose(); - _horizontalFallbackController = null; - } else if (widget.horizontalDetails.controller == null) { - // If the new controller is null, we need to set up the fallback - // ScrollController. - assert(_horizontalFallbackController == null); - _horizontalFallbackController = ScrollController(); - } - } - } - - @protected - @override - Widget build(BuildContext context) { - assert( - axisDirectionToAxis(widget.verticalDetails.direction) == Axis.vertical, - 'TwoDimensionalScrollable.verticalDetails are not Axis.vertical.', - ); - assert( - axisDirectionToAxis(widget.horizontalDetails.direction) == - Axis.horizontal, - 'TwoDimensionalScrollable.horizontalDetails are not Axis.horizontal.', - ); - - final Widget result = RestorationScope( - restorationId: widget.restorationId, - child: _VerticalOuterDimension( - key: _verticalOuterScrollableKey, - // For gesture forwarding - horizontalKey: _horizontalInnerScrollableKey, - axisDirection: widget.verticalDetails.direction, - controller: - widget.verticalDetails.controller ?? _verticalFallbackController!, - physics: widget.verticalDetails.physics, - clipBehavior: widget.verticalDetails.clipBehavior ?? - widget.verticalDetails.decorationClipBehavior ?? - Clip.hardEdge, - incrementCalculator: widget.incrementCalculator, - excludeFromSemantics: widget.excludeFromSemantics, - restorationId: 'OuterVerticalTwoDimensionalScrollable', - dragStartBehavior: widget.dragStartBehavior, - diagonalDragBehavior: widget.diagonalDragBehavior, - hitTestBehavior: widget.hitTestBehavior, - viewportBuilder: (BuildContext context, ViewportOffset verticalOffset) { - return _HorizontalInnerDimension( - key: _horizontalInnerScrollableKey, - verticalOuterKey: _verticalOuterScrollableKey, - axisDirection: widget.horizontalDetails.direction, - controller: widget.horizontalDetails.controller ?? - _horizontalFallbackController!, - physics: widget.horizontalDetails.physics, - clipBehavior: widget.horizontalDetails.clipBehavior ?? - widget.horizontalDetails.decorationClipBehavior ?? - Clip.hardEdge, - incrementCalculator: widget.incrementCalculator, - excludeFromSemantics: widget.excludeFromSemantics, - restorationId: 'InnerHorizontalTwoDimensionalScrollable', - dragStartBehavior: widget.dragStartBehavior, - diagonalDragBehavior: widget.diagonalDragBehavior, - hitTestBehavior: widget.hitTestBehavior, - viewportBuilder: - (BuildContext context, ViewportOffset horizontalOffset) { - return widget.viewportBuilder( - context, verticalOffset, horizontalOffset); - }, - ); - }, - ), - ); - - // TODO(Piinks): Build scrollbars for 2 dimensions instead of 1, - // https://github.com/flutter/flutter/issues/122348 - - return _TwoDimensionalScrollableScope( - twoDimensionalScrollable: this, child: result); - } - - @protected - @override - void dispose() { - _verticalFallbackController?.dispose(); - _horizontalFallbackController?.dispose(); - super.dispose(); - } -} - -// Enable TwoDimensionalScrollable.of() to work as if -// TwoDimensionalScrollableState was an inherited widget. -// TwoDimensionalScrollableState.build() always rebuilds its -// _TwoDimensionalScrollableScope. -class _TwoDimensionalScrollableScope extends InheritedWidget { - const _TwoDimensionalScrollableScope({ - required this.twoDimensionalScrollable, - required super.child, - }); - - final TwoDimensionalScrollableState twoDimensionalScrollable; - - @override - bool updateShouldNotify(_TwoDimensionalScrollableScope old) => false; -} - -// Vertical outer scrollable of 2D scrolling -class _VerticalOuterDimension extends CustomScrollable { - const _VerticalOuterDimension({ - super.key, - required this.horizontalKey, - required super.viewportBuilder, - required super.axisDirection, - super.controller, - super.physics, - super.clipBehavior, - super.incrementCalculator, - super.excludeFromSemantics, - super.dragStartBehavior, - super.restorationId, - super.hitTestBehavior, - this.diagonalDragBehavior = DiagonalDragBehavior.none, - }) : assert(axisDirection == AxisDirection.up || - axisDirection == AxisDirection.down); - - final DiagonalDragBehavior diagonalDragBehavior; - final GlobalKey horizontalKey; - - @override - _VerticalOuterDimensionState createState() => _VerticalOuterDimensionState(); -} - -class _VerticalOuterDimensionState extends CustomScrollableState { - DiagonalDragBehavior get diagonalDragBehavior => - (widget as _VerticalOuterDimension).diagonalDragBehavior; - CustomScrollableState get horizontalScrollable => - (widget as _VerticalOuterDimension).horizontalKey.currentState!; - - Axis? lockedAxis; - Offset? lastDragOffset; - - // Implemented in the _HorizontalInnerDimension instead. - @override - _EnsureVisibleResults _performEnsureVisible( - RenderObject object, { - double alignment = 0.0, - Duration duration = Duration.zero, - Curve curve = Curves.ease, - ScrollPositionAlignmentPolicy alignmentPolicy = - ScrollPositionAlignmentPolicy.explicit, - RenderObject? targetRenderObject, - }) { - assert( - false, - 'The _performEnsureVisible method was called for the vertical scrollable ' - 'of a TwoDimensionalScrollable. This should not happen as the horizontal ' - 'scrollable handles both axes.', - ); - return (>[], this); - } - - void _evaluateLockedAxis(Offset offset) { - assert(lastDragOffset != null); - final Offset offsetDelta = lastDragOffset! - offset; - final double axisDifferential = offsetDelta.dx.abs() - offsetDelta.dy.abs(); - if (axisDifferential.abs() >= kTouchSlop) { - // We have single axis winner. - lockedAxis = axisDifferential > 0.0 ? Axis.horizontal : Axis.vertical; - } else { - lockedAxis = null; - } - } - - @override - void _handleDragDown(DragDownDetails details) { - switch (diagonalDragBehavior) { - case DiagonalDragBehavior.none: - break; - case DiagonalDragBehavior.weightedEvent: - case DiagonalDragBehavior.weightedContinuous: - case DiagonalDragBehavior.free: - // Initiate hold. If one or the other wins the gesture, cancel the - // opposite axis. - horizontalScrollable._handleDragDown(details); - } - super._handleDragDown(details); - } - - @override - void _handleDragStart(DragStartDetails details) { - lastDragOffset = details.globalPosition; - switch (diagonalDragBehavior) { - case DiagonalDragBehavior.none: - break; - case DiagonalDragBehavior.free: - // Prepare to scroll both. - // vertical - will call super below after switch. - horizontalScrollable._handleDragStart(details); - case DiagonalDragBehavior.weightedEvent: - case DiagonalDragBehavior.weightedContinuous: - // See if one axis wins the drag. - _evaluateLockedAxis(details.globalPosition); - switch (lockedAxis) { - case null: - // Prepare to scroll both, null means no winner yet. - // vertical - will call super below after switch. - horizontalScrollable._handleDragStart(details); - case Axis.horizontal: - // Prepare to scroll horizontally. - horizontalScrollable._handleDragStart(details); - return; - case Axis.vertical: - // Prepare to scroll vertically - will call super below after switch. - } - } - super._handleDragStart(details); - } - - @override - void _handleDragUpdate(DragUpdateDetails details) { - final DragUpdateDetails verticalDragDetails = DragUpdateDetails( - sourceTimeStamp: details.sourceTimeStamp, - delta: Offset(0.0, details.delta.dy), - primaryDelta: details.delta.dy, - globalPosition: details.globalPosition, - localPosition: details.localPosition, - ); - final DragUpdateDetails horizontalDragDetails = DragUpdateDetails( - sourceTimeStamp: details.sourceTimeStamp, - delta: Offset(details.delta.dx, 0.0), - primaryDelta: details.delta.dx, - globalPosition: details.globalPosition, - localPosition: details.localPosition, - ); - - switch (diagonalDragBehavior) { - case DiagonalDragBehavior.none: - // Default gesture handling from super class. - super._handleDragUpdate(verticalDragDetails); - return; - case DiagonalDragBehavior.free: - // Scroll both axes - horizontalScrollable._handleDragUpdate(horizontalDragDetails); - super._handleDragUpdate(verticalDragDetails); - return; - case DiagonalDragBehavior.weightedContinuous: - // Re-evaluate locked axis for every update. - _evaluateLockedAxis(details.globalPosition); - lastDragOffset = details.globalPosition; - case DiagonalDragBehavior.weightedEvent: - // Lock axis only once per gesture. - if (lockedAxis == null && lastDragOffset != null) { - // A winner has not been declared yet. - // See if one axis has won the drag. - _evaluateLockedAxis(details.globalPosition); - } - } - switch (lockedAxis) { - case null: - // Scroll both - vertical after switch - horizontalScrollable._handleDragUpdate(horizontalDragDetails); - case Axis.horizontal: - // Scroll horizontally - horizontalScrollable._handleDragUpdate(horizontalDragDetails); - return; - case Axis.vertical: - // Scroll vertically - after switch - } - super._handleDragUpdate(verticalDragDetails); - } - - @override - void _handleDragEnd(DragEndDetails details) { - lastDragOffset = null; - lockedAxis = null; - final double dx = details.velocity.pixelsPerSecond.dx; - final double dy = details.velocity.pixelsPerSecond.dy; - final DragEndDetails verticalDragDetails = DragEndDetails( - velocity: Velocity(pixelsPerSecond: Offset(0.0, dy)), - primaryVelocity: dy, - ); - final DragEndDetails horizontalDragDetails = DragEndDetails( - velocity: Velocity(pixelsPerSecond: Offset(dx, 0.0)), - primaryVelocity: dx, - ); - - switch (diagonalDragBehavior) { - case DiagonalDragBehavior.none: - break; - case DiagonalDragBehavior.weightedEvent: - case DiagonalDragBehavior.weightedContinuous: - case DiagonalDragBehavior.free: - horizontalScrollable._handleDragEnd(horizontalDragDetails); - } - super._handleDragEnd(verticalDragDetails); - } - - @override - void _handleDragCancel() { - lastDragOffset = null; - lockedAxis = null; - switch (diagonalDragBehavior) { - case DiagonalDragBehavior.none: - break; - case DiagonalDragBehavior.weightedEvent: - case DiagonalDragBehavior.weightedContinuous: - case DiagonalDragBehavior.free: - horizontalScrollable._handleDragCancel(); - } - super._handleDragCancel(); - } - - @override - void setCanDrag(bool value) { - switch (diagonalDragBehavior) { - case DiagonalDragBehavior.none: - // If we aren't scrolling diagonally, the default drag gesture recognizer - // is used. - super.setCanDrag(value); - return; - case DiagonalDragBehavior.weightedEvent: - case DiagonalDragBehavior.weightedContinuous: - case DiagonalDragBehavior.free: - if (value) { - // Replaces the typical vertical/horizontal drag gesture recognizers - // with a pan gesture recognizer to allow bidirectional scrolling. - // Based on the diagonalDragBehavior, valid vertical deltas are - // applied to this scrollable, while horizontal deltas are routed to - // the horizontal scrollable. - _gestureRecognizers = { - PanGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => PanGestureRecognizer( - supportedDevices: _configuration.dragDevices), - (PanGestureRecognizer instance) { - instance - ..onDown = _handleDragDown - ..onStart = _handleDragStart - ..onUpdate = _handleDragUpdate - ..onEnd = _handleDragEnd - ..onCancel = _handleDragCancel - ..minFlingDistance = _physics?.minFlingDistance - ..minFlingVelocity = _physics?.minFlingVelocity - ..maxFlingVelocity = _physics?.maxFlingVelocity - ..velocityTrackerBuilder = - _configuration.velocityTrackerBuilder(context) - ..dragStartBehavior = widget.dragStartBehavior - ..gestureSettings = _mediaQueryGestureSettings; - }, - ), - }; - // Cancel the active hold/drag (if any) because the gesture recognizers - // will soon be disposed by our RawGestureDetector, and we won't be - // receiving pointer up events to cancel the hold/drag. - _handleDragCancel(); - _lastCanDrag = value; - _lastAxisDirection = widget.axis; - if (_gestureDetectorKey.currentState != null) { - _gestureDetectorKey.currentState! - .replaceGestureRecognizers(_gestureRecognizers); - } - } - return; - } - } - - @override - Widget _buildChrome(BuildContext context, Widget child) { - final ScrollableDetails details = ScrollableDetails( - direction: widget.axisDirection, - controller: _effectiveScrollController, - clipBehavior: widget.clipBehavior, - ); - // Skip building a scrollbar here, the dual scrollbar is added in - // TwoDimensionalScrollableState. - return _configuration.buildOverscrollIndicator(context, child, details); - } -} - -// Horizontal inner scrollable of 2D scrolling -class _HorizontalInnerDimension extends CustomScrollable { - const _HorizontalInnerDimension({ - super.key, - required this.verticalOuterKey, - required super.viewportBuilder, - required super.axisDirection, - super.controller, - super.physics, - super.clipBehavior, - super.incrementCalculator, - super.excludeFromSemantics, - super.dragStartBehavior, - super.restorationId, - super.hitTestBehavior, - this.diagonalDragBehavior = DiagonalDragBehavior.none, - }) : assert(axisDirection == AxisDirection.left || - axisDirection == AxisDirection.right); - - final GlobalKey verticalOuterKey; - final DiagonalDragBehavior diagonalDragBehavior; - - @override - _HorizontalInnerDimensionState createState() => - _HorizontalInnerDimensionState(); -} - -class _HorizontalInnerDimensionState extends CustomScrollableState { - late CustomScrollableState verticalScrollable; - - GlobalKey get verticalOuterKey => - (widget as _HorizontalInnerDimension).verticalOuterKey; - DiagonalDragBehavior get diagonalDragBehavior => - (widget as _HorizontalInnerDimension).diagonalDragBehavior; - - @override - void didChangeDependencies() { - verticalScrollable = CustomScrollable.of(context); - assert( - axisDirectionToAxis(verticalScrollable.axisDirection) == Axis.vertical); - super.didChangeDependencies(); - } - - // Returns the Future from calling ensureVisible for the ScrollPosition, as - // as well as the vertical ScrollableState instance so its context can be - // used to check for other ancestor Scrollables in executing ensureVisible. - @override - _EnsureVisibleResults _performEnsureVisible( - RenderObject object, { - double alignment = 0.0, - Duration duration = Duration.zero, - Curve curve = Curves.ease, - ScrollPositionAlignmentPolicy alignmentPolicy = - ScrollPositionAlignmentPolicy.explicit, - RenderObject? targetRenderObject, - }) { - final List> newFutures = >[ - position.ensureVisible( - object, - alignment: alignment, - duration: duration, - curve: curve, - alignmentPolicy: alignmentPolicy, - ), - verticalScrollable.position.ensureVisible( - object, - alignment: alignment, - duration: duration, - curve: curve, - alignmentPolicy: alignmentPolicy, - ), - ]; - - return (newFutures, verticalScrollable); - } - - @override - void setCanDrag(bool value) { - switch (diagonalDragBehavior) { - case DiagonalDragBehavior.none: - // If we aren't scrolling diagonally, the default drag gesture - // recognizer is used. - super.setCanDrag(value); - return; - case DiagonalDragBehavior.weightedEvent: - case DiagonalDragBehavior.weightedContinuous: - case DiagonalDragBehavior.free: - if (value) { - // If a type of diagonal scrolling is enabled, a panning gesture - // recognizer will be created for the _VerticalOuterDimension. So in - // this case, the _HorizontalInnerDimension does not require a gesture - // recognizer, meanwhile we should ensure the outer dimension has - // updated in case it did not have enough content to enable dragging. - _gestureRecognizers = const {}; - verticalOuterKey.currentState!.setCanDrag(value); - // Cancel the active hold/drag (if any) because the gesture recognizers - // will soon be disposed by our RawGestureDetector, and we won't be - // receiving pointer up events to cancel the hold/drag. - _handleDragCancel(); - _lastCanDrag = value; - _lastAxisDirection = widget.axis; - if (_gestureDetectorKey.currentState != null) { - _gestureDetectorKey.currentState! - .replaceGestureRecognizers(_gestureRecognizers); - } - } - return; - } - } - - @override - Widget _buildChrome(BuildContext context, Widget child) { - final ScrollableDetails details = ScrollableDetails( - direction: widget.axisDirection, - controller: _effectiveScrollController, - clipBehavior: widget.clipBehavior, - ); - // Skip building a scrollbar here, the dual scrollbar is added in - // TwoDimensionalScrollableState. - return _configuration.buildOverscrollIndicator(context, child, details); - } -} - /// An auto scroller that scrolls the [scrollable] if a drag gesture drags close /// to its edge. /// @@ -2894,11 +2123,3 @@ class EdgeDraggingAutoScroller { } } } - -/// A typedef for a function that can calculate the offset for a type of scroll -/// increment given a [ScrollIncrementDetails]. -/// -/// This function is used as the type for [CustomScrollable.incrementCalculator], -/// which is called from a [ScrollAction]. -typedef ScrollIncrementCalculator = double Function( - ScrollIncrementDetails details);