From fac4c42a7e250607574229c5a71251f8861d7369 Mon Sep 17 00:00:00 2001 From: Daichi Fujita <68946713+fujidaiti@users.noreply.github.com> Date: Mon, 17 Jun 2024 01:33:10 +0900 Subject: [PATCH] Make stretching behavior of StretchingSheetPhysics more customizable (#171) Closes #159. ## Breaking Changes - Removed the `StretchingSheetPhysics.stretchingRange` property. - Added `StretchingBehavior` and its subclasses as the replacements. - `FixedStretchingBehavior`: Allows a sheet to stretch by up to a fixed amount. - `DirectionAwareStretchingBehavior`: Similar to `FixedStretchingBehavior`, but can accept different values for upward and downward directions. ## Misc - Added the migration guide. - Updated README and CHANGELOG. --- docs/migration-guide-0.8.x.md | 30 +++++ package/CHANGELOG.md | 6 + package/README.md | 4 +- package/lib/src/foundation/foundation.dart | 3 + package/lib/src/foundation/sheet_physics.dart | 112 +++++++++++++++--- 5 files changed, 139 insertions(+), 16 deletions(-) create mode 100644 docs/migration-guide-0.8.x.md diff --git a/docs/migration-guide-0.8.x.md b/docs/migration-guide-0.8.x.md new file mode 100644 index 00000000..46c0cf88 --- /dev/null +++ b/docs/migration-guide-0.8.x.md @@ -0,0 +1,30 @@ +# Migration guide to 0.8.x from 0.7.x + +Here is the summary of the breaking changes included in the version 0.8.0. + +## Changes in StretchingSheetPhysics + +### New way to control the stretching behavior of a sheet + +[StretchingSheetBehavior](https://pub.dev/documentation/smooth_sheets/latest/smooth_sheets/StretchingSheetBehavior-class.html) was added as the new way to control the stretching behavior of a sheet. It replaces `StretchingSheetPhysics.stretchingRange` property, which has been removed. + +**BEFORE:** + +```dart +const physics = StretchingSheetPhysics( + stretchingRange: Extent.proportional(0.1), +); +``` + +**AFTER:** + +```dart +const physics = StretchingSheetPhysics( + behavior: FixedStretchingBehavior(Extent.proportional(0.1)), +); +``` + +See also: + +- [FixedStretchingBehavior](https://pub.dev/documentation/smooth_sheets/latest/smooth_sheets/FixedStretchingBehavior-class.html), which stretches the sheet by a fixed amount. +- [DirectionAwareStretchingBehavior](https://pub.dev/documentation/smooth_sheets/latest/smooth_sheets/DirectionAwareStretchingBehavior-class.html), which stretches the sheet by a fixed amount, based on the direction of a drag. diff --git a/package/CHANGELOG.md b/package/CHANGELOG.md index 6fbc3c6c..8d1dfa48 100644 --- a/package/CHANGELOG.md +++ b/package/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.8.x + +This version contains some breaking changes. See the [migration guide](https://github.com/fujidaiti/smooth_sheets/blob/main/docs/migration-guide-0.8.x.md) for more details. + +- Make stretching behavior of StretchingSheetPhysics more customizable (#171) + ## 0.7.3 Jun 9, 2024 - Fix: DropdownButton doesn't work in NavigationSheet (#139) diff --git a/package/README.md b/package/README.md index 9c23c0bb..6043e207 100644 --- a/package/README.md +++ b/package/README.md @@ -21,8 +21,8 @@ This library is currently in the experimental stage. The API may undergo changes ## Migration guide -- [0.6.x to 0.7.x](https://github.com/fujidaiti/smooth_sheets/blob/main/docs/migration-guide-0.7.x.md) 🆕 -- [0.5.x to 0.6.x](https://github.com/fujidaiti/smooth_sheets/blob/main/docs/migration-guide-0.6.x.md) +- [0.7.x to 0.8.x](https://github.com/fujidaiti/smooth_sheets/blob/main/docs/migration-guide-0.8.x.md) 🆕 +- [0.6.x to 0.7.x](https://github.com/fujidaiti/smooth_sheets/blob/main/docs/migration-guide-0.7.x.md) See [here](https://github.com/fujidaiti/smooth_sheets/blob/main/docs/) for older versions. diff --git a/package/lib/src/foundation/foundation.dart b/package/lib/src/foundation/foundation.dart index 20070419..3b2311e9 100644 --- a/package/lib/src/foundation/foundation.dart +++ b/package/lib/src/foundation/foundation.dart @@ -35,6 +35,8 @@ export 'sheet_notification.dart' export 'sheet_physics.dart' show ClampingSheetPhysics, + DirectionAwareStretchingBehavior, + FixedStretchingBehavior, InterpolationSimulation, SheetPhysics, SheetPhysicsMixin, @@ -42,6 +44,7 @@ export 'sheet_physics.dart' SnapToNearestEdge, SnappingSheetBehavior, SnappingSheetPhysics, + StretchingBehavior, StretchingSheetPhysics, kDefaultSheetPhysics, kDefaultSheetSpring; diff --git a/package/lib/src/foundation/sheet_physics.dart b/package/lib/src/foundation/sheet_physics.dart index 977c8194..71e6e740 100644 --- a/package/lib/src/foundation/sheet_physics.dart +++ b/package/lib/src/foundation/sheet_physics.dart @@ -239,8 +239,8 @@ mixin _SnapToNearestMixin implements SnappingSheetBehavior { /// it will snap to [SheetMetrics.maxPixels]. /// /// Using this behavior is functionally identical to using [SnapToNearest] -/// with the snap positions of [SheetExtentConfig.minExtent] and -/// [SheetExtentConfig.maxExtent], but more simplified and efficient. +/// with the snap positions of [SheetExtent.minExtent] and +/// [SheetExtent.maxExtent], but more simplified and efficient. class SnapToNearestEdge with _SnapToNearestMixin { /// Creates a [SnappingSheetBehavior] that snaps to either /// [SheetMetrics.minPixels] or [SheetMetrics.maxPixels]. @@ -378,15 +378,100 @@ class ClampingSheetPhysics extends SheetPhysics with SheetPhysicsMixin { } } +/// An object that determines the behavior of a sheet when it is out of bounds. +/// +/// See also: +/// - [FixedStretchingBehavior], which stretches the sheet by a fixed amount. +/// - [DirectionAwareStretchingBehavior], which stretches the sheet based on the +/// direction of a drag. +abstract class StretchingBehavior { + /// Returns the number of pixels the sheet can stretch beyond the content + /// bounds. + /// + /// [StretchingSheetPhysics.applyPhysicsToOffset] calls this method to + /// determine how many pixels the sheet can stretch beyond the content bounds. + /// The returned value must be non-negative. + /// + /// The [offset] is the amount of pixels that will be applied to the sheet, + /// and [metrics] is the current metrics of the sheet. + double computeStretchablePixels(double offset, SheetMetrics metrics); +} + +/// A [StretchingBehavior] that stretches the sheet by a fixed amount. +/// +/// The following is an example of a [StretchingSheetPhysics] that allows the +/// sheet to stretch by 12% of the content size. +/// ```dart +/// const physics = StretchingSheetPhysics( +/// behavior: FixedStretchingBehavior(Extent.proportional(0.12)), +/// ); +/// ``` +class FixedStretchingBehavior implements StretchingBehavior { + /// Creates a [StretchingBehavior] that stretches the sheet by a fixed amount. + const FixedStretchingBehavior(this.range); + + /// How much the sheet can stretch beyond the content bounds. + final Extent range; + + @override + double computeStretchablePixels(double offset, SheetMetrics metrics) { + return range.resolve(metrics.contentSize); + } +} + +/// A [StretchingBehavior] that stretches the sheet by a fixed amount. +/// +/// Different stretching ranges can be specified for upward and downward +/// directions. For example, the following [StretchingSheetPhysics] allows the +/// sheet to stretch by 12% of the content size when dragged downward, and by +/// 8 pixels when dragged upward. +/// +/// ```dart +/// const physics = StretchingSheetPhysics( +/// behavior: DirectionAwareStretchingBehavior( +/// upward: Extent.pixels(8), +/// downward: Extent.proportional(0.12), +/// ), +/// ); +/// ``` +class DirectionAwareStretchingBehavior implements StretchingBehavior { + /// Creates a [StretchingBehavior] that stretches the sheet by a fixed amount + /// based on the direction of a drag. + const DirectionAwareStretchingBehavior({ + this.upward = const Extent.pixels(0), + this.downward = const Extent.pixels(0), + }); + + /// How much the sheet can stretch beyond the content bounds when dragged + /// upward. + final Extent upward; + + /// How much the sheet can stretch beyond the content bounds when dragged + /// downward. + final Extent downward; + + @override + double computeStretchablePixels(double offset, SheetMetrics metrics) { + return switch (offset) { + > 0.0 => upward.resolve(metrics.contentSize), + < 0.0 => downward.resolve(metrics.contentSize), + _ => 0.0, + }; + } +} + class StretchingSheetPhysics extends SheetPhysics with SheetPhysicsMixin { const StretchingSheetPhysics({ super.parent, - this.stretchingRange = const Extent.proportional(0.12), + this.behavior = const FixedStretchingBehavior(Extent.proportional(0.12)), this.frictionCurve = Curves.easeOutSine, this.spring = kDefaultSheetSpring, }); - final Extent stretchingRange; + /// The behavior that determines how the sheet stretches when it is + /// out of the content bounds. + final StretchingBehavior behavior; + final Curve frictionCurve; @override @@ -397,22 +482,22 @@ class StretchingSheetPhysics extends SheetPhysics with SheetPhysicsMixin { SheetPhysics? parent, SpringDescription? spring, Extent? stretchingRange, + StretchingBehavior? behavior, Curve? frictionCurve, }) { return StretchingSheetPhysics( parent: parent ?? this.parent, spring: spring ?? this.spring, - stretchingRange: stretchingRange ?? this.stretchingRange, + behavior: behavior ?? this.behavior, frictionCurve: frictionCurve ?? this.frictionCurve, ); } @override double computeOverflow(double offset, SheetMetrics metrics) { - final stretchingRange = this.stretchingRange.resolve(metrics.contentSize); - + final stretchingRange = behavior.computeStretchablePixels(offset, metrics); if (stretchingRange != 0) { - return 0; + return const ClampingSheetPhysics().applyPhysicsToOffset(offset, metrics); } return super.computeOverflow(offset, metrics); @@ -420,6 +505,11 @@ class StretchingSheetPhysics extends SheetPhysics with SheetPhysicsMixin { @override double applyPhysicsToOffset(double offset, SheetMetrics metrics) { + final stretchingRange = behavior.computeStretchablePixels(offset, metrics); + if (stretchingRange == 0) { + return const ClampingSheetPhysics().applyPhysicsToOffset(offset, metrics); + } + final currentPixels = metrics.pixels; final minPixels = metrics.minPixels; final maxPixels = metrics.maxPixels; @@ -432,12 +522,6 @@ class StretchingSheetPhysics extends SheetPhysics with SheetPhysicsMixin { return offset; } - final stretchingRange = this.stretchingRange.resolve(metrics.contentSize); - - if (stretchingRange.isApprox(0)) { - return 0; - } - // We divide the delta into smaller fragments // and apply friction to each fragment in sequence. // This ensures that the friction is not too small