Engineering

How to scroll your bottom sheet differently with Flutter?

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:

  1. 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.
  2. 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
with marginwithout 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.