Flexible Sheet
A flexible, persistent sheet widget for Flutter that supports top-to-bottom and bottom-to-top directions with configurable snap behavior, spring physics, and programmatic control.
Features
- Bidirectional — slides from top or bottom via
SheetDirection.topToBottom/SheetDirection.bottomToTop - Snap behavior — snap to edges or remain at the released position via
SheetSnapBehavior.snapToEdge/SheetSnapBehavior.freePosition - Spring physics — fine-tune mass, stiffness, damping, and velocity through
SheetPhysics - Programmatic control —
open(),close(),toggle(),animateTo(height)viaFlexibleSheetController - Handle visibility — show or hide the drag handle at runtime with
showHandle()/hideHandle() - Width & alignment — optionally constrain the sheet width and align it horizontally within its parent
- Callbacks — listen to open/close state changes (
onStateChanged) and continuous height updates (onHeightChanged) - Zero dependencies — built entirely on the Flutter SDK with no external packages
Demo
Getting Started
Add the package to your project:
flutter pub add flexible_sheet
Import it:
import 'package:flexible_sheet/flexible_sheet.dart';
Usage
Top-to-Bottom Sheet (default)
final controller = FlexibleSheetController();
FlexibleSheet(
maxHeight: 500,
minHeight: 50,
controller: controller,
childBuilder: (height) => MyContent(height: height),
handleBuilder: (height) => const MyHandle(),
onStateChanged: (isOpen) => debugPrint('isOpen: $isOpen'),
onHeightChanged: (h) => debugPrint('height: ${h.round()}'),
);
// Programmatic control
controller.open();
controller.close();
controller.toggle();
controller.animateTo(250);
Bottom-to-Top Sheet
FlexibleSheet(
maxHeight: 500,
minHeight: 50,
direction: SheetDirection.bottomToTop,
controller: controller,
childBuilder: (height) => MyContent(height: height),
handleBuilder: (height) => const MyHandle(),
);
Free Position (no snapping)
FlexibleSheet(
maxHeight: 500,
minHeight: 50,
snapBehavior: SheetSnapBehavior.freePosition,
childBuilder: (height) => MyContent(height: height),
handleBuilder: (height) => const MyHandle(),
);
Custom Width & Alignment
FlexibleSheet(
maxHeight: 400,
minHeight: 60,
width: 320,
alignment: Alignment.centerRight,
childBuilder: (height) => MyContent(height: height),
handleBuilder: (height) => const MyHandle(),
);
Custom Spring Physics
FlexibleSheet(
maxHeight: 500,
minHeight: 50,
physics: SheetPhysics(
spring: SpringDescription(mass: 1, stiffness: 600, damping: 35),
defaultVelocity: 2000,
),
childBuilder: (height) => MyContent(height: height),
handleBuilder: (height) => const MyHandle(),
);
Handle Visibility Control
final controller = FlexibleSheetController();
// Hide the drag handle programmatically
controller.hideHandle();
// Show it again
controller.showHandle();
// Check current state
print(controller.isHandleVisible); // true or false
Start in Open State
final controller = FlexibleSheetController(initialIsOpen: true);
FlexibleSheet(
maxHeight: 500,
minHeight: 50,
initialHeight: 500,
controller: controller,
childBuilder: (height) => MyContent(height: height),
handleBuilder: (height) => const MyHandle(),
);
API Reference
FlexibleSheet Widget
| Parameter | Type | Default | Description |
|---|---|---|---|
maxHeight |
double |
required | Maximum height the sheet can expand to |
minHeight |
double |
required | Minimum height the sheet collapses to |
childBuilder |
Widget Function(double) |
required | Builder for the sheet content; receives the current height |
handleBuilder |
Widget Function(double)? |
null |
Builder for the drag handle; receives the current height |
controller |
FlexibleSheetController? |
null |
Controller for programmatic open, close, toggle, and animation |
direction |
SheetDirection |
topToBottom |
Slide direction: topToBottom or bottomToTop |
snapBehavior |
SheetSnapBehavior |
snapToEdge |
Snap behavior on release: snapToEdge or freePosition |
physics |
SheetPhysics? |
default | Spring animation configuration (mass, stiffness, damping, velocity) |
initialHeight |
double? |
minHeight |
Starting height; must be between minHeight and maxHeight |
width |
double? |
null |
Optional fixed width; if null, the sheet fills its parent width |
alignment |
AlignmentGeometry? |
null |
Horizontal alignment when width is set; defaults to center |
isDraggable |
bool |
true |
Whether the user can drag the handle to resize |
onStateChanged |
ValueChanged<bool>? |
null |
Called when the sheet opens (true) or closes (false) |
onHeightChanged |
ValueChanged<double>? |
null |
Called on every height change (drag or animation) |
clipBehavior |
Clip |
Clip.hardEdge |
Clipping behavior for the sheet content |
FlexibleSheetController
Constructor
FlexibleSheetController({bool initialIsOpen = false})
| Parameter | Type | Default | Description |
|---|---|---|---|
initialIsOpen |
bool |
false |
Whether the sheet starts in the open state |
Methods
| Method | Description |
|---|---|
open() |
Animate the sheet to maxHeight |
close() |
Animate the sheet to minHeight |
toggle() |
Toggle between open and closed states |
animateTo(double height) |
Animate to a specific height (clamped to min/max) |
showHandle() |
Make the drag handle visible |
hideHandle() |
Hide the drag handle |
Properties
| Property | Type | Description |
|---|---|---|
isOpen |
bool |
Whether the sheet is currently at maxHeight |
currentHeight |
double |
The current height of the sheet |
isHandleVisible |
bool |
Whether the drag handle is currently visible |
SheetPhysics
const SheetPhysics({
SpringDescription spring = const SpringDescription(mass: 1, stiffness: 500, damping: 30),
double defaultVelocity = 1500,
})
| Parameter | Type | Default | Description |
|---|---|---|---|
spring |
SpringDescription |
mass: 1, stiffness: 500, damping: 30 | Spring configuration for animations |
defaultVelocity |
double |
1500 |
Default animation velocity in px/s for programmatic actions |
Enums
SheetDirection
| Value | Description |
|---|---|
topToBottom |
Content on top, handle below — dragging down expands the sheet |
bottomToTop |
Handle on top, content below — dragging up expands the sheet |
SheetSnapBehavior
| Value | Description |
|---|---|
snapToEdge |
Snaps to fully open or fully closed based on velocity and position |
freePosition |
Stays at the exact height where the user released the drag |
Migration from 1.x
// Before (1.x)
import 'package:persistent_top_sheet/persistent_top_sheet.dart';
final controller = PersistentTopSheetController();
PersistentTopSheet(
maxHeight: 500, minHeight: 50,
animationSpeed: 2000,
controller: controller,
childBuilder: (h) => ...,
handleBuilder: (h) => ...,
);
// After (2.0)
import 'package:flexible_sheet/flexible_sheet.dart';
final controller = FlexibleSheetController();
FlexibleSheet(
maxHeight: 500, minHeight: 50,
physics: SheetPhysics(defaultVelocity: 2000),
controller: controller,
childBuilder: (h) => ...,
handleBuilder: (h) => ...,
);
Additional Information
A complete working example is available in the /example folder.
Feel free to open an issue, contribute, or request features on GitHub!
Libraries
- flexible_sheet
- A flexible, persistent sheet widget for Flutter that supports top-to-bottom and bottom-to-top directions with configurable snap behavior.