Skip to content

Commit

Permalink
Add automatic keyboard dismissing behavior on dragging/scrolling
Browse files Browse the repository at this point in the history
  • Loading branch information
fujidaiti committed Jan 15, 2024
1 parent 02d3f2e commit fb8effa
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 2 deletions.
1 change: 1 addition & 0 deletions package/lib/smooth_sheets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export 'src/draggable/draggable_sheet.dart';
export 'src/draggable/sheet_draggable.dart';
export 'src/foundation/animation.dart';
export 'src/foundation/framework.dart';
export 'src/foundation/keyboard_dismissible.dart';
export 'src/foundation/modal_sheet.dart';
export 'src/foundation/notification.dart';
export 'src/foundation/sheet_activity.dart';
Expand Down
1 change: 1 addition & 0 deletions package/lib/src/draggable/draggable_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class DraggableSheet extends SingleChildSheet {
const DraggableSheet({
super.key,
this.hitTestBehavior = HitTestBehavior.translucent,
super.keyboardDismissBehavior,
super.resizeToAvoidBottomInset,
super.initialExtent,
super.minExtent,
Expand Down
149 changes: 149 additions & 0 deletions package/lib/src/foundation/keyboard_dismissible.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import 'package:flutter/widgets.dart';
import 'package:smooth_sheets/src/draggable/draggable_sheet.dart';
import 'package:smooth_sheets/src/foundation/notification.dart';

/// A widget that dismisses the on-screen keyboard when the user
/// drags the sheet below this widget.
///
/// It is rarely used directly since the sheets internally have this widget
/// and expose a slot for a [SheetKeyboardDismissBehavior], which is directly
/// passed to this widget.
///
/// See also:
/// - [DraggableSheet.keyboardDismissBehavior], which is the slot for
/// a custom [SheetKeyboardDismissBehavior].
class SheetKeyboardDismissible extends StatelessWidget {
/// Creates a widget that dismisses the on-screen keyboard when the user
/// drags the sheet below this widget.
const SheetKeyboardDismissible({
super.key,
required this.dismissBehavior,
required this.child,
});

/// Determines when the on-screen keyboard should be dismissed.
final SheetKeyboardDismissBehavior dismissBehavior;

/// The widget below this widget in the tree.
final Widget child;

@override
Widget build(BuildContext context) {
Widget result = NotificationListener<SheetDragUpdateNotification>(
onNotification: (notification) {
if (primaryFocus?.hasFocus == true &&
dismissBehavior.shouldDismissKeyboard(notification.delta)) {
primaryFocus!.unfocus();
}
return false;
},
child: child,
);

if (dismissBehavior.isContentScrollAware) {
result = NotificationListener<ScrollUpdateNotification>(
onNotification: (notification) {
final dragDelta = notification.dragDetails?.delta.dy;
if (notification.depth == 0 &&
dragDelta != null &&
primaryFocus?.hasFocus == true &&
dismissBehavior.shouldDismissKeyboard(-1 * dragDelta)) {
primaryFocus!.unfocus();
}
return false;
},
child: result,
);
}

return result;
}
}

/// Determines when the on-screen keyboard should be dismissed.
abstract class SheetKeyboardDismissBehavior {
/// Creates an object that determines when the on-screen keyboard
/// should be dismissed.
const SheetKeyboardDismissBehavior({
this.isContentScrollAware = false,
});

/// {@macro drag_sheet_keyboard_dismiss_behavior.ctor}
const factory SheetKeyboardDismissBehavior.onDrag(
{bool isContentScrollAware}) = DragSheetKeyboardDismissBehavior;

/// {@macro drag_down_sheet_keyboard_dismiss_behavior.ctor}
const factory SheetKeyboardDismissBehavior.onDragDown(
{bool isContentScrollAware}) = DragDownSheetKeyboardDismissBehavior;

/// {@macro drag_up_sheet_keyboard_dismiss_behavior.ctor}
const factory SheetKeyboardDismissBehavior.onDragUp(
{bool isContentScrollAware}) = DragUpSheetKeyboardDismissBehavior;

/// Whether the sheet should be aware of the content scrolling.
///
/// If this is `true`, [shouldDismissKeyboard] will also be called whenever
/// the user scrolls a scrollable content within the sheet.
final bool isContentScrollAware;

/// Whether the on-screen keyboard should be dismissed.
///
/// This method is called whenever the sheet is dragged by the user.
/// Returns `true` if the on-screen keyboard should be dismissed.
bool shouldDismissKeyboard(double userDragDelta);
}

/// A [SheetKeyboardDismissBehavior] that always dismisses the on-screen
/// keyboard when the sheet is dragged.
class DragSheetKeyboardDismissBehavior extends SheetKeyboardDismissBehavior {
/// {@template drag_sheet_keyboard_dismiss_behavior.ctor}
/// Creates a [SheetKeyboardDismissBehavior] that always dismisses the
/// on-screen keyboard when the sheet is dragged.
///
/// If [isContentScrollAware] is `true`, the keyboard will also be dismissed
/// when the user scrolls a scrollable content within the sheet.
/// {@endtemplate}
const DragSheetKeyboardDismissBehavior({super.isContentScrollAware});

@override
bool shouldDismissKeyboard(double userDragDelta) {
return userDragDelta.abs() > 0;
}
}

/// A [SheetKeyboardDismissBehavior] that dismisses the on-screen keyboard
/// only when the sheet is dragged down.
class DragDownSheetKeyboardDismissBehavior
extends SheetKeyboardDismissBehavior {
/// {@template drag_down_sheet_keyboard_dismiss_behavior.ctor}
/// Creates a [SheetKeyboardDismissBehavior] that dismisses the on-screen
/// keyboard only when the sheet is dragged down.
///
/// If [isContentScrollAware] is `true`, the keyboard will also be dismissed
/// when the user scrolls up a scrollable content within the sheet.
/// {@endtemplate}
const DragDownSheetKeyboardDismissBehavior({super.isContentScrollAware});

@override
bool shouldDismissKeyboard(double userDragDelta) {
return userDragDelta < 0;
}
}

/// A [SheetKeyboardDismissBehavior] that dismisses the on-screen keyboard
/// only when the sheet is dragged up.
class DragUpSheetKeyboardDismissBehavior extends SheetKeyboardDismissBehavior {
/// {@template drag_up_sheet_keyboard_dismiss_behavior.ctor}
/// Creates a [SheetKeyboardDismissBehavior] that dismisses the on-screen
/// keyboard only when the sheet is dragged up.
///
/// If [isContentScrollAware] is `true`, the keyboard will also be dismissed
/// when the user scrolls down a scrollable content within the sheet.
/// {@endtemplate}
const DragUpSheetKeyboardDismissBehavior({super.isContentScrollAware});

@override
bool shouldDismissKeyboard(double userDragDelta) {
return userDragDelta > 0;
}
}
14 changes: 13 additions & 1 deletion package/lib/src/foundation/single_child_sheet.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/widgets.dart';
import 'package:smooth_sheets/src/foundation/framework.dart';
import 'package:smooth_sheets/src/foundation/keyboard_dismissible.dart';
import 'package:smooth_sheets/src/foundation/sheet_controller.dart';
import 'package:smooth_sheets/src/foundation/sheet_extent.dart';
import 'package:smooth_sheets/src/foundation/sheet_physics.dart';
Expand Down Expand Up @@ -34,6 +35,7 @@ abstract class SingleChildSheetExtentFactory extends SheetExtentFactory {
abstract class SingleChildSheet extends StatefulWidget {
const SingleChildSheet({
super.key,
this.keyboardDismissBehavior,
this.resizeToAvoidBottomInset = true,
this.initialExtent = const Extent.proportional(1),
this.minExtent = const Extent.proportional(1),
Expand All @@ -45,6 +47,7 @@ abstract class SingleChildSheet extends StatefulWidget {
required this.child,
});

final SheetKeyboardDismissBehavior? keyboardDismissBehavior;
final bool resizeToAvoidBottomInset;
final Extent initialExtent;
final Extent minExtent;
Expand Down Expand Up @@ -83,12 +86,21 @@ abstract class SingleChildSheetState<T extends SingleChildSheet>

@override
Widget build(BuildContext context) {
return SheetContainer(
Widget result = SheetContainer(
factory: factory,
controller: widget.controller,
resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
child: buildContent(context),
);

if (widget.keyboardDismissBehavior != null) {
result = SheetKeyboardDismissible(
dismissBehavior: widget.keyboardDismissBehavior!,
child: result,
);
}

return result;
}

/// Builds the content of the sheet.
Expand Down
14 changes: 13 additions & 1 deletion package/lib/src/navigation/navigation_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:smooth_sheets/src/foundation/framework.dart';
import 'package:smooth_sheets/src/foundation/keyboard_dismissible.dart';
import 'package:smooth_sheets/src/foundation/sheet_activity.dart';
import 'package:smooth_sheets/src/foundation/sheet_controller.dart';
import 'package:smooth_sheets/src/foundation/sheet_extent.dart';
Expand All @@ -15,6 +16,7 @@ class NavigationSheet extends StatefulWidget with TransitionAwareWidgetMixin {
const NavigationSheet({
super.key,
required this.transitionObserver,
this.keyboardDismissBehavior,
this.resizeToAvoidBottomInset = true,
this.controller,
required this.child,
Expand All @@ -23,6 +25,7 @@ class NavigationSheet extends StatefulWidget with TransitionAwareWidgetMixin {
@override
final NavigationSheetTransitionObserver transitionObserver;

final SheetKeyboardDismissBehavior? keyboardDismissBehavior;
final bool resizeToAvoidBottomInset;
final SheetController? controller;
final Widget child;
Expand Down Expand Up @@ -93,7 +96,7 @@ class NavigationSheetState extends State<NavigationSheet>

@override
Widget build(BuildContext context) {
return SheetContainer(
Widget result = SheetContainer(
resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
factory: const _NavigationSheetExtentFactory(),
controller: widget.controller,
Expand All @@ -102,6 +105,15 @@ class NavigationSheetState extends State<NavigationSheet>
},
child: widget.child,
);

if (widget.keyboardDismissBehavior != null) {
result = SheetKeyboardDismissible(
dismissBehavior: widget.keyboardDismissBehavior!,
child: result,
);
}

return result;
}
}

Expand Down
1 change: 1 addition & 0 deletions package/lib/src/scrollable/scrollable_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:smooth_sheets/src/scrollable/scrollable_sheet_extent.dart';
class ScrollableSheet extends SingleChildSheet {
const ScrollableSheet({
super.key,
super.keyboardDismissBehavior,
super.resizeToAvoidBottomInset,
super.initialExtent,
super.minExtent,
Expand Down

0 comments on commit fb8effa

Please sign in to comment.