Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add swipe dismiss sensitivity params for modals #222

Merged
merged 23 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
05edf0e
feat: add dismiss configuration params to modals
ice-orion Aug 20, 2024
9b860b8
feat: add SwipeDismissConfig for modals
ice-orion Aug 20, 2024
c370e7d
Merge branch 'main' into feature/modal-dismiss-params
ice-orion Aug 20, 2024
fdceb2c
refactor: rename SwipeDismissConfig, remove util class methods
ice-orion Aug 22, 2024
cc59aed
chore: format code
ice-orion Aug 23, 2024
81b7145
chore: bump version to 0.10.0, add swipeDismissSensitivity example usage
ice-orion Aug 23, 2024
836ce27
Rename swipeDismissSensitivity → sensitivity
fujidaiti Aug 23, 2024
b3b053d
Fix 80-chars warning
fujidaiti Aug 23, 2024
896d56a
format
fujidaiti Aug 23, 2024
a9a1c80
Use raw velocity rather than ratio for threshold
fujidaiti Aug 23, 2024
4c045b0
Revert "Use raw velocity rather than ratio for threshold"
fujidaiti Aug 23, 2024
42fe562
Rename minFlingVelocity to minFlingVelocityRatio
fujidaiti Aug 23, 2024
20e59a6
Tweak default ratio threshold
fujidaiti Aug 23, 2024
5e45637
Doc improvement
fujidaiti Aug 23, 2024
587ee6d
Doc improvement
fujidaiti Aug 23, 2024
6e246ea
Merge branch 'main' into feature/modal-dismiss-params
fujidaiti Aug 23, 2024
2a613ec
Merge branch 'main' into feature/modal-dismiss-params
fujidaiti Aug 31, 2024
85d9544
Update changelog
fujidaiti Aug 31, 2024
b103ce5
Add unit tests
fujidaiti Aug 31, 2024
f5d4b9e
Tweak default sensitivity
fujidaiti Aug 31, 2024
e7d91c8
Update examples
fujidaiti Aug 31, 2024
4098310
Mention new api in readme
fujidaiti Aug 31, 2024
91c0be3
Format
fujidaiti Aug 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.10.0 Aug 31, 2024

- Add `SwipeDismissSensitivity`, a way to customize sensitivity of swipe-to-dismiss action on modal sheet (#222)

## 0.9.3 Aug 19, 2024

- Fix: Press-and-hold gesture in PageView doesn't stop momentum scrolling (#219)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ Furthermore, [the modal sheets in the style of iOS 15](https://medium.com/surf-d

See also:

- [SwipeDismissSensitivity](https://pub.dev/documentation/smooth_sheets/latest/smooth_sheets/SwipeDismissSensitivity-class.html), which can be used to tweak the sensitivity of the swipe-to-dismiss action.
- [declarative_modal_sheet.dart](https://github.com/fujidaiti/smooth_sheets/blob/main/example/lib/tutorial/declarative_modal_sheet.dart), a tutorial of integration with declarative navigation using [go_router](https://pub.dev/packages/go_router) package.
- [imperative_modal_sheet.dart](https://github.com/fujidaiti/smooth_sheets/blob/main/example/lib/tutorial/imperative_modal_sheet.dart), a tutorial of integration with imperative Navigator API.
- [cupertino_modal_sheet.dart](https://github.com/fujidaiti/smooth_sheets/blob/main/example/lib/tutorial/cupertino_modal_sheet.dart), a tutorial of iOS style modal sheets.
Expand Down
8 changes: 7 additions & 1 deletion example/lib/tutorial/cupertino_modal_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ void _showModalSheet(BuildContext context, {required bool isFullScreen}) {
// Use `CupertinoModalSheetRoute` to show an ios 15 style modal sheet.
// For declarative navigation (Navigator 2.0), use `CupertinoModalSheetPage` instead.
final modalRoute = CupertinoModalSheetRoute(
swipeDismissible: true, // Enable the swipe-to-dismiss behavior.
// Enable the swipe-to-dismiss behavior.
swipeDismissible: true,
// Use `SwipeDismissSensitivity` to tweak the sensitivity of the swipe-to-dismiss behavior.
swipeDismissSensitivity: const SwipeDismissSensitivity(
minFlingVelocityRatio: 2.0,
minDragDistance: 300.0,
),
builder: (context) => switch (isFullScreen) {
true => const _FullScreenSheet(),
false => const _HalfScreenSheet(),
Expand Down
5 changes: 5 additions & 0 deletions example/lib/tutorial/declarative_modal_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ final _router = GoRouter(
key: state.pageKey,
// Enable the swipe-to-dismiss behavior.
swipeDismissible: true,
// Use `SwipeDismissSensitivity` to tweak the sensitivity of the swipe-to-dismiss behavior.
swipeDismissSensitivity: const SwipeDismissSensitivity(
minFlingVelocityRatio: 2.0,
minDragDistance: 200.0,
),
child: const _ExampleSheet(),
);
},
Expand Down
5 changes: 5 additions & 0 deletions example/lib/tutorial/imperative_modal_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ void _showModalSheet(BuildContext context) {
final modalRoute = ModalSheetRoute(
// Enable the swipe-to-dismiss behavior.
swipeDismissible: true,
// Use `SwipeDismissSensitivity` to tweak the sensitivity of the swipe-to-dismiss behavior.
swipeDismissSensitivity: const SwipeDismissSensitivity(
minFlingVelocityRatio: 2.0,
minDragDistance: 200.0,
),
builder: (context) => const _ExampleSheet(),
);

Expand Down
12 changes: 12 additions & 0 deletions lib/src/modal/cupertino.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart';
import '../foundation/sheet_controller.dart';
import '../internal/double_utils.dart';
import 'modal_sheet.dart';
import 'swipe_dismiss_sensitivity.dart';

const _minimizedViewportScale = 0.92;
const _cupertinoBarrierColor = Color(0x18000000);
Expand Down Expand Up @@ -422,6 +423,7 @@ class CupertinoModalSheetPage<T> extends Page<T> {
this.barrierColor = _cupertinoBarrierColor,
this.transitionDuration = _cupertinoTransitionDuration,
this.transitionCurve = _cupertinoTransitionCurve,
this.swipeDismissSensitivity = const SwipeDismissSensitivity(),
required this.child,
});

Expand All @@ -445,6 +447,8 @@ class CupertinoModalSheetPage<T> extends Page<T> {

final Curve transitionCurve;

final SwipeDismissSensitivity swipeDismissSensitivity;

@override
Route<T> createRoute(BuildContext context) {
return _PageBasedCupertinoModalSheetRoute(
Expand Down Expand Up @@ -485,6 +489,10 @@ class _PageBasedCupertinoModalSheetRoute<T>
@override
Duration get transitionDuration => _page.transitionDuration;

@override
SwipeDismissSensitivity get swipeDismissSensitivity =>
_page.swipeDismissSensitivity;

@override
String get debugLabel => '${super.debugLabel}(${_page.name})';

Expand All @@ -504,6 +512,7 @@ class CupertinoModalSheetRoute<T> extends _BaseCupertinoModalSheetRoute<T> {
this.barrierColor = _cupertinoBarrierColor,
this.transitionDuration = _cupertinoTransitionDuration,
this.transitionCurve = _cupertinoTransitionCurve,
this.swipeDismissSensitivity = const SwipeDismissSensitivity(),
});

final WidgetBuilder builder;
Expand All @@ -529,6 +538,9 @@ class CupertinoModalSheetRoute<T> extends _BaseCupertinoModalSheetRoute<T> {
@override
final Curve transitionCurve;

@override
final SwipeDismissSensitivity swipeDismissSensitivity;

@override
Widget buildContent(BuildContext context) {
return builder(context);
Expand Down
1 change: 1 addition & 0 deletions lib/src/modal/modal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export 'cupertino.dart'
CupertinoStackedTransition;
export 'modal_sheet.dart'
show ModalSheetPage, ModalSheetRoute, ModalSheetRouteMixin;
export 'swipe_dismiss_sensitivity.dart' show SwipeDismissSensitivity;
22 changes: 18 additions & 4 deletions lib/src/modal/modal_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import 'package:flutter/material.dart';
import '../foundation/sheet_drag.dart';
import '../foundation/sheet_gesture_tamperer.dart';
import '../internal/float_comp.dart';
import 'swipe_dismiss_sensitivity.dart';

const _minFlingVelocityToDismiss = 1.0;
const _minDragDistanceToDismiss = 100.0; // Logical pixels.
const _minReleasedPageForwardAnimationTime = 300; // Milliseconds.
const _releasedPageForwardAnimationCurve = Curves.fastLinearToSlowEaseIn;

Expand All @@ -26,6 +25,7 @@ class ModalSheetPage<T> extends Page<T> {
this.barrierColor = Colors.black54,
this.transitionDuration = const Duration(milliseconds: 300),
this.transitionCurve = Curves.fastEaseInToSlowEaseOut,
this.swipeDismissSensitivity = const SwipeDismissSensitivity(),
required this.child,
});

Expand All @@ -49,6 +49,8 @@ class ModalSheetPage<T> extends Page<T> {

final Curve transitionCurve;

final SwipeDismissSensitivity swipeDismissSensitivity;

@override
Route<T> createRoute(BuildContext context) {
return _PageBasedModalSheetRoute(
Expand Down Expand Up @@ -88,6 +90,10 @@ class _PageBasedModalSheetRoute<T> extends PageRoute<T>
@override
Duration get transitionDuration => _page.transitionDuration;

@override
SwipeDismissSensitivity get swipeDismissSensitivity =>
_page.swipeDismissSensitivity;

@override
String get debugLabel => '${super.debugLabel}(${_page.name})';

Expand All @@ -107,6 +113,7 @@ class ModalSheetRoute<T> extends PageRoute<T> with ModalSheetRouteMixin<T> {
this.swipeDismissible = false,
this.transitionDuration = const Duration(milliseconds: 300),
this.transitionCurve = Curves.fastEaseInToSlowEaseOut,
this.swipeDismissSensitivity = const SwipeDismissSensitivity(),
});

final WidgetBuilder builder;
Expand All @@ -132,6 +139,9 @@ class ModalSheetRoute<T> extends PageRoute<T> with ModalSheetRouteMixin<T> {
@override
final Curve transitionCurve;

@override
final SwipeDismissSensitivity swipeDismissSensitivity;

@override
Widget buildContent(BuildContext context) {
return builder(context);
Expand All @@ -141,6 +151,7 @@ class ModalSheetRoute<T> extends PageRoute<T> with ModalSheetRouteMixin<T> {
mixin ModalSheetRouteMixin<T> on ModalRoute<T> {
bool get swipeDismissible;
Curve get transitionCurve;
SwipeDismissSensitivity get swipeDismissSensitivity;

@override
bool get opaque => false;
Expand All @@ -149,6 +160,7 @@ mixin ModalSheetRouteMixin<T> on ModalRoute<T> {
late final _swipeDismissibleController = _SwipeDismissibleController(
route: this,
transitionController: controller!,
sensitivity: swipeDismissSensitivity,
);

Widget buildContent(BuildContext context);
Expand Down Expand Up @@ -226,10 +238,12 @@ class _SwipeDismissibleController with SheetGestureTamperer {
_SwipeDismissibleController({
required this.route,
required this.transitionController,
required this.sensitivity,
});

final ModalRoute<dynamic> route;
final AnimationController transitionController;
final SwipeDismissSensitivity sensitivity;

BuildContext get _context => route.subtreeContext!;

Expand Down Expand Up @@ -344,12 +358,12 @@ class _SwipeDismissibleController with SheetGestureTamperer {
invokePop = false;
} else if (effectiveVelocity < 0) {
// Flings down.
invokePop = effectiveVelocity.abs() > _minFlingVelocityToDismiss;
invokePop = effectiveVelocity.abs() > sensitivity.minFlingVelocityRatio;
} else if (FloatComp.velocity(MediaQuery.devicePixelRatioOf(_context))
.isApprox(effectiveVelocity, 0)) {
assert(draggedDistance >= 0);
// Dragged down enough to dismiss.
invokePop = draggedDistance > _minDragDistanceToDismiss;
invokePop = draggedDistance > sensitivity.minDragDistance;
} else {
// Flings up.
invokePop = false;
Expand Down
40 changes: 40 additions & 0 deletions lib/src/modal/swipe_dismiss_sensitivity.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:flutter/widgets.dart';

import 'modal_sheet.dart';

/// Configuration for the swipe-to-dismiss sensitivity of [ModalSheetRoute],
/// [ModalSheetPage], and related classes.
///
/// The modal will be dismissed under the following conditions:
/// - A downward fling gesture with the ratio of the velocity to the viewport
/// height that exceeds [minFlingVelocityRatio].
/// - A drag gesture ending with zero velocity, where the downward distance
/// exceeds [minDragDistance].
class SwipeDismissSensitivity {
/// Creates a swipe-to-dismiss sensitivity configuration.
const SwipeDismissSensitivity({
this.minFlingVelocityRatio = 2.0,
this.minDragDistance = 200.0,
});

/// Minimum ratio of gesture velocity to viewport height required to
/// trigger dismissal for a downward fling gesture.
///
/// The viewport height is obtained from the `size` property of the
/// navigator's [BuildContext] where the modal route belongs to.
/// Therefore, the larger the viewport height, the higher the velocity
/// required to dismiss the modal (and vice versa). This is to ensure that
/// the swipe-to-dismiss behavior is consistent across different screen sizes.
///
/// As a reference, the ratio of 1.0 corresponds to the velocity such that
/// the user moves their finger from the top to the bottom of the screen
/// in exactly 1 second.
final double minFlingVelocityRatio;

/// Minimum downward drag distance required for dismissal when the
/// gesture ends with zero velocity.
///
/// If the drag gesture ends with a non-zero velocity, it's treated as
/// a fling gesture, and this value is not used.
final double minDragDistance;
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: smooth_sheets
description: Sheet widgets with smooth motion and great flexibility. Also supports nested navigation in both imperative and declarative ways.
version: 0.9.3
version: 0.10.0
repository: https://github.com/fujidaiti/smooth_sheets
screenshots:
- description: Practical examples of smooth_sheets.
Expand Down
Loading
Loading