Forked from Jaime Blasco's Sheet Flutter package (since it seems to have been abandoned)
https://github.com/jamesblasco/modal_bottom_sheet/tree/main/sheet
A fully customizable draggable bottom sheet for Flutter A canvas modal package that covers up part of the main screen
It allows to add a [Sheet] widget to any page or push a new [SheetRoute] to your navigator.
Add tarp to your pubspec
tarp:
git:
url: https://github.com/CyMathew/tarp
Learn more about:
- Using
Sheet
to create bottom sheets inside your page - Using
SheetRoute
orSheetPage
to push a new modal bottom sheet route
Sheet(
initialExtent: 200,
child: Container(color: Colors.blue[100]),
)
You can add it above any other widget. Usually you might want to use a Stack for that
Stack(
children: [
body,
Sheet(
initialExtent: 200,
child: Container(color: Colors.blue[100]),
),
],
)
The widget has several parameters that allow fully customization of the sheet
Use initialExtent
to set a initial offset from the bottom
Sheet(
initialExtent: 200,
child: Container(color: Colors.blue[100]),
)
You can set a minExtent
and maxExtent
to limit the position of the Sheet between those values
Sheet(
initialExtent: 200,
minExtent: 100,
maxExtent: 400,
child: Container(color: Colors.blue[100]),
)
Allow to open the sheet when is fully hidden
When the sheet is hidden you might wanna allow the user to drag up the bottom sheet even if this one is no visible. You can define an area where the interaction can be detected
Sheet(
initialExtent: 0,
minInteractionExtent: 20,
child: Container(color: Colors.blue[100]),
)
By default the sheet height will be the minimum between the max available height and the one recommended by the child.
It is possible to force the sheet child to be the maximum size available by setting SheetFit.expand
Sheet(
initialExtent: 200,
fit: SheetFit.expand,
child: Container(color: Colors.blue[100]),
)
By default the sheet has a fixed sized and it is vertically translated according to the user drag.
It is possible to make the sheet change the height of the child by setting resize: true
This will force the child to fit the available visual space.
Sheet(
initialExtent: 200,
resizable:true
child: Container(color: Colors.blue[100]),
)
It is possible to set a min height for a resizable sheet. When the height reaches that min value, the sheet will start vertically translating instead of shrinking
Sheet(
initialExtent: 200,
resizable: true
child: Container(color: Colors.blue[100]),
)
It is possible to pass a SheetController
to control programmatically the position of the sheet.
SheetController controller = SheetController();
Sheet(
controller: controller,
initialExtent: 200,
child: Container(color: Colors.blue[100]),
)
controller.jumpTo(400);
controller.relativeJumpTo(1); // Value between 0 and 1
controller.animateTo(400, ...);
controller.relativeAnimateTo(1, ...); // Value between 0 and 1
The sheet controller also contains an animation value that can be used to animate other parts of the ui in sync
AnimatedBuilder(
animation: controller.animation,
builder: (context, child) {
return ...;
}
);
You can push a new modal bottom sheet route above your current page using SheetRoute
SheetRoute<void>(
builder: (BuildContext) => Container(),
)
Or you can also use SheetPage
with the Navigator 2.0
Navigator(
pages: <Page<dynamic>>[
MaterialPage<bool>(
key: const ValueKey<String>('BooksListPage'),
child: BooksListScreen(
books: books,
onTapped: _handleBookTapped,
),
),
if (_selectedBook != null)
SheetPage<void>(
key: ValueKey<Book?>(_selectedBook),
child: BookDetailsScreen(book_selectedBook!),
barrierColor: Colors.black26,
)
],
)
An example using GoRouter is available here
late final GoRouter _router = GoRouter(
routes: <GoRoute>[
GoRoute(
path: '/',
pageBuilder: (BuildContext context, GoRouterState state) {
return MaterialExtendedPage<void>(
key: state.pageKey,
child: BooksListScreen(
books: books,
onBrigthnessChanged: (Brightness brightness) {
setState(() {
this.brightness = brightness;
});
},
),
);
},
routes: <GoRoute>[
GoRoute(
name: 'book',
path: 'book/:bid',
pageBuilder: (BuildContext context, GoRouterState state) {
final String id = state.pathParameters['bid']!;
final Book? book =
books.firstWhereOrNull((Book b) => b.id == id);
return CupertinoSheetPage<void>(
key: state.pageKey,
child: BookDetailsScreen(
book: book!,
),
);
},
redirect: (context, state) {
final String id = state.pathParameters['bid']!;
final Book? book =
books.firstWhereOrNull((Book b) => b.id == id);
if (book == null) {
return '/404';
}
// no need to redirect at all
return null;
},
),
]),
],
);
Use initialExtent
to set the initial position the bottom sheet will be dragged to:
SheetRoute(
initialExtent: 200,
builder: (BuildContext) => Container(color: Colors.blue[100]),
)
In order for the previous route to be affected by a fullscreen modal, you'll need to use MaterialExtendedPageRoute
or MaterialExtendedPage
to transition to the page in question.
Using Navigator:
Navigator.of(context).push(MaterialExtendedPageRoute((context) => (_YOUR_PAGE_HERE)));
Once you are on a MaterialExtendedPage
or navigated to it via a MaterialExtendedPageRoute
then you can use CupertinoSheetRoute
or CupertinoSheetPage
to have iOS style modals
Navigator.of(context).push(
CupertinoSheetRoute<void>(
initialDetent: Detents.medium,
detents: <double>[
Detents.none,
Detents.medium,
Detents.large
],
builder: (BuildContext context) => const ModalFit(),
),
);