Problem
Some time ago I had to implement a bottom sheet that should be possible to expand or collapse by dragging just a header and a user shouldn’t be bound to some fixed height of that bottom sheet. First of all, I tried to use DraggableScrollableSheet
(a standard BottomSheet included in Flutter), but it turned out that it didn’t offer such behavior out of the box. The default use of DraggableScrollableSheet
has two problems:
- We can’t allow users to adjust the height of the bottom sheet freely. We can set
snapSizes
parameter, which is a list of target sizes that the widget should snap to, but it’s not ideal. - The bottom sheet can be dragged anywhere to expand or collapse it and we want to constrain that action only for a specific part of that sheet - the header.
A common use case for DraggableScrollableSheet
:
Unfortunately on the internet, there is no clear solution on how to achieve the previous goals, so I want to share some with you 😀
So, our goal is to create something like the following:
Solution
So in the beginning, we have some logic inside initState
which is responsible for calculating the height of the bottom sheet’s header. As you can see, we use a commentsHeaderKey
key assigned on line 50 to the header widget. Thanks to that, we can get the height of our bottom sheet’s header. Later in the build
method on line 33, we use that height and viewPadding
(which shows us the size of the system’s UI like the status bar or system navigation) to calculate the initialChildSize
and minChildSize
for the DraggableScrollableBottomSheet
. As a result, the user sees only a header of our bottom sheet when the page is opened.
Then on line 42, we have to set the margin in order not to allow the bottom sheet to cover the system status bar at the top of the device. The previous line (41) is not required, it’s up to us which solution we prefer.
with margin | without margin |
---|---|
I think that the solution without a margin but with a higher header (to cover system navigation) would be the best. Something like that:
So, let’s back to the topic. The last very important thing is where we should put scrollController
received from DraggableScrollableSheet
’s builder method. That’s the tricky one because the first thing that comes to mind is just to pass it to the ListView
(or any scrollable widget) and that’s not something we want to do because we need to open/close the bottom sheet by dragging for a header and not list. That’s why on line 50, we pass it to CommentsHeader
widget. And then on line 81, we pass it to a scrollable widget connected just to the header and not the whole bottom sheet.
And I think that’s the main clue. As you can notice, it’s quite simple, but I was really struggling with it and that’s why this text has been written. For me from the future and for you, in order not to waste too much time on that 😁.
That was quite easy, right?
Do you know that the very first page that the user sees in your application is a Splash Screen? I explained how to easily add it to your app in my previous article. Check it out!
The full code presented in the following section can be found on my GitHub account here.