Repository: fujidaiti/smooth_sheets
Pull request #248 (2024-09-20 15:43:43)

Refactor
## Description

- DRY the bottom viewport inset handling logic shared across activities
- Done "use controller.value instead"
- Change signature of didChangeBoundaryConstraints
- Rename applyUserDragUpdate to onDragUpdate
- Rename applyUserDragEnd to onDragEnd
- Rename SheetGestureTamperer to SheetGestureProxy
- Fix warnings

## Summary (check all that apply)

- [x] Modified / added code
- [x] Modified / added tests
- [ ] Modified / added examples
- [ ] Modified / added others (pubspec.yaml, workflows, etc...)
- [ ] Updated README
- [ ] Contains breaking changes
  - [ ] Created / updated migration guide
- [ ] Incremented version number
  - [ ] Updated CHANGELOG



Patch:
diff --git a/lib/smooth_sheets.dart b/lib/smooth_sheets.dart
index a8e7e4f..abbc8c9 100644
--- a/lib/smooth_sheets.dart
+++ b/lib/smooth_sheets.dart
@@ -1,7 +1,7 @@
 /// Comprehensive bottom sheet library supporting imperative and declarative
 /// navigation APIs, nested navigation, persistent and modal styles (including
 /// the iOS flavor), and more.
-library smooth_sheets;
+library;
 
 export 'src/draggable/draggable.dart';
 export 'src/foundation/foundation.dart';
diff --git a/lib/src/draggable/draggable_sheet.dart b/lib/src/draggable/draggable_sheet.dart
index 177e832..e3bf6b7 100644
--- a/lib/src/draggable/draggable_sheet.dart
+++ b/lib/src/draggable/draggable_sheet.dart
@@ -72,7 +72,7 @@ class _DraggableSheetState extends State<DraggableSheet>
   Widget build(BuildContext context) {
     final theme = SheetTheme.maybeOf(context);
     final physics = widget.physics ?? theme?.physics ?? kDefaultSheetPhysics;
-    final gestureTamper = TamperSheetGesture.maybeOf(context);
+    final gestureTamper = SheetGestureProxy.maybeOf(context);
     final controller =
         widget.controller ?? SheetControllerScope.maybeOf(context);
 
diff --git a/lib/src/foundation/sheet_activity.dart b/lib/src/foundation/sheet_activity.dart
index 21f3678..f8835d6 100644
--- a/lib/src/foundation/sheet_activity.dart
+++ b/lib/src/foundation/sheet_activity.dart
@@ -66,10 +66,9 @@ abstract class SheetActivity<T extends SheetExtent> {
 
   void didChangeViewportDimensions(Size? oldSize, EdgeInsets? oldInsets) {}
 
-  // TODO: Change `double?`s to `Extent?`s.
   void didChangeBoundaryConstraints(
-    double? oldMinPixels,
-    double? oldMaxPixels,
+    Extent? oldMinExtent,
+    Extent? oldMaxExtent,
   ) {}
 
   /// Called when all relevant metrics of the sheet are finalized
@@ -84,60 +83,15 @@ abstract class SheetActivity<T extends SheetExtent> {
   /// latest metrics.
   ///
   /// By default, this method updates [SheetMetrics.pixels] to maintain the
-  /// current [Extent], which is determined by [SheetPhysics.findSettledExtent]
-  /// using the metrics of the previous frame.
+  /// visual position of the sheet when the viewport insets change, typically
+  /// due to the appearance or disappearance of the on-screen keyboard.
   void didFinalizeDimensions(
     Size? oldContentSize,
     Size? oldViewportSize,
     EdgeInsets? oldViewportInsets,
   ) {
-    if (oldContentSize == null &&
-        oldViewportSize == null &&
-        oldViewportInsets == null) {
-      return;
-    }
-
-    final oldMetrics = owner.metrics.copyWith(
-      contentSize: oldContentSize,
-      viewportSize: oldViewportSize,
-      viewportInsets: oldViewportInsets,
-    );
-    final prevDetent = owner.physics.findSettledExtent(0, oldMetrics);
-    final newPixels = prevDetent.resolve(owner.metrics.contentSize);
-
-    if (newPixels == owner.metrics.pixels) {
-      return;
-    } else if (oldViewportInsets != null &&
-        oldViewportInsets.bottom != owner.metrics.viewportInsets.bottom) {
-      // TODO: Is it possible to remove this assumption?
-      // We currently assume that when the bottom viewport inset changes,
-      // it is due to the appearance or disappearance of the keyboard,
-      // and that this change will gradually occur over several frames,
-      // likely due to animation.
-      owner
-        ..setPixels(newPixels)
-        ..didUpdateMetrics();
-      return;
-    }
-
-    const minAnimationDuration = Duration(milliseconds: 150);
-    const meanAnimationVelocity = 300 / 1000; // pixels per millisecond
-    final distance = (newPixels - owner.metrics.pixels).abs();
-    final estimatedDuration = Duration(
-      milliseconds: (distance / meanAnimationVelocity).round(),
-    );
-    if (estimatedDuration >= minAnimationDuration) {
-      owner.animateTo(
-        prevDetent,
-        duration: estimatedDuration,
-        curve: Curves.easeInOut,
-      );
-    } else {
-      // The destination is close enough to the current position,
-      // so we immediately snap to it without animation.
-      owner
-        ..setPixels(newPixels)
-        ..didUpdateMetrics();
+    if (oldViewportInsets != null) {
+      absorbBottomViewportInset(owner, oldViewportInsets);
     }
   }
 
@@ -241,26 +195,9 @@ class AnimatedSheetActivity extends SheetActivity
     Size? oldViewportSize,
     EdgeInsets? oldViewportInsets,
   ) {
-    if (oldContentSize == null &&
-        oldViewportSize == null &&
-        oldViewportInsets == null) {
-      return;
-    }
-
     if (oldViewportInsets != null) {
-      // TODO: DRY with other activities.
-      // Appends the delta of the bottom inset (typically the keyboard height)
-      // to keep the visual sheet position unchanged.
-      final newInsets = owner.metrics.viewportInsets;
-      final deltaInsetBottom = newInsets.bottom - oldViewportInsets.bottom;
-      final correctedPixels = owner.metrics.pixels - deltaInsetBottom;
-      if (correctedPixels != owner.metrics.pixels) {
-        owner
-          ..setPixels(correctedPixels)
-          ..didUpdateMetrics();
-      }
+      absorbBottomViewportInset(owner, oldViewportInsets);
     }
-
     final newEndPixels = destination.resolve(owner.metrics.contentSize);
     if (newEndPixels != _endPixels) {
       final remainingDuration =
@@ -278,14 +215,6 @@ class BallisticSheetActivity extends SheetActivity
   });
 
   final Simulation simulation;
-  // TODO: Use controller.value instead.
-  late double _lastAnimatedValue;
-
-  @override
-  void init(SheetExtent delegate) {
-    super.init(delegate);
-    _lastAnimatedValue = controller.value;
-  }
 
   @override
   AnimationController createAnimationController() {
@@ -300,11 +229,9 @@ class BallisticSheetActivity extends SheetActivity
   @override
   void onAnimationTick() {
     if (mounted) {
-      final oldPixels = owner.metrics.pixels;
       owner
-        ..setPixels(oldPixels + controller.value - _lastAnimatedValue)
+        ..setPixels(controller.value)
         ..didUpdateMetrics();
-      _lastAnimatedValue = controller.value;
     }
   }
 
@@ -332,16 +259,9 @@ class BallisticSheetActivity extends SheetActivity
     );
     final destination = owner.physics.findSettledExtent(velocity, oldMetrics);
 
-    // TODO: DRY with other activities.
-    // Appends the delta of the bottom inset (typically the keyboard height)
-    // to keep the visual sheet position unchanged.
-    final newInsets = owner.metrics.viewportInsets;
-    final oldInsets = oldViewportInsets ?? newInsets;
-    final deltaInsetBottom = newInsets.bottom - oldInsets.bottom;
-    final newPixels = owner.metrics.pixels - deltaInsetBottom;
-    owner
-      ..setPixels(newPixels)
-      ..didUpdateMetrics();
+    if (oldViewportInsets != null) {
+      absorbBottomViewportInset(owner, oldViewportInsets);
+    }
 
     final endPixels = destination.resolve(owner.metrics.contentSize);
     if (endPixels == owner.metrics.pixels) {
@@ -462,16 +382,7 @@ class SettlingSheetActivity extends SheetActivity {
     EdgeInsets? oldViewportInsets,
   ) {
     if (oldViewportInsets != null) {
-      // TODO: DRY with other activities.
-      // Appends the delta of the bottom inset (typically the keyboard height)
-      // to keep the visual sheet position unchanged.
-      final newInsets = owner.metrics.viewportInsets;
-      final oldInsets = oldViewportInsets;
-      final deltaInsetBottom = newInsets.bottom - oldInsets.bottom;
-      final newPixels = owner.metrics.pixels - deltaInsetBottom;
-      owner
-        ..setPixels(newPixels)
-        ..didUpdateMetrics();
+      absorbBottomViewportInset(owner, oldViewportInsets);
     }
 
     _invalidateVelocity();
@@ -508,6 +419,65 @@ class SettlingSheetActivity extends SheetActivity {
 class IdleSheetActivity extends SheetActivity {
   @override
   SheetStatus get status => SheetStatus.stable;
+
+  /// Updates [SheetMetrics.pixels] to maintain the current [Extent], which
+  /// is determined by [SheetPhysics.findSettledExtent] using the metrics of
+  /// the previous frame.
+  @override
+  void didFinalizeDimensions(
+    Size? oldContentSize,
+    Size? oldViewportSize,
+    EdgeInsets? oldViewportInsets,
+  ) {
+    if (oldContentSize == null &&
+        oldViewportSize == null &&
+        oldViewportInsets == null) {
+      return;
+    }
+
+    final oldMetrics = owner.metrics.copyWith(
+      contentSize: oldContentSize,
+      viewportSize: oldViewportSize,
+      viewportInsets: oldViewportInsets,
+    );
+    final prevDetent = owner.physics.findSettledExtent(0, oldMetrics);
+    final newPixels = prevDetent.resolve(owner.metrics.contentSize);
+
+    if (newPixels == owner.metrics.pixels) {
+      return;
+    } else if (oldViewportInsets != null &&
+        oldViewportInsets.bottom != owner.metrics.viewportInsets.bottom) {
+      // TODO: Is it possible to remove this assumption?
+      // We currently assume that when the bottom viewport inset changes,
+      // it is due to the appearance or disappearance of the keyboard,
+      // and that this change will gradually occur over several frames,
+      // likely due to animation.
+      owner
+        ..setPixels(newPixels)
+        ..didUpdateMetrics();
+      return;
+    }
+
+    const minAnimationDuration = Duration(milliseconds: 150);
+    const meanAnimationVelocity = 300 / 1000; // pixels per millisecond
+    final distance = (newPixels - owner.metrics.pixels).abs();
+    final estimatedDuration = Duration(
+      milliseconds: (distance / meanAnimationVelocity).round(),
+    );
+    if (estimatedDuration >= minAnimationDuration) {
+      owner.animateTo(
+        prevDetent,
+        duration: estimatedDuration,
+        curve: Curves.easeInOut,
+      );
+    } else {
+      // The destination is close enough to the current position,
+      // so we immediately snap to it without animation.
+      owner
+        ..setPixels(newPixels)
+        ..didUpdateMetrics();
+    }
+  }
 }
 
 @internal
@@ -538,7 +508,7 @@ class DragSheetActivity extends SheetActivity
   }
 
   @override
-  void applyUserDragUpdate(SheetDragUpdateDetails details) {
+  void onDragUpdate(SheetDragUpdateDetails details) {
     final physicsAppliedDelta =
         owner.physics.applyPhysicsToOffset(details.deltaY, owner.metrics);
     if (physicsAppliedDelta != 0) {
@@ -555,7 +525,7 @@ class DragSheetActivity extends SheetActivity
   }
 
   @override
-  void applyUserDragEnd(SheetDragEndDetails details) {
+  void onDragEnd(SheetDragEndDetails details) {
     owner
       ..didDragEnd(details)
       ..goBallistic(details.velocityY);
@@ -619,16 +589,29 @@ mixin UserControlledSheetActivityMixin<T extends SheetExtent>
     EdgeInsets? oldViewportInsets,
   ) {
     assert(owner.metrics.hasDimensions);
-
-    final newInsets = owner.metrics.viewportInsets;
-    final oldInsets = oldViewportInsets ?? newInsets;
-    final deltaInsetBottom = newInsets.bottom - oldInsets.bottom;
-    // Appends the delta of the bottom inset (typically the keyboard height)
-    // to keep the visual sheet position unchanged.
-    owner
-      ..setPixels(owner.metrics.pixels - deltaInsetBottom)
-      ..didUpdateMetrics();
+    if (oldViewportInsets != null) {
+      absorbBottomViewportInset(owner, oldViewportInsets);
+    }
     // We don't call `goSettling` here because the user is still
     // manually controlling the sheet position.
   }
 }
+
+/// Appends the delta of the bottom viewport inset, which is typically
+/// equal to the height of the on-screen keyboard, to the [activityOwner]'s
+/// `pixels` to maintain the visual sheet position.
+@internal
+void absorbBottomViewportInset(
+  SheetExtent activityOwner,
+  EdgeInsets oldViewportInsets,
+) {
+  final newInsets = activityOwner.metrics.viewportInsets;
+  final oldInsets = oldViewportInsets;
+  final deltaInsetBottom = newInsets.bottom - oldInsets.bottom;
+  final newPixels = activityOwner.metrics.pixels - deltaInsetBottom;
+  if (newPixels != activityOwner.metrics.pixels) {
+    activityOwner
+      ..setPixels(newPixels)
+      ..didUpdateMetrics();
+  }
+}
diff --git a/lib/src/foundation/sheet_drag.dart b/lib/src/foundation/sheet_drag.dart
index 8f72270..276b239 100644
--- a/lib/src/foundation/sheet_drag.dart
+++ b/lib/src/foundation/sheet_drag.dart
@@ -223,11 +223,8 @@ class SheetDragCancelDetails extends SheetDragDetails {
 @internal
 abstract class SheetDragControllerTarget {
   VerticalDirection get dragAxisDirection;
-  // TODO: Rename to onDragUpdate.
-  void applyUserDragUpdate(SheetDragUpdateDetails details);
-  // TODO: Rename to onDragEnd.
-  void applyUserDragEnd(SheetDragEndDetails details);
-
+  void onDragUpdate(SheetDragUpdateDetails details);
+  void onDragEnd(SheetDragEndDetails details);
   void onDragCancel(SheetDragCancelDetails details);
 
   /// Returns the minimum number of pixels that the sheet being dragged
@@ -245,13 +242,13 @@ class SheetDragController implements Drag, ScrollActivityDelegate {
   /// finger across the screen.
   SheetDragController({
     required SheetDragControllerTarget target,
-    required SheetGestureTamperer? gestureTamperer,
+    required SheetGestureProxyMixin? gestureTamperer,
     required SheetDragStartDetails details,
     required VoidCallback onDragCanceled,
     required double? carriedVelocity,
     required double? motionStartDistanceThreshold,
   })  : _target = target,
-        _gestureTamperer = gestureTamperer,
+        _gestureProxy = gestureTamperer,
         _lastDetails = details,
         pointerDeviceKind = details.kind {
     // Actual work is done by this object.
@@ -277,8 +274,7 @@ class SheetDragController implements Drag, ScrollActivityDelegate {
 
   late SheetDragControllerTarget _target;
 
-  // TODO: Rename to _gestureProxy.
-  SheetGestureTamperer? _gestureTamperer;
+  SheetGestureProxyMixin? _gestureProxy;
 
   /// The details of the most recently observed drag event.
   SheetDragDetails get lastDetails => _lastDetails;
@@ -292,8 +288,8 @@ class SheetDragController implements Drag, ScrollActivityDelegate {
     _target = delegate;
   }
 
-  void updateGestureTamperer(SheetGestureTamperer? gestureTamperer) {
-    _gestureTamperer = gestureTamperer;
+  void updateGestureTamperer(SheetGestureProxyMixin? gestureTamperer) {
+    _gestureProxy = gestureTamperer;
   }
 
   @override
@@ -321,17 +317,17 @@ class SheetDragController implements Drag, ScrollActivityDelegate {
         velocityX: rawDetails.velocity.pixelsPerSecond.dx,
         velocityY: -1 * velocity,
       );
-      if (_gestureTamperer case final tamper?) {
-        endDetails = tamper.tamperWithDragEnd(endDetails);
+      if (_gestureProxy case final tamper?) {
+        endDetails = tamper.onDragEnd(endDetails);
       }
       _lastDetails = endDetails;
-      _target.applyUserDragEnd(endDetails);
+      _target.onDragEnd(endDetails);
     } else {
       final cancelDetails = SheetDragCancelDetails(
         axisDirection: _target.dragAxisDirection,
       );
       _lastDetails = cancelDetails;
-      _gestureTamperer?.onDragCancel(cancelDetails);
+      _gestureProxy?.onDragCancel(cancelDetails);
       _target.onDragCancel(cancelDetails);
     }
   }
@@ -352,19 +348,19 @@ class SheetDragController implements Drag, ScrollActivityDelegate {
       deltaY: delta,
     );
 
-    if (_gestureTamperer case final tamper?) {
+    if (_gestureProxy case final tamper?) {
       final minPotentialDeltaConsumption =
           _target.computeMinPotentialDeltaConsumption(details.delta);
       assert(minPotentialDeltaConsumption.dx.abs() <= details.delta.dx.abs());
       assert(minPotentialDeltaConsumption.dy.abs() <= details.delta.dy.abs());
-      details = tamper.tamperWithDragUpdate(
+      details = tamper.onDragUpdate(
         details,
         minPotentialDeltaConsumption,
       );
     }
 
     _lastDetails = details;
-    _target.applyUserDragUpdate(details);
+    _target.onDragUpdate(details);
   }
 
   @override
@@ -388,7 +384,7 @@ class SheetDragController implements Drag, ScrollActivityDelegate {
 
   @mustCallSuper
   void dispose() {
-    _gestureTamperer = null;
+    _gestureProxy = null;
     _impl.dispose();
   }
 }
diff --git a/lib/src/foundation/sheet_extent.dart b/lib/src/foundation/sheet_extent.dart
index dec60d7..5ed7d3a 100644
--- a/lib/src/foundation/sheet_extent.dart
+++ b/lib/src/foundation/sheet_extent.dart
@@ -131,7 +131,7 @@ abstract class SheetExtent extends ChangeNotifier
     required Extent maxExtent,
     required SheetPhysics physics,
     this.debugLabel,
-    SheetGestureTamperer? gestureTamperer,
+    SheetGestureProxyMixin? gestureTamperer,
   })  : _physics = physics,
         _gestureTamperer = gestureTamperer,
         _metrics = SheetMetrics.empty.copyWith(
@@ -177,8 +177,8 @@ abstract class SheetExtent extends ChangeNotifier
   /// {@template SheetExtent.gestureTamperer}
   /// An object that can modify the gesture details of the sheet.
   /// {@endtemplate}
-  SheetGestureTamperer? get gestureTamperer => _gestureTamperer;
-  SheetGestureTamperer? _gestureTamperer;
+  SheetGestureProxyMixin? get gestureTamperer => _gestureTamperer;
+  SheetGestureProxyMixin? _gestureTamperer;
 
   /// A label that is used to identify this object in debug output.
   final String? debugLabel;
@@ -253,7 +253,7 @@ abstract class SheetExtent extends ChangeNotifier
   }
 
   @mustCallSuper
-  void updateGestureTamperer(SheetGestureTamperer? gestureTamperer) {
+  void updateGestureTamperer(SheetGestureProxyMixin? gestureTamperer) {
     if (_gestureTamperer != gestureTamperer) {
       _gestureTamperer = gestureTamperer;
       currentDrag?.updateGestureTamperer(gestureTamperer);
@@ -268,15 +268,9 @@ abstract class SheetExtent extends ChangeNotifier
   @mustCallSuper
   void applyNewContentSize(Size contentSize) {
     if (metrics.maybeContentSize != contentSize) {
-      final oldMaxPixels = metrics.maybeMaxPixels;
-      final oldMinPixels = metrics.maybeMinPixels;
       _oldContentSize = metrics.maybeContentSize;
       _updateMetrics(contentSize: contentSize);
       activity.didChangeContentSize(_oldContentSize);
-      if (oldMinPixels != metrics.minPixels ||
-          oldMaxPixels != metrics.maxPixels) {
-        activity.didChangeBoundaryConstraints(oldMinPixels, oldMaxPixels);
-      }
     }
   }
 
@@ -297,10 +291,10 @@ abstract class SheetExtent extends ChangeNotifier
   @mustCallSuper
   void applyNewBoundaryConstraints(Extent minExtent, Extent maxExtent) {
     if (minExtent != this.minExtent || maxExtent != this.maxExtent) {
+      final oldMinExtent = metrics.maybeMinExtent;
+      final oldMaxExtent = metrics.maybeMaxExtent;
       _updateMetrics(minExtent: minExtent, maxExtent: maxExtent);
-      final oldMinPixels = metrics.maybeMinPixels;
-      final oldMaxPixels = metrics.maybeMaxPixels;
-      activity.didChangeBoundaryConstraints(oldMinPixels, oldMaxPixels);
+      activity.didChangeBoundaryConstraints(oldMinExtent, oldMaxExtent);
     }
   }
 
@@ -438,7 +432,7 @@ abstract class SheetExtent extends ChangeNotifier
       kind: details.kind,
     );
     if (_gestureTamperer case final tamperer?) {
-      startDetails = tamperer.tamperWithDragStart(startDetails);
+      startDetails = tamperer.onDragStart(startDetails);
     }
 
     final drag = SheetDragController(
diff --git a/lib/src/foundation/sheet_extent_scope.dart b/lib/src/foundation/sheet_extent_scope.dart
index 32c0054..655f48d 100644
--- a/lib/src/foundation/sheet_extent_scope.dart
+++ b/lib/src/foundation/sheet_extent_scope.dart
@@ -80,7 +80,7 @@ abstract class SheetExtentScope extends StatefulWidget {
   final SheetPhysics physics;
 
   /// {@macro SheetExtent.gestureTamperer}
-  final SheetGestureTamperer? gestureTamperer;
+  final SheetGestureProxyMixin? gestureTamperer;
 
   // TODO: Remove this. Specifying null to `controller` is sufficient.
   final bool isPrimary;
diff --git a/lib/src/foundation/sheet_gesture_tamperer.dart b/lib/src/foundation/sheet_gesture_tamperer.dart
index 1a847dc..2142de0 100644
--- a/lib/src/foundation/sheet_gesture_tamperer.dart
+++ b/lib/src/foundation/sheet_gesture_tamperer.dart
@@ -4,40 +4,39 @@ import 'package:meta/meta.dart';
 import 'sheet_drag.dart';
 
 // TODO: Expose this as a public API.
-// TODO: Rename to SheetGestureProxy.
 @internal
-class TamperSheetGesture extends StatefulWidget {
-  const TamperSheetGesture({
+class SheetGestureProxy extends StatefulWidget {
+  const SheetGestureProxy({
     super.key,
     required this.tamperer,
     required this.child,
   });
 
-  final SheetGestureTamperer tamperer;
+  final SheetGestureProxyMixin tamperer;
   final Widget child;
 
   @override
-  State<TamperSheetGesture> createState() => _TamperSheetGestureState();
+  State<SheetGestureProxy> createState() => _SheetGestureProxyState();
 
-  static SheetGestureTamperer? maybeOf(BuildContext context) {
+  static SheetGestureProxyMixin? maybeOf(BuildContext context) {
     return context
-        .dependOnInheritedWidgetOfExactType<_TamperSheetGestureScope>()
+        .dependOnInheritedWidgetOfExactType<_SheetGestureProxyScope>()
         ?.tamperer;
   }
 }
 
-class _TamperSheetGestureState extends State<TamperSheetGesture> {
+class _SheetGestureProxyState extends State<SheetGestureProxy> {
   @override
   void didChangeDependencies() {
     super.didChangeDependencies();
-    widget.tamperer.updateParent(TamperSheetGesture.maybeOf(context));
+    widget.tamperer.updateParent(SheetGestureProxy.maybeOf(context));
   }
 
   @override
-  void didUpdateWidget(TamperSheetGesture oldWidget) {
+  void didUpdateWidget(SheetGestureProxy oldWidget) {
     super.didUpdateWidget(oldWidget);
     oldWidget.tamperer.updateParent(null);
-    widget.tamperer.updateParent(TamperSheetGesture.maybeOf(context));
+    widget.tamperer.updateParent(SheetGestureProxy.maybeOf(context));
   }
 
   @override
@@ -48,55 +47,51 @@ class _TamperSheetGestureState extends State<TamperSheetGesture> {
 
   @override
   Widget build(BuildContext context) {
-    return _TamperSheetGestureScope(
+    return _SheetGestureProxyScope(
       tamperer: widget.tamperer,
       child: widget.child,
     );
   }
 }
 
-// TODO: Rename to SheetGestureProxyScope.
-class _TamperSheetGestureScope extends InheritedWidget {
-  const _TamperSheetGestureScope({
+class _SheetGestureProxyScope extends InheritedWidget {
+  const _SheetGestureProxyScope({
     required this.tamperer,
     required super.child,
   });
 
-  final SheetGestureTamperer tamperer;
+  final SheetGestureProxyMixin tamperer;
 
   @override
-  bool updateShouldNotify(_TamperSheetGestureScope oldWidget) =>
+  bool updateShouldNotify(_SheetGestureProxyScope oldWidget) =>
       oldWidget.tamperer != tamperer;
 }
 
 // TODO: Expose this as a public API.
-// TODO: Rename to SheetGestureProxyMixin.
 @internal
-mixin SheetGestureTamperer {
-  SheetGestureTamperer? _parent;
+mixin SheetGestureProxyMixin {
+  SheetGestureProxyMixin? _parent;
 
   @mustCallSuper
-  void updateParent(SheetGestureTamperer? parent) {
+  void updateParent(SheetGestureProxyMixin? parent) {
     _parent = parent;
   }
 
   @useResult
   @mustCallSuper
-  // TODO: Rename to onDragStart.
-  SheetDragStartDetails tamperWithDragStart(SheetDragStartDetails details) {
-    return _parent?.tamperWithDragStart(details) ?? details;
+  SheetDragStartDetails onDragStart(SheetDragStartDetails details) {
+    return _parent?.onDragStart(details) ?? details;
   }
 
   @useResult
   @mustCallSuper
-  // TODO: Rename to onDragUpdate.
-  SheetDragUpdateDetails tamperWithDragUpdate(
+  SheetDragUpdateDetails onDragUpdate(
     SheetDragUpdateDetails details,
     Offset minPotentialDeltaConsumption,
   ) {
     return switch (_parent) {
       null => details,
-      final parent => parent.tamperWithDragUpdate(
+      final parent => parent.onDragUpdate(
           details,
           minPotentialDeltaConsumption,
         ),
@@ -105,9 +100,8 @@ mixin SheetGestureTamperer {
 
   @useResult
   @mustCallSuper
-  // TODO: Rename to onDragEnd.
-  SheetDragEndDetails tamperWithDragEnd(SheetDragEndDetails details) {
-    return _parent?.tamperWithDragEnd(details) ?? details;
+  SheetDragEndDetails onDragEnd(SheetDragEndDetails details) {
+    return _parent?.onDragEnd(details) ?? details;
   }
 
   @mustCallSuper
diff --git a/lib/src/foundation/sheet_physics.dart b/lib/src/foundation/sheet_physics.dart
index 105919e..eaa4b50 100644
--- a/lib/src/foundation/sheet_physics.dart
+++ b/lib/src/foundation/sheet_physics.dart
@@ -62,7 +62,8 @@ abstract class SheetPhysics {
 
   double computeOverflow(double offset, SheetMetrics metrics);
 
-  // TODO: Change to return a tuple of (physicsAppliedOffset, overflow) to avoid recomputation of the overflow.
+  // TODO: Change to return a tuple of (physicsAppliedOffset, overflow)
+  // to avoid recomputation of the overflow.
   double applyPhysicsToOffset(double offset, SheetMetrics metrics);
 
   Simulation? createBallisticSimulation(double velocity, SheetMetrics metrics);
diff --git a/lib/src/internal/float_comp.dart b/lib/src/internal/float_comp.dart
index 206c13d..fabb411 100644
--- a/lib/src/internal/float_comp.dart
+++ b/lib/src/internal/float_comp.dart
@@ -8,7 +8,8 @@ import 'package:meta/meta.dart';
 /// rarely changes during the app's lifetime.
 final _instanceForEpsilon = <double, FloatComp>{};
 
-// TODO: Reimplement this class as an extension type of [double] to avoid object creation.
+// TODO: Reimplement this class as an extension type of [double]
+// to avoid object creation.
 /// A comparator for floating-point numbers in a certain precision.
 ///
 /// [FloatComp.distance] and [FloatComp.velocity] determine the [epsilon] based
diff --git a/lib/src/modal/modal_sheet.dart b/lib/src/modal/modal_sheet.dart
index e1ae95f..56d63c7 100644
--- a/lib/src/modal/modal_sheet.dart
+++ b/lib/src/modal/modal_sheet.dart
@@ -172,7 +172,7 @@ mixin ModalSheetRouteMixin<T> on ModalRoute<T> {
     Animation<double> secondaryAnimation,
   ) {
     return switch (swipeDismissible) {
-      true => TamperSheetGesture(
+      true => SheetGestureProxy(
           tamperer: _swipeDismissibleController,
           child: buildContent(context),
         ),
@@ -234,7 +234,7 @@ mixin ModalSheetRouteMixin<T> on ModalRoute<T> {
   }
 }
 
-class _SwipeDismissibleController with SheetGestureTamperer {
+class _SwipeDismissibleController with SheetGestureProxyMixin {
   _SwipeDismissibleController({
     required this.route,
     required this.transitionController,
@@ -258,7 +258,7 @@ class _SwipeDismissibleController with SheetGestureTamperer {
   }
 
   @override
-  SheetDragUpdateDetails tamperWithDragUpdate(
+  SheetDragUpdateDetails onDragUpdate(
     SheetDragUpdateDetails details,
     Offset minPotentialDeltaConsumption,
   ) {
@@ -288,7 +288,7 @@ class _SwipeDismissibleController with SheetGestureTamperer {
       assert(dragDelta * effectiveDragDelta >= 0);
     } else {
       // Otherwise, the drag delta doesn't change the transition progress.
-      return super.tamperWithDragUpdate(details, minPotentialDeltaConsumption);
+      return super.onDragUpdate(details, minPotentialDeltaConsumption);
     }
 
     final viewport = _context.size!.height;
@@ -309,21 +309,21 @@ class _SwipeDismissibleController with SheetGestureTamperer {
       VerticalDirection.down => viewportDelta - dragDelta,
     };
 
-    return super.tamperWithDragUpdate(
+    return super.onDragUpdate(
       details.copyWith(deltaY: unconsumedDragDelta),
       minPotentialDeltaConsumption,
     );
   }
 
   @override
-  SheetDragEndDetails tamperWithDragEnd(SheetDragEndDetails details) {
+  SheetDragEndDetails onDragEnd(SheetDragEndDetails details) {
     final wasHandled = _handleDragEnd(
       velocity: details.velocity,
       axisDirection: details.axisDirection,
     );
     return wasHandled
-        ? super.tamperWithDragEnd(details.copyWith(velocityX: 0, velocityY: 0))
-        : super.tamperWithDragEnd(details);
+        ? super.onDragEnd(details.copyWith(velocityX: 0, velocityY: 0))
+        : super.onDragEnd(details);
   }
 
   @override
diff --git a/lib/src/navigation/navigation_routes.dart b/lib/src/navigation/navigation_routes.dart
index f3e7e75..a0596f7 100644
--- a/lib/src/navigation/navigation_routes.dart
+++ b/lib/src/navigation/navigation_routes.dart
@@ -34,7 +34,7 @@ class _ScrollableNavigationSheetRouteContent extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     final theme = SheetTheme.maybeOf(context);
-    final gestureTamper = TamperSheetGesture.maybeOf(context);
+    final gestureTamper = SheetGestureProxy.maybeOf(context);
 
     return NavigationSheetRouteContent(
       scopeBuilder: (context, key, child) {
@@ -76,7 +76,7 @@ class _DraggableNavigationSheetRouteContent extends StatelessWidget {
   Widget build(BuildContext context) {
     final theme = SheetTheme.maybeOf(context);
     final physics = this.physics ?? theme?.physics ?? kDefaultSheetPhysics;
-    final gestureTamper = TamperSheetGesture.maybeOf(context);
+    final gestureTamper = SheetGestureProxy.maybeOf(context);
 
     return NavigationSheetRouteContent(
       scopeBuilder: (context, key, child) {
diff --git a/lib/src/navigation/navigation_sheet.dart b/lib/src/navigation/navigation_sheet.dart
index 567570e..1c77abb 100644
--- a/lib/src/navigation/navigation_sheet.dart
+++ b/lib/src/navigation/navigation_sheet.dart
@@ -47,7 +47,7 @@ class _NavigationSheetState extends State<NavigationSheet>
 
   @override
   Widget build(BuildContext context) {
-    final gestureTamper = TamperSheetGesture.maybeOf(context);
+    final gestureTamper = SheetGestureProxy.maybeOf(context);
     final controller =
         widget.controller ?? SheetControllerScope.maybeOf(context);
 
diff --git a/lib/src/navigation/navigation_sheet_activity.dart b/lib/src/navigation/navigation_sheet_activity.dart
index 4b55db0..3236f8a 100644
--- a/lib/src/navigation/navigation_sheet_activity.dart
+++ b/lib/src/navigation/navigation_sheet_activity.dart
@@ -65,15 +65,8 @@ class TransitionSheetActivity extends NavigationSheetActivity {
     Size? oldViewportSize,
     EdgeInsets? oldViewportInsets,
   ) {
-    // Appends the delta of the bottom inset (typically the keyboard height)
-    // to keep the visual sheet position unchanged.
-    final newInsets = owner.metrics.viewportInsets;
-    final oldInsets = oldViewportInsets ?? newInsets;
-    final deltaInsetBottom = newInsets.bottom - oldInsets.bottom;
-    if (deltaInsetBottom != 0) {
-      owner
-        ..setPixels(owner.metrics.pixels - deltaInsetBottom)
-        ..didUpdateMetrics();
+    if (oldViewportInsets != null) {
+      absorbBottomViewportInset(owner, oldViewportInsets);
     }
   }
 }
diff --git a/lib/src/scrollable/scrollable_sheet.dart b/lib/src/scrollable/scrollable_sheet.dart
index c2fd72f..8a61eee 100644
--- a/lib/src/scrollable/scrollable_sheet.dart
+++ b/lib/src/scrollable/scrollable_sheet.dart
@@ -52,7 +52,7 @@ class _ScrollableSheetState extends State<ScrollableSheet>
   Widget build(BuildContext context) {
     final theme = SheetTheme.maybeOf(context);
     final physics = widget.physics ?? theme?.physics ?? kDefaultSheetPhysics;
-    final gestureTamper = TamperSheetGesture.maybeOf(context);
+    final gestureTamper = SheetGestureProxy.maybeOf(context);
     final controller =
         widget.controller ?? SheetControllerScope.maybeOf(context);
 
diff --git a/lib/src/scrollable/scrollable_sheet_activity.dart b/lib/src/scrollable/scrollable_sheet_activity.dart
index bac85a9..d4011fb 100644
--- a/lib/src/scrollable/scrollable_sheet_activity.dart
+++ b/lib/src/scrollable/scrollable_sheet_activity.dart
@@ -191,7 +191,7 @@ class DragScrollDrivenSheetActivity extends ScrollableSheetActivity
   }
 
   @override
-  void applyUserDragUpdate(SheetDragUpdateDetails details) {
+  void onDragUpdate(SheetDragUpdateDetails details) {
     scrollPosition.userScrollDirection = details.deltaY > 0.0
         ? ScrollDirection.forward
         : ScrollDirection.reverse;
@@ -206,7 +206,7 @@ class DragScrollDrivenSheetActivity extends ScrollableSheetActivity
   }
 
   @override
-  void applyUserDragEnd(SheetDragEndDetails details) {
+  void onDragEnd(SheetDragEndDetails details) {
     owner
       ..didDragEnd(details)
       ..goBallisticWithScrollPosition(
diff --git a/lib/src/scrollable/scrollable_sheet_extent.dart b/lib/src/scrollable/scrollable_sheet_extent.dart
index 573cc15..db2403d 100644
--- a/lib/src/scrollable/scrollable_sheet_extent.dart
+++ b/lib/src/scrollable/scrollable_sheet_extent.dart
@@ -152,7 +152,7 @@ class ScrollableSheetExtent extends SheetExtent
       kind: details.kind,
     );
     if (gestureTamperer case final tamperer?) {
-      startDetails = tamperer.tamperWithDragStart(startDetails);
+      startDetails = tamperer.onDragStart(startDetails);
     }
     final heldPreviousVelocity = switch (activity) {
       final HoldScrollDrivenSheetActivity holdActivity =>


Test patch:
diff --git a/test/draggable/draggable_sheet_test.dart b/test/draggable/draggable_sheet_test.dart
index 84950b8..881fa38 100644
--- a/test/draggable/draggable_sheet_test.dart
+++ b/test/draggable/draggable_sheet_test.dart
@@ -36,13 +36,10 @@ class _TestApp extends StatelessWidget {
 
 class _TestSheetContent extends StatelessWidget {
   const _TestSheetContent({
-    super.key,
     this.height = 500,
-    this.child,
   });
 
   final double? height;
-  final Widget? child;
 
   @override
   Widget build(BuildContext context) {
@@ -50,7 +47,6 @@ class _TestSheetContent extends StatelessWidget {
       height: height,
       width: double.infinity,
       color: Colors.white,
-      child: child,
     );
   }
 }
diff --git a/test/foundation/sheet_viewport_test.dart b/test/foundation/sheet_viewport_test.dart
index 97677a1..a8cd1cd 100644
--- a/test/foundation/sheet_viewport_test.dart
+++ b/test/foundation/sheet_viewport_test.dart
@@ -27,23 +27,24 @@ class _FakeSheetContext extends Fake implements SheetContext {
 class _FakeSheetActivity extends SheetActivity {
   _FakeSheetActivity({
     this.shouldIgnorePointer = false,
-    this.status = SheetStatus.stable,
   });
 
   @override
   final bool shouldIgnorePointer;
 
   @override
-  final SheetStatus status;
+  SheetStatus get status => SheetStatus.stable;
 }
 
 class _FakeSheetExtent extends SheetExtent {
   _FakeSheetExtent({
-    super.minExtent = const Extent.proportional(0.5),
-    super.maxExtent = const Extent.proportional(1),
-    super.physics = const ClampingSheetPhysics(),
     this.createIdleActivity,
-  }) : super(context: _FakeSheetContext());
+  }) : super(
+          context: _FakeSheetContext(),
+          minExtent: const Extent.proportional(0.5),
+          maxExtent: const Extent.proportional(1),
+          physics: const ClampingSheetPhysics(),
+        );
 
   final ValueGetter<SheetActivity>? createIdleActivity;
 
diff --git a/test/navigation/navigation_sheet_test.dart b/test/navigation/navigation_sheet_test.dart
index 4ec74cb..0c49ec9 100644
--- a/test/navigation/navigation_sheet_test.dart
+++ b/test/navigation/navigation_sheet_test.dart
@@ -12,7 +12,6 @@ class _TestWidget extends StatelessWidget {
     this.sheetTransitionObserver, {
     required this.initialRoute,
     required this.routes,
-    this.onTapBackgroundText,
     this.sheetKey,
     this.contentBuilder,
     this.sheetBuilder,
@@ -22,7 +21,6 @@ class _TestWidget extends StatelessWidget {
 
   final String initialRoute;
   final Map<String, ValueGetter<Route<dynamic>>> routes;
-  final VoidCallback? onTapBackgroundText;
   final Widget Function(BuildContext, Widget)? contentBuilder;
   final Widget Function(BuildContext, Widget)? sheetBuilder;
   final SheetController? sheetController;
@@ -53,7 +51,7 @@ class _TestWidget extends StatelessWidget {
     Widget content = Stack(
       children: [
         TextButton(
-          onPressed: onTapBackgroundText,
+          onPressed: () {},
           child: const Text('Background text'),
         ),
         navigationSheet,
@@ -536,7 +534,8 @@ void main() {
       await tester.pump(const Duration(milliseconds: 300));
       // Press and hold the list view in the second page
       // until the transition animation is finished.
-      // TODO: Change warnIfMissed to 'true' and verify that the long press gesture fails.
+      // TODO: Change warnIfMissed to 'true' and verify that
+      // the long press gesture fails.
       await tester.press(find.byKey(const Key('Second')), warnIfMissed: false);
       await tester.pumpAndSettle();
       // Ensure that the transition is completed without any exceptions.
diff --git a/test/scrollable/scrollable_sheet_test.dart b/test/scrollable/scrollable_sheet_test.dart
index 6c16996..cc7ddf4 100644
--- a/test/scrollable/scrollable_sheet_test.dart
+++ b/test/scrollable/scrollable_sheet_test.dart
@@ -44,13 +44,11 @@ class _TestSheetContent extends StatelessWidget {
     this.height = 500,
     this.itemCount = 30,
     // Disable the snapping effect by default in tests.
-    this.scrollPhysics = const ClampingScrollPhysics(),
     this.onTapItem,
   });
 
   final double? height;
   final int itemCount;
-  final ScrollPhysics? scrollPhysics;
   final void Function(int index)? onTapItem;
 
   @override
@@ -60,7 +58,7 @@ class _TestSheetContent extends StatelessWidget {
       child: Material(
         color: Colors.white,
         child: ListView(
-          physics: scrollPhysics,
+          physics: const ClampingScrollPhysics(),
           children: List.generate(
             itemCount,
             (index) => ListTile(
diff --git a/test/src/stubbing.mocks.dart b/test/src/stubbing.mocks.dart
index b042aeb..02626bc 100644
--- a/test/src/stubbing.mocks.dart
+++ b/test/src/stubbing.mocks.dart
@@ -296,7 +296,7 @@ class MockSheetExtent extends _i1.Mock implements _i3.SheetExtent {
       );
 
   @override
-  void updateGestureTamperer(_i13.SheetGestureTamperer? gestureTamperer) =>
+  void updateGestureTamperer(_i13.SheetGestureProxyMixin? gestureTamperer) =>
       super.noSuchMethod(
         Invocation.method(
           #updateGestureTamperer,