A package that provides a highly customizable sheet widget that snaps to different vertical & horizontal positions

Overview

Snapping Sheet

example workflow pub points popularity likes License: MIT

A package that provides a highly customizable sheet widget that snaps to different vertical & horizontal positions

Can adapt to scrollable
widgets inside the sheet.
Fully customizable animation
and content.
Not limited to the bottom part
of the app.

You can find and run the examples closing this repository and running the app from the example folder.

Getting started

As usual, begin by adding the package to your pubspec.yaml file, see install instruction.

Here is the most basic setup with the Snapping Sheet:

    import 'package:flutter/material.dart';
    import 'package:snapping_sheet/snapping_sheet.dart';

    class GettingStartedExample extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
            return Scaffold(
                body: SnappingSheet(
                    // TODO: Add your content that is placed
                    // behind the sheet. (Can be left empty)
                    child: MyOwnPageContent(), 
                    grabbingHeight: 75,
                    // TODO: Add your grabbing widget here,
                    grabbing: MyOwnGrabbingWidget(),
                    sheetBelow: SnappingSheetContent(
                        draggable: true,
                        // TODO: Add your sheet content here
                        child: MyOwnSheetContent(),
                    ),
                ),
            );
        }
    }

Customize snapping positions

To change the snap positions for the sheet, change the snappingPositions parameter witch takes in a list of SnappingPosition.factor or SnappingPosition.pixels. These two objects are used to specify the location using a factor or pixels. You also have the option to specify the duration and curve of how the sheet should snap to that given position.

    SnappingSheet(
        snappingPositions: [
            SnappingPosition.factor(
                positionFactor: 0.0,
                snappingCurve: Curves.easeOutExpo,
                snappingDuration: Duration(seconds: 1),
                grabbingContentOffset: GrabbingContentOffset.top,
            ),
            SnappingPosition.pixels(
                positionPixels: 400,
                snappingCurve: Curves.elasticOut,
                snappingDuration: Duration(milliseconds: 1750),
            ),
            SnappingPosition.factor(
                positionFactor: 1.0,
                snappingCurve: Curves.bounceOut,
                snappingDuration: Duration(seconds: 1),
                grabbingContentOffset: GrabbingContentOffset.bottom,
            ),
        ],
    )

Adding content to the sheet

You can place content both below or/and above the grabbing part of the sheet. If you do not want any content in the above or below part of the sheet, pass in null.

  • sizeBehavior: How the size of the content should behave. Can either be SheetSizeFill which fills the available height of the sheet, or SheetSizeStatic, which takes in a height that is respected in the sheet.
  • draggable: If the sheet itself can be draggable to expand or close the Snapping Sheet.
  • child: Any Widget of your choosing.
    SnappingSheet(
        sheetAbove: SnappingSheetContent(
            sizeBehavior: SheetSizeFill(),
            draggable: false,
            child: Container(color: Colors.blue),
        ),
        sheetBelow: SnappingSheetContent(
            sizeBehavior: SheetSizeStatic(size: 300),
            draggable: true,
            child: Container(color: Colors.red),
        ),
    )

Make SnappingSheet adapt to a scroll controller

In order to make the sheet know about the scroll controller, you need to provide it in the SnappingSheetContent class (See example below). It is recommended to set lockOverflowDrag to true to prevent the sheet to be dragged above or below its max and min snapping position.

     SnappingSheet(
        lockOverflowDrag: true, // (Recommended) Set this to true.
        sheetBelow: SnappingSheetContent(
            // Pass in the scroll controller here!
            childScrollController: _myScrollController,
            draggable: true,
            child: ListView(
                // And in the scrollable widget that you create!
                controller: _myScrollController,

                // OBS! Should be false if it is in sheetBelow.
                // OBS! Should be true if it is in sheetAbove.
                reverse: false,
            ),
        ),
    )

OBS that the scrollable widget, e.g ListView, SingleChildScrollView, etc. needs to have the correct reverse value depending on were it is located. If the scrollable widget is in the sheetBelow, the reverse value should be set to false. If it is located in the sheetAbove it should be set to true. The reason is that the current logic of the SnappingSheet only support that configuration of a scrollable widget.

Using the SnappingSheetController

You can control the Snapping Sheet using the SnappingSheetController

    // Create your controller
    final snappingSheetController = SnappingSheetController();
    SnappingSheet(

        // Connect it to the SnappingSheet
        controller: SnappingSheetController();

        sheetAbove: SnappingSheetContent(
            draggable: false,
            child: Container(color: Colors.blue),
        ),
        sheetBelow: SnappingSheetContent(
            draggable: true,
            child: Container(color: Colors.red),
        ),
    )

    // You can now control the sheet in multiple ways.
    snappingSheetController.snapToPosition(
      SnappingPosition.factor(positionFactor: 0.75),
    );
    snappingSheetController.stopCurrentSnapping();
    snappingSheetController.setSnappingSheetPosition(100);

    // You can also extract information from the sheet
    snappingSheetController.currentPosition;
    snappingSheetController.currentSnappingPosition;
    snappingSheetController.currentlySnapping;
    snappingSheetController.isAttached;

Listen to changes

You can listen to movement and when a snapping animation is completed by using the following parameters:

    SnappingSheet(
        onSheetMoved: (sheetPosition) {
            print("Current position ${sheetPosition.pixels}");
        },
        onSnapCompleted: (sheetPosition, snappingPosition) {
            print("Current position ${sheetPosition.pixels}");
            print("Current snapping position $snappingPosition");
        },
        onSnapStart: (sheetPosition, snappingPosition) {
            print("Current position ${sheetPosition.pixels}");
            print("Next snapping position $snappingPosition");
        },
    )

Horizontal Snapping Sheet

There also exist a horizontal mode for the snapping sheet. To use it, see the code below:

    SnappingSheet.horizontal(
        // TODO: Add your content that is placed
        // behind the sheet. (Can be left empty)
        child: MyOwnPageContent(), 
        grabbingWidth: 50,
        // TODO: Add your grabbing widget here,
        grabbing: MyOwnGrabbingWidget(),
        sheetLeft: SnappingSheetContent(
            draggable: true,
            // TODO: Add your sheet content here
            child: MyOwnSheetContent(),
        ),
    ),

A more complex example of a horizontal SnappingSheet can be found here.

Comments
  • minHeight Property to prevent the panel from disappearing

    minHeight Property to prevent the panel from disappearing

    Thank you for this amazing package. I'm wondering is it possible to add a minHeight property to prevent the panel from disappear. Now when I slide the panel really fast down below it just disappear.

    Thanks

    opened by salehahmedZ 7
  • Allow multiple Snapping Sheets

    Allow multiple Snapping Sheets

    Is there any way to add trigger different Snapping Sheets with different configurations (e.g snappingPositions, grabbingHeight, etc.)? Currently each Snapping Sheet needs to have one child. But I would like to have multiple sheets for this single child.

    opened by kamami 6
  • Error when dragging sheet down

    Error when dragging sheet down

    So i dont know how to explain this, but will do my best. When the app launches and i drag the sheet down while keep holding it, i get the following error:

    The following NoSuchMethodError was thrown while dispatching a pointer event:
    The method '_getPositionInPixels' was called on null.
    Receiver: null
    Tried calling: _getPositionInPixels(210.0)
    
    When the exception was thrown, this was the stack
    #0      Object.noSuchMethod  (dart:core-patch/object_patch.dart:53:5)
    #1      _SnappingSheetState._getSnapPositionInPixels 
    package:snapping_sheet/snapping_sheet.dart:268
    #2      _SnappingSheetState._snapToPosition 
    package:snapping_sheet/snapping_sheet.dart:250
    #3      _SnappingSheetState._animateToClosestStop 
    package:snapping_sheet/snapping_sheet.dart:237
    #4      _SnappingSheetState._wrapDraggable.<anonymous closure> 
    package:snapping_sheet/snapping_sheet.dart:313
    ...
    Event: PointerUpEvent#2f3ce(position: Offset(226.0, 483.0), timeStamp: 60:51:45.292000, pointer: 117, kind: touch, down: false, pressure: 0.2, pressureMin: 0.0)
        position: Offset(226.0, 483.0)
    Target: RenderPointerListener#aa8f9
        parentData: right=0.0; bottom=-39.0; left=0.0; height=50.0; offset=Offset(0.0, 249.0) (can use size)
        constraints: BoxConstraints(w=360.0, h=50.0)
        size: Size(360.0, 50.0)
        behavior: translucent
        listeners: down, move, up
    
    

    This happens when you drag it down for the first time after app launches, and without releasing it. Like i mean i grab it, drag to down of the screen without releasing your finger, and i get this error. After that snapPositions got broken as well. It stops anywhere on the screen. If i drag it down than release my finger it works, the problem occurs when i keep going down of the screen without releasing the grabber. And it gets broken.

    Here is my code if needed:

                SnappingSheet(
                      sheetBelow: Padding(
                        padding: EdgeInsets.only(top: 20.0),
                        child: Align(
                          alignment: Alignment(0.90, -1.0),
                        ),
                      ),
                      onSnapEnd: () {
                        if (_controller.currentSnapPosition.positionFactor == 1.2) {
                          _toggleShowDetailStatus();
                        }
                      },
                      snappingSheetController: _controller,
                      snapPositions: const [
                        SnapPosition(
                            positionFactor: 0.2,
                            snappingCurve: Curves.elasticOut,
                            snappingDuration: Duration(milliseconds: 750)),
                        SnapPosition(positionFactor: 0.6),
                        SnapPosition(positionFactor: 1.0),
                        SnapPosition(positionFactor: 1.2),
                      ],
                      initSnapPosition: SnapPosition(positionFactor: 0.6),
                      grabbingHeight: MediaQuery.of(context).padding.bottom + 50,
                      grabbing: GrabSection(),
                      sheetAbove: _sheetContent(),
                    )
    
    

    This is my first submitting an issue on github so im sorry if the format is wrong or something. I can provide any additional informations if needed.

    I also added a video of the problem which will makes things more clearer i guess. The same problem also occurs on your Example app you created.

    Screen record link: https://streamable.com/mmllg

    edit1: added code block edit2: I want to fix that the error doesnt only occur after app launches but whenever i reopen the snap sheet. Hope the video will make it clear.

    bug 
    opened by talhakerpicci 3
  • Problem with state of SpanningSheet widget

    Problem with state of SpanningSheet widget

    opened by II11II 3
  • Snapping positions not working as expected with controller

    Snapping positions not working as expected with controller

    Hi, I have 2 snapping points as follows:

    snappingPositions: [
        SnappingPosition.pixels(
        positionPixels: context.percentWidth * 0, 
        snappingCurve: Curves.decelerate,
        snappingDuration: const Duration(milliseconds: 400),
        grabbingContentOffset: GrabbingContentOffset.top,
    ),
        SnappingPosition.pixels(
        positionPixels: context.percentWidth * 40,
        snappingCurve: Curves.decelerate,
       snappingDuration: const Duration(milliseconds: 400),
    ),], 
    

    When I drag the snapping sheet the snapping positions works as expected but when I try to hide de sheet using a control and the same snapping position the sheet hides a bit like this:

    draggableController.snapToPosition( SnappingPosition.pixels(positionPixels: 0));

    Is this intended?, is there a way to make the controller to move exactly to the same as the snappingPositions?

    thanks

    opened by mikechari 2
  • GrabbingWidget wrong position at bottom

    GrabbingWidget wrong position at bottom

    GrabbingWidget get cut in half when you set

    initialSnappingPosition:
              const SnappingPosition.factor(positionFactor: 0.4),
    snappingPositions: const [
      const SnappingPosition.factor(
        positionFactor: 0.8,
        grabbingContentOffset: GrabbingContentOffset.middle,
      ),
      const SnappingPosition.factor(
        positionFactor: 0.4,
        grabbingContentOffset: GrabbingContentOffset.top,
      ),
      const SnappingPosition.factor(
        positionFactor: 0.0,
        grabbingContentOffset: GrabbingContentOffset.middle,
      ),
    ],
    

    When the SnappingPosition is 0.0 will do https://github.com/AdamJonsson/snapping_sheet/blob/f46832b8b7c428fb618c5c76fa7e54e04d8fdf63/lib/src/snapping_sheet_widget.dart#L311

    A fix will be to change to

    final position = max(_currentPosition - widget.grabbingHeight / 2, 0.0);
    

    It's working for the below sheet didn't test if this will work for the top sheet.

    bug 
    opened by valentingrigorean 2
  • Fix wrongly calculated position in pixels of the first snap position

    Fix wrongly calculated position in pixels of the first snap position

    I believe we have to subtract the grabbingHeight from the _currentConstraints.maxHeight in order to calculate the correct pixel position of the first SnapPosition, just as the function _getSnapPositionInPixels does.

      /// Getting the snap position in pixels
      double _getSnapPositionInPixels(SnapPosition snapPosition) {
        return snapPosition._getPositionInPixels(
            _currentConstraints.maxHeight - widget.grabbingHeight);
      }
    
    opened by b1acKr0se 2
  • Add function to close sheetBelow programmatically

    Add function to close sheetBelow programmatically

    is there any way to close sheet below programmatically, because in my case I use RefreshIndicator when on refresh in sheet below will be show loading widget only that and I updated state SnapPosition position factor

    opened by digarahayu 2
  • Fix gesturedetection for buttons inside grabbing widget

    Fix gesturedetection for buttons inside grabbing widget

    Issue from: "...The SnappingCheat reacts to a "single tap" in the grabber section. Therefore I'm looking for way that the grabber won't listen to taps, because I have buttons in this section and the SnappingSheet should not move when the user just taps these buttons."

    bug 
    opened by AdamJonsson 2
  • The bottom sheet below the screen when navigate

    The bottom sheet below the screen when navigate

    I have a Screen A with the snapping sheet and a button to navigate to Screen B. Screen B has a button to open the snapping sheet with the snappingController in Screen A. When i navigate to Screen B and click button open snapping sheet, it works but it is below the screen B. I see it when i go back from Screen B. How can i fix this?

    opened by nguyenxuankiet262 1
  • [Feature] Dragbble according to scroll controller.

    [Feature] Dragbble according to scroll controller.

    In case i have listview inside my bottom snapping sheet, and it on top. the user swipe to the bottom and close the snapping sheet.

    I would be nice feature to have that, draggble know to adapt to the listview inside. reference tiktok app, comments section

    opened by dvird 1
  • Is this library still maintained?

    Is this library still maintained?

    Hi,

    I want to use this library for a production project but I have to ensure that it's still maintained, because I didn't see any change in 1 year I'm wondering if this library is still maintained?

    I'm looking to make a bottom sheet Maps like with multiple snapping points and a textfield but I have a weird behavior with the keyboard and the snapping sheet.

    opened by Bazni 4
  • feat: make SnappingSheetController listenable

    feat: make SnappingSheetController listenable

    This allows to listen for position changes of the SnappingSheetController. Might be useful e.g. to transform or animcate the sheet's content based on the current position.

    The Listenable overrides are forwarded to the ValueNotifier inside the _SnappingSheetState.

    • implement Listenable in SnappingSheetController
    • use ValueNotifier for current position

    Signed-off-by: TheOneWithTheBraid [email protected]

    opened by TheOneWithTheBraid 4
  • Feature: set pixel snapping position reference point to either start or end

    Feature: set pixel snapping position reference point to either start or end

    Sometimes its useful to be able to set a pixel margin from the top of the snapping sheet; for instance to avoid safe-area infringement.

    This PR adds a snappingPositionAnchor argument to the SnappingPosition.pixels constructor for specifying the offset anchor to either start (default) or end.

    opened by LucasAschenbach 0
  • Dynamic grabbingHeight causes snapping to not complete the entire path. (buggy)

    Dynamic grabbingHeight causes snapping to not complete the entire path. (buggy)

    First off amazing package!

    I am running into this issue where dynamic values seem to confuse the sheets snapping positions.

    My grabbing widget changes in height. Hence why I need to change the grabbingHeight property dynamically in my build function.

    It seems like the SnappingSheet widget doesn't refresh the new values while snapping is in motion. After snapping is done (and off by a few pixels), I can drag and release the sheet slightly just to find it snap in the right place.

    On onSnapStart -> set expanded bool to true/false

    onSnapStart: (sheetPosition, snappingPosition) {
      if (snappingPosition.grabbingContentOffset == -1) {
        setState(() {
          _isExpanded = true;
        });
      } else {
        setState(() {
          _isExpanded = false;
        });
      }
    },
    

    In build function

    var grabbingHeight = _isExpanded ? 150  : 105 ;
    

    Any workarounds?

    opened by shantaly 0
Releases(3.1.0)
  • 3.1.0(Apr 22, 2021)

    • New feature: Horizontal snapping sheets are not possible using SnappingSheet.horizontal(...)!
    • New feature: controller.snapToPosition returns a future.
    • Breaking: New and more callback data on moving events.
    • Breaking: SheetSizeStatic(height: 123) is now SheetSizeStatic(size: 123).
    • Remove debug output
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0+2(Mar 17, 2021)

  • 3.0.0+1(Mar 17, 2021)

  • 3.0.0(Mar 17, 2021)

    • FULL REFACTORING OF PROJECT. The package has gone through a full refactoring while adding null-safety. Be aware of many breaking changes, it is strongly recommended to read the documentation of this package again if updating from an earlier version of the Snapping Sheet.
    • Added many many tests for the package to make it more stable and easier to maintain in the feature.
    • The Snapping Sheet can now adapt to a scrollable widget located inside it.
    • To Snapping Sheet no longer gets stuck at certain positions.
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0-nullsafety.0(Mar 16, 2021)

Owner
Adam Jonsson
AdamJonsson->sayHi(); AdamJonsson->description();
Adam Jonsson
Vineet Kalghatgi 32 May 13, 2022
Custom dropdown widget allows to add highly customizable widget in your projects with proper open and close animations and also comes with form required validation.

Custom Dropdown Custom Dropdown package lets you add customizable animated dropdown widget. Features Lots of properties to use and customize dropdown

Abdullah Chauhan 22 Dec 29, 2022
A smartphone application called Easy Job goal is to make easier for businesses to find people who meet their standards as well as for job seekers to search for and choose from available positions .

Easy_Jobs 19SW54(MAD-Project) A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to ge

Muskan 2 Nov 6, 2022
A highly customizable Flutter widget to render and interact with JSON objects.

The spreadsheet with superpowers ✨ ! JSON Data Explorer A highly customizable widget to render and interact with JSON objects. Features Expand and col

rows 15 Dec 21, 2022
The flutter_calendar_widget is highly customizable calendar widget.

flutter_calendar_widget The flutter_calendar_widget is highly customizable calendar widget. Not only can you change the style, but you can also change

dooboolab 4 Jun 24, 2022
A Flutter slidable widget that provides an easy to use configuration. Highly customisable. Just as you want it!

A Flutter slidable widget that provides an easy to use configuration. Highly customisable. Just as you want it!

Ravi Kavaiya 89 Dec 8, 2022
Draggable Scrollbar - A scrollbar that can be dragged for quickly navigation through a vertical list.

A scrollbar that can be dragged for quickly navigation through a vertical list. Additionaly it can show label next to scrollthumb with information about current item, for example date of picture created

Flutter Community 425 Dec 10, 2022
Create highly customizable, simple, and controllable autocomplete fields for Flutter.

Field Suggestion Installing Depend on it Add this to your package's pubspec.yaml file: dependencies: field_suggestion: <latest_version> Install it Y

Ismael Shakverdiev 21 Oct 18, 2022
Powerful, helpfull, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server with boilerplate code free.

flutter_axelor_sdk Powerful, helpful, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server w

Abd al-Rahman al-Ktefane 5 Dec 25, 2022
A highly customizable Flutter color picker.

FlexColorPicker FlexColorPicker is a customizable color picker for Flutter. The ColorPicker can show six different types of color pickers, three of wh

Rydmike 132 Dec 14, 2022
A highly customizable Flutter color picker.

FlexColorPicker FlexColorPicker is a customizable color picker for Flutter. The ColorPicker can show six different types of color pickers, three of wh

Rydmike 132 Dec 14, 2022
⚡️A highly customizable, powerful and easy-to-use alerting library for Flutter.

Flash ⚡️ A highly customizable, powerful and easy-to-use alerting library for Flutter. Website: https://sososdk.github.io/flash Specs This library all

null 368 Jan 5, 2023
Add beautiful animated effects & builders in Flutter, via an easy, highly customizable unified API.

Flutter Animate A performant library that makes it simple to add almost any kind of animated effect in Flutter. Pre-built effects, like fade, scale, s

Grant Skinner 352 Dec 25, 2022
A #Flutter package that let you draw a flow chart diagram with different kind of customizable elements

Flutter Flow Chart A package that let you draw a flow chart diagram with different kind of customizable elements. Dashboards can be saved for later us

Marco Bavagnoli 50 Jan 1, 2023
This library provides a customizable Flutter widget that makes it easy to display text in the middle of a Divider.

1. About 1.1. Introduction 1.1.1. Install Library 1.1.2. Import It 1.1.3. Use TextDivider 1.2. Details 1.2.1. Customization Options 1.2.2. Horizontal

Kato Shinya 2 Feb 9, 2022
A Dart package that provides a customizable country phone code picker for your Flutter application

A Flutter package that provides an easy and customizable country phone code picker widget! Features This package comes with a lot of customization all

Samit Kapoor 1 Oct 1, 2022
A CustomPaint example where we can draw with different colors and different stroke sizes

CustomPaint A CustomPaint example where we can draw with different colors and different stroke sizes. A Flutter application which runs on iOS, Android

Behruz Hurramov 0 Dec 27, 2021
OrderFoody - A template with command buttons in horizontal scrolling list

order_coffee In this project you create a template with command buttons in horiz

Le Gia Huy 3 Jun 22, 2022
A highly customisable and simple widget for having iOS 13 style tab bars.

cupertino_tabbar A highly customisable and simple widget for having iOS 13 style tab bars. It is highly recommended to read the documentation and run

AliYigitBireroglu 98 Oct 31, 2022