flutter DraggableScrollableSheet with sticky headers

Viewed 2219

I am implementing a DraggableScrollableSheet in my flutter app and want to have a sticky header, i.e. only the list view scrolls and the top part of the sheet always stays inplace. My widget looks like this:

SizedBox.expand(
    child: DraggableScrollableSheet(
      maxChildSize: 0.9,
      minChildSize: 0.2,
      initialChildSize: 0.3,
      expand: false,
      builder:
          (BuildContext context, ScrollController scrollController) {
        return Container(
          decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.only(
                topLeft: Radius.circular(20),
                topRight: Radius.circular(20),
              )),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Align(
                alignment: Alignment.topCenter,
                child: Container(
                  margin: EdgeInsets.symmetric(vertical: 8),
                  height: 8.0,
                  width: 70.0,
                  decoration: BoxDecoration(
                      color: Colors.grey[400],
                      borderRadius: BorderRadius.circular(10.0)),
                ),
              ),
              SizedBox(height: 16),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 24),
                child: Text(
                  'Neuigkeiten',
                  style: TextStyle(
                      fontSize: 20, fontWeight: FontWeight.bold),
                ),
              ),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 24),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    SizedBox(height: 20.0),
                    Text('Erfahre mehr ... '),
                  ],
                ),
              ),
              SizedBox(height: 16),
              Expanded(child: NewsList(controller: scrollController))
            ],
          ),
        );
      },
    ),
  );

The basic functionality works, however the sheet is only draggable and scrollable when dragging/scrolling on the list view items. What changes do I need to make to make the other widgets in the column also scrollable. I tried the Scrollable and Draggable widget without solution.

Any help is appreciated.

3 Answers

The problem is the ListView itself. For this you need to use a SliverList, check this answer

 Widget _buildDiscoverDrawer() {
    return DraggableScrollableSheet(
        maxChildSize: 0.95,
        minChildSize: 0.25,
        initialChildSize: 0.4,
        builder: (context, scrollController) {
          List<Widget> _sliverList(int size, int sliverChildCount) {
            List<Widget> widgetList = [];
            for (int index = 0; index < size; index++)
              widgetList
                ..add(SliverAppBar(
                  title: Text("Title $index"),
                  pinned: true,
                ))
                ..add(SliverFixedExtentList(
                  itemExtent: 50.0,
                  delegate: SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                    return Container(
                      alignment: Alignment.center,
                      color: Colors.lightBlue[100 * (index % 9)],
                      child: Text('list item $index'),
                    );
                  }, childCount: sliverChildCount),
                ));

            return widgetList;
          }

          return CustomScrollView(
            controller: scrollController,
            slivers: _sliverList(50, 10),
          );
        });
  }

The video tutorial says that the builder should return a scrollable widget, like SingleChildScrollView or ListView. Have you tried using one of those instead of the Container?

You can get this working by using a Stack as the content of your sheet. This way, you can make the ListView span the full size of the sheet (so you can drag it even if you touch the header), and have the header on top as an overlay. Make sure that you add some padding on the top of the list though, so you don't hide the first items under your header.

You have to make sure that the header does not consume the swipe events though. For example, I wanted to use AppBar on top, but that widget consumed the swipe events. If you want to use a widget that consumes these events, you should wrap it in an IgnorePointer().

Here is the example from the flutter documentation, but now with a sticky AppBar header.

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('DraggableScrollableSheet'),
      ),
      body: SizedBox.expand(
        child: DraggableScrollableSheet(
          builder: (BuildContext context, ScrollController scrollController) {
            return Stack(
              children: [

                //this section is your list
                Container(
                  color: Colors.blue[100],
                  child: ListView.builder(
                    controller: scrollController,
                    itemCount: 26,
                    itemBuilder: (BuildContext context, int index) {
                      if(index == 0)return Container(height: 50);//this is the padding on the top of the list
                      return ListTile(title: Text('Item ${index-1}'));
                      },
                  ),
                ),

                //this section is your header
                IgnorePointer(
                  child: Container(
                    height: 50,
                    child: AppBar(
                      title: Text("Sheet title"),
                      automaticallyImplyLeading: false,//this prevents the appBar from having a close button (that button wouldn't work because of IgnorePointer)
                    ),
                )),
              ],
            );
          },
        ),
      ),
    );
  }
}
Related