If you’re not familiar with Bottom Sheets, we can take an example from the material design specification:
On the left-hand side we see a persistent bottom sheet, in this case it can be popped up and down to view the current track playing — persisting it’s state when out of view. On the right-hand side we have the modal bottom sheet, used to carry out interactions on the content shown within the screen.
Modal Bottom Sheet
When a Modal Bottom Sheet is displayed, it acts as a blocking widget — this means that it stops the user from interacting with other content within your application. This can be seen as an alternative to displaying some form of menu or dialog to the user. Pressing back or touching outside of the bottom sheet will dismiss it from view.
We can create and show a bottom sheet by using the showModalBottomSheet function, when this is done the internals of the class use the Navigator class to push this sheet as a route onto the navigator for the application.
showModalBottomSheet<void>(context: context,
builder: (BuildContext context) {
return new Column(
mainAxisSize
: MainAxisSize.min,
children: <Widget>[
new ListTile(
leading: new Icon(Icons.music_note),
title: new Text('Music'),
onTap: () => ...,
),
new ListTile(
leading: new Icon(Icons.photo_album),
title: new Text('Photos'),
onTap: () => ...,
),
new ListTile(
leading: new Icon(Icons.videocam),
title: new Text('Video'),
onTap: () => ...,
),
],
);
});
});
We can run this in our application and when we display the bottom sheet we’ll notice that the sheet is shown at the bottom of the screen.
For the builder argument of the showModalBottomSheet function we simply need to return a widget that is to be shown within the sheet. This will vary on your implementation requirements, but in the example here I have used a Column to display a collection of ListTile instances as these match the visual look of the sheet screenshot from the material guidelines above. When clicking outside of the bottom sheet, the dialog will be dismissed and completely removed out of the view. Because it is the modal bottom sheet, no state is persisted — it is there to invoke some form of action within the current context.
Note: If using a column for the sheet content, it’s important to use the mainAxisSize
property to MainAxisSize.min as this will ensure the content is wrapped.
Persistent Bottom Sheet
When a persistent bottom sheet is displayed, it is usually shown to compliment the current content of the application. These sheets can be used to display context aware content and will remain in place whilst the user is navigating around. Press back or dragging the bottom sheet downwards will pop the sheet back down until it is next prompted to be shown by the user. However, at this point it is still always kept in view ready for the user to pop back open again.
When we want to use the persistent bottom sheet we need to do things a little bit different. First of all we need to create a new GlobalKey instance to provide us with access to our Scaffold which will be used to display our persistent bottom sheet.
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
We then need to assign this key to our Scaffold like so:
return new Scaffold( key: _scaffoldKey, ....
And then we can use the Scaffold key to retrieve the current state and show our persistent bottom sheet:
_scaffoldKey.currentState .showBottomSheet<Null>((BuildContext context) { return new Container( child: new Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ new Padding( padding: const EdgeInsets.all(16.0), child: new Text( 'Persistent header for bottom bar!', textAlign: TextAlign.left, )), new Text( 'Then here there will likely be some other content ' 'which will be displayed within the bottom bar', textAlign: TextAlign.left, ), ], )); });
We need this additional setup as the persistent bottom sheet works a little differently than the modal bottom sheet. The modal bottom sheet is essentially a blocking dialog, so it can just be displayed on the screen to the user. But because the persistent bottom sheet needs to slide in and out of view, complimenting the current context, it needs to be aware of the Scaffold in which it is currently being displayed in.
When it comes to the content inside of the bottom sheet. just like the modal bottom sheet, we need to provide a widget that is to be shown.
The only difference here is that we need to be mindful of the content which is displayed. As shown in the material design screenshot at the top of this article, the persistent bottom sheet is always visible to the user — even when it is not expanded. Therefore, it is likely that you will want some form of header content which may remain in view when the sheet becomes expanded, or transforms into some other form of content to provide further insight to the user.
Another thing here is that you’ll notice how the persistent bottom sheet doesn’t display a background overlay when it is opened, this is because it is not intended to block the user from other interactions. Also unlike the modal bottom sheet, it can be popped up and down by performing a vertical swipe action on the widget.
When we call the showBottomSheet function here we are returned an instance of the PersistentBottomSheetController, we can use this controller to programatically close the bottom sheet:
PersistentBottomSheetController controller = _scaffoldKey.currentState.showBottomSheet...
controller.close();
We can also subscribe to the close event by retrieving a Future from the controller, allowing us to react once the bottom sheet has been closed:
controller.closed.then(...)
I hope this post has given some further insight into how you can implement bottom sheets into your applications, as well as which one is the right one to go for. If you have any questions or comments, then please do reach out!