GrabberSheet

pub.dev Test codecov license

한국어 문서 (Korean Docs)

A reusable and customizable draggable bottom sheet for Flutter, inspired by the modal sheet in the Google Maps app.

When building UIs with bottom sheets, developers often face the repetitive task of manually implementing drag handling and scroll physics. grabber_sheet solves this by providing a pre-built, robust drag mechanism that works seamlessly with your content, while offering extensive customization options to fit any design.

🚀 Installation

flutter pub add grabber_sheet

⚡ Simple Usage

Wrap your list, connect the controller. Done.

GrabberSheet(
  snap: true,
  builder: (context, controller) {
    return ListView.builder(
      controller: controller, // <--- Key Step: Connect this!
      itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
    );
  },
)

💡 Why GrabberSheet?

Pain Point DraggableScrollableSheet GrabberSheet
Gesture Conflicts ⚠️ Scrolls when you want to Drag Zero Conflict (Perfectly separated)
Header / Grabber ❌ Build it yourself (Hard) Built-in & Persistent
Snapping ⚠️ Complex math required One Parameter (snap: true)
Dev Experience 🔥 Frustrating 🍰 Plug & Play

🎨 Advanced Customization

1. Smart Snapping

Enable snap: true for a polished feel. It handles both slow drags (closest point) and fast flings (momentum) automatically.

GrabberSheet(
  snap: true,
  minChildSize: 0.2,
  maxChildSize: 1.0,
  snapSizes: const [0.5], // Add intermediate snap points
  // ...
)

snap gif

2. Custom Grabber Styling

Match your app's design system effortlessly.

GrabberSheet(
  grabberStyle: GrabberStyle(
    width: 60,
    height: 6,
    color: Colors.grey.shade300,
    radius: const Radius.circular(12),
  ),
  // ...
)

You can also hide the grabber completely (showGrabber: false).

3. Fixed Header (Non-Scrollable Area)

Need a title or close button that always stays visible? Use the bottom property.

GrabberSheet(
  bottom: Row(
    children: [
      Text('Locations', style: TextStyle(fontSize: 18)),
      Spacer(),
      CloseButton(),
    ],
  ),
  // ...
)

4. Programmatic Control

Open, close, or snap to specific sizes using GrabberSheetController.

final controller = GrabberSheetController();

// ...
controller.maximize(); // Go to maxChildSize
controller.minimize(); // Go to minChildSize
controller.animateTo(0.5); // Go to 50%
Example of GrabberSheet with FAB control

5. Desktop & Web Support

By default, the grabber is hidden on desktop/web (standard UI pattern). Force it visible if needed:

GrabberSheet(
  showGrabberOnNonMobile: true,
  // ...
)

grabber_sheet_web

6. Snap Animation Control

Customize the duration and curve of the snap animation for a personalized feel.

GrabberSheet(
  snap: true,
  snapAnimationDuration: const Duration(milliseconds: 500), // Default is 300ms
  snapAnimationCurve: Curves.bounceOut, // Default is Curves.easeOut
  // ...
)

7. Scrim Overlay

Add a semi-transparent overlay behind the sheet to draw focus to the sheet content. You can also dismiss the sheet by tapping this overlay.

GrabberSheet(
  showScrim: true,
  scrimColor: Colors.black.withOpacity(0.6), // Default is Colors.black54
  dismissOnScrimTap: true, // Default is true
  // ...
)

📘 Properties

Property Description Default
builder (Required) Builds content. Must use the provided ScrollController. -
snap Enable auto-snapping behavior. false
initialChildSize Starting height fraction (0.0 - 1.0). 0.5
minChildSize Minimum collapsed height. 0.25
maxChildSize Maximum expanded height. 1.0
snapSizes List of intermediate snap points (e.g. [0.5, 0.8]). null
showGrabber Toggle the grabber handle visibility. true
grabberStyle Customize width, height, color, radius, margin. Default Style
bottom Widget to display below grabber (Title, Buttons, etc.). null
controller Control sheet position programmatically. null
onSizeChanged Callback for size changes during drag/animation. null
onSnap Callback when snapping completes. null
snapAnimationDuration Duration of the snap animation. 300ms
snapAnimationCurve Curve of the snap animation. Curves.easeOut
showScrim Toggle the visibility of the background overlay. false
scrimColor Color of the background overlay. Colors.black54
dismissOnScrimTap Dismisses the sheet when the scrim is tapped. true

🤝 Contribution

Found a bug or have a feature request? Please visit the GitHub repository.

Libraries

grabber_sheet