From 4963e57ac8380a78d11e17f2a9807beb9a25f022 Mon Sep 17 00:00:00 2001 From: Daichi Fujita <68946713+fujidaiti@users.noreply.github.com> Date: Sat, 20 Apr 2024 19:54:15 +0900 Subject: [PATCH] Refactor physics module (#97) --- package/lib/src/foundation/physics.dart | 134 +++++++----------- .../scrollable/scrollable_sheet_extent.dart | 1 + .../scrollable/scrollable_sheet_physics.dart | 8 +- package/test/foundation/physics_test.dart | 3 +- 4 files changed, 64 insertions(+), 82 deletions(-) diff --git a/package/lib/src/foundation/physics.dart b/package/lib/src/foundation/physics.dart index f18ddd9d..a420f41b 100644 --- a/package/lib/src/foundation/physics.dart +++ b/package/lib/src/foundation/physics.dart @@ -1,29 +1,33 @@ import 'dart:math'; import 'dart:ui'; -import 'package:collection/collection.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; import '../internal/double_utils.dart'; import 'sheet_extent.dart'; +/// The default [SpringDescription] used by [SheetPhysics] subclasses. +/// +/// This spring has the same configuration as the resulting spring +/// from the [SpringDescription.withDampingRatio] constructor with +/// a ratio of `1.1`, a mass of `0.5`, and a stiffness of `100.0`. +const kDefaultSheetSpring = SpringDescription( + mass: 0.5, + stiffness: 100.0, + // Use a pre-calculated value to define the spring as a const variable. + // See the implementation of withDampingRatio() for the formula. + damping: 15.5563491861, +); + const _minSettlingDuration = Duration(milliseconds: 160); const _defaultSettlingSpeed = 600.0; // logical pixels per second abstract class SheetPhysics { - const SheetPhysics({ - this.parent, - SpringDescription? spring, - }) : _spring = spring; + const SheetPhysics({this.parent}); final SheetPhysics? parent; - final SpringDescription? _spring; - SpringDescription get spring { - return _spring ?? const ScrollPhysics().spring; - } - /// Create a copy of this object appending the [ancestor] to /// the physics chain, much like [ScrollPhysics.applyTo]. /// @@ -42,6 +46,19 @@ abstract class SheetPhysics { /// by the new values. SheetPhysics copyWith({SheetPhysics? parent, SpringDescription? spring}); + double computeOverflow(double offset, SheetMetrics metrics); + + double applyPhysicsToOffset(double offset, SheetMetrics metrics); + + Simulation? createBallisticSimulation(double velocity, SheetMetrics metrics); + + Simulation? createSettlingSimulation(SheetMetrics metrics); +} + +mixin SheetPhysicsMixin on SheetPhysics { + SpringDescription get spring => kDefaultSheetSpring; + + @override double computeOverflow(double offset, SheetMetrics metrics) { if (parent case final parent?) { return parent.computeOverflow(offset, metrics); @@ -57,6 +74,7 @@ abstract class SheetPhysics { } } + @override double applyPhysicsToOffset(double offset, SheetMetrics metrics) { if (parent case final parent?) { return parent.applyPhysicsToOffset(offset, metrics); @@ -71,6 +89,7 @@ abstract class SheetPhysics { } } + @override Simulation? createBallisticSimulation(double velocity, SheetMetrics metrics) { if (parent case final parent?) { return parent.createBallisticSimulation(velocity, metrics); @@ -94,6 +113,7 @@ abstract class SheetPhysics { ); } + @override Simulation? createSettlingSimulation(SheetMetrics metrics) { if (parent case final parent?) { return parent.createSettlingSimulation(metrics); @@ -113,16 +133,6 @@ abstract class SheetPhysics { ), ); } - - @override - bool operator ==(Object other) => - identical(this, other) || - (other is SheetPhysics && - runtimeType == other.runtimeType && - parent == other.parent); - - @override - int get hashCode => Object.hash(runtimeType, parent); } /// A [Simulation] that interpolates between two values over a given duration. @@ -233,19 +243,6 @@ class SnapToNearestEdge with _SnapToNearestMixin { assert(metrics.pixels.isInBounds(metrics.minPixels, metrics.maxPixels)); return (metrics.minPixels, metrics.maxPixels); } - - @override - bool operator ==(Object other) => - identical(this, other) || - (other is SnapToNearestEdge && - runtimeType == other.runtimeType && - minFlingSpeed == other.minFlingSpeed); - - @override - int get hashCode => Object.hash( - runtimeType, - minFlingSpeed, - ); } class SnapToNearest with _SnapToNearestMixin { @@ -304,32 +301,20 @@ class SnapToNearest with _SnapToNearestMixin { return (nearestSmaller, nearestGreater); } - - @override - bool operator ==(Object other) => - identical(this, other) || - (other is SnapToNearest && - runtimeType == other.runtimeType && - minFlingSpeed == other.minFlingSpeed && - const DeepCollectionEquality().equals(snapTo, other.snapTo)); - - @override - int get hashCode => Object.hash( - runtimeType, - minFlingSpeed, - snapTo, - ); } -class SnappingSheetPhysics extends SheetPhysics { +class SnappingSheetPhysics extends SheetPhysics with SheetPhysicsMixin { const SnappingSheetPhysics({ super.parent, - super.spring, + this.spring = kDefaultSheetSpring, this.snappingBehavior = const SnapToNearestEdge(), }); final SnappingSheetBehavior snappingBehavior; + @override + final SpringDescription spring; + @override SheetPhysics copyWith({ SheetPhysics? parent, @@ -348,44 +333,49 @@ class SnappingSheetPhysics extends SheetPhysics { final snapPixels = snappingBehavior.findSnapPixels(velocity, metrics); if (snapPixels != null && !metrics.pixels.isApprox(snapPixels)) { return ScrollSpringSimulation( - spring, metrics.pixels, snapPixels, velocity); + spring, + metrics.pixels, + snapPixels, + velocity, + ); } else { return super.createBallisticSimulation(velocity, metrics); } } - - @override - bool operator ==(Object other) => - identical(this, other) || - (other is SnappingSheetPhysics && - snappingBehavior == other.snappingBehavior && - super == other); - - @override - int get hashCode => Object.hash(snappingBehavior, super.hashCode); } -class ClampingSheetPhysics extends SheetPhysics { +class ClampingSheetPhysics extends SheetPhysics with SheetPhysicsMixin { const ClampingSheetPhysics({ super.parent, + this.spring = kDefaultSheetSpring, }); + @override + final SpringDescription spring; + @override SheetPhysics copyWith({SheetPhysics? parent, SpringDescription? spring}) { - return ClampingSheetPhysics(parent: parent ?? this.parent); + return ClampingSheetPhysics( + parent: parent ?? this.parent, + spring: spring ?? this.spring, + ); } } -class StretchingSheetPhysics extends SheetPhysics { +class StretchingSheetPhysics extends SheetPhysics with SheetPhysicsMixin { const StretchingSheetPhysics({ super.parent, this.stretchingRange = const Extent.proportional(0.12), this.frictionCurve = Curves.easeOutSine, + this.spring = kDefaultSheetSpring, }); final Extent stretchingRange; final Curve frictionCurve; + @override + final SpringDescription spring; + @override SheetPhysics copyWith({ SheetPhysics? parent, @@ -395,6 +385,7 @@ class StretchingSheetPhysics extends SheetPhysics { }) { return StretchingSheetPhysics( parent: parent ?? this.parent, + spring: spring ?? this.spring, stretchingRange: stretchingRange ?? this.stretchingRange, frictionCurve: frictionCurve ?? this.frictionCurve, ); @@ -455,19 +446,4 @@ class StretchingSheetPhysics extends SheetPhysics { return newPixels - currentPixels; } - - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StretchingSheetPhysics && - stretchingRange == other.stretchingRange && - frictionCurve == other.frictionCurve && - super == other); - - @override - int get hashCode => Object.hash( - stretchingRange, - frictionCurve, - super.hashCode, - ); } diff --git a/package/lib/src/scrollable/scrollable_sheet_extent.dart b/package/lib/src/scrollable/scrollable_sheet_extent.dart index d98683ca..43db2e9d 100644 --- a/package/lib/src/scrollable/scrollable_sheet_extent.dart +++ b/package/lib/src/scrollable/scrollable_sheet_extent.dart @@ -75,6 +75,7 @@ class ScrollableSheetExtent extends SheetExtent { required super.context, required SheetPhysics physics, }) : super( + // TODO: Do this in the build method of ExtentConfig physics: physics is ScrollableSheetPhysics ? physics : ScrollableSheetPhysics(parent: physics), diff --git a/package/lib/src/scrollable/scrollable_sheet_physics.dart b/package/lib/src/scrollable/scrollable_sheet_physics.dart index f1778b5d..3c082e53 100644 --- a/package/lib/src/scrollable/scrollable_sheet_physics.dart +++ b/package/lib/src/scrollable/scrollable_sheet_physics.dart @@ -3,15 +3,19 @@ import 'package:flutter/physics.dart'; import '../foundation/physics.dart'; import '../foundation/sheet_extent.dart'; -class ScrollableSheetPhysics extends SheetPhysics { +class ScrollableSheetPhysics extends SheetPhysics with SheetPhysicsMixin { const ScrollableSheetPhysics({ super.parent, - super.spring, + this.spring = kDefaultSheetSpring, this.maxScrollSpeedToInterrupt = double.infinity, }) : assert(maxScrollSpeedToInterrupt >= 0); + // TODO: Expose this from the ScrollableSheet's constructor final double maxScrollSpeedToInterrupt; + @override + final SpringDescription spring; + @override SheetPhysics copyWith({ SheetPhysics? parent, diff --git a/package/test/foundation/physics_test.dart b/package/test/foundation/physics_test.dart index eaaed14d..85ea4743 100644 --- a/package/test/foundation/physics_test.dart +++ b/package/test/foundation/physics_test.dart @@ -4,7 +4,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:smooth_sheets/smooth_sheets.dart'; import 'package:smooth_sheets/src/foundation/sheet_status.dart'; -class _SheetPhysicsWithDefaultConfiguration extends SheetPhysics { +class _SheetPhysicsWithDefaultConfiguration extends SheetPhysics + with SheetPhysicsMixin { const _SheetPhysicsWithDefaultConfiguration(); @override