How to add scrollable tabs with a pinned header within a DraggableScrollableSheet?

1.2k Views Asked by At

I'm trying to use a NestedScrollView for scrollable tabs within a DraggableScrollableSheet and need to pass the sheet's controller to the NestedScrollView's scrollable body, however doing so is not supported by NestedScrollView which requires that inner scrollables implicitly use the context's PrimaryScrollController.

The resultant behaviour is that I am seeing:

  1. The draggable sheet behaviour is correct and the sheet moves up before scrolling but the header's elevation is not applied once scrolling nor is the header responding to drag events ("Foo" tab below), OR;
  2. The inverse of the above - the sheet does not respond to drag events but the header's elevation is correctly applied and the header itself correctly responds to gestures for scrolling ("Bar" tab below)

Screen-Recording-2020-12-06-at-5.35.01-pm.gif

I've tried a multiple combinations of NestedScrollView, CustomScrollView, and TabView, tried adding the controller to all, some, none of the scrollables. No luck. I have even tried listening to Notification events from either attached scrollable and update the other one with those events to try to keep them in sync. Nada.

Would appreciate any and all help on this, I've been tearing my hair out for a while now. Thank you!

Here's the code for main.dart in the video above:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: DraggableScrollableSheet(
        initialChildSize: 0.6,
        maxChildSize: 0.9,
        minChildSize: 0.6,
        builder: (context, scrollController) => DefaultTabController(
          length: 2,
          child: _buildNestedScrollView(scrollController),
        ),
      ),
    );
  }

  NestedScrollView _buildNestedScrollView(ScrollController scrollController) {
    return NestedScrollView(
      // controller: scrollController,
      headerSliverBuilder: (context, innerBoxIsScrolled) {
        print('innerBoxIsScrolled $innerBoxIsScrolled');
        return [
          SliverOverlapAbsorber(
            handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
            sliver: SliverAppBar(
              toolbarHeight: 0.0,
              pinned: true,
              primary: false,
              forceElevated: innerBoxIsScrolled,
              bottom: TabBar(
                tabs: [
                  Tab(text: 'Foo'),
                  Tab(text: 'Bar'),
                ],
              ),
            ),
          ),
        ];
      },
      body: TabBarView(
        children: [
          ListView.builder(
            controller: scrollController,
            physics: ClampingScrollPhysics(),
            padding: EdgeInsets.zero,
            itemBuilder: (context, index) => Container(
              color: Colors.primaries[index % 10],
              height: 150.0,
            ),
          ),
          ListView.builder(
            // controller: scrollController,
            physics: ClampingScrollPhysics(),
            padding: EdgeInsets.zero,
            itemBuilder: (context, index) => Container(
              color: Colors.primaries[index % 10],
              height: 25.0,
            ),
          ),
        ],
      ),
    );
  }
}

Relevant links:

https://api.flutter.dev/flutter/widgets/ScrollableWidgetBuilder.html

https://api.flutter.dev/flutter/widgets/NestedScrollView/body.html

Potentially relevant Flutter issue.

1

There are 1 best solutions below

0
On

smart solution

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: SizedBox.expand(
        child: DraggableScrollableSheet(
          initialChildSize: 0.6,
          maxChildSize: 0.9,
          minChildSize: 0.6,
          builder: (context, scrollController) => DefaultTabController(
            length: 2,
            child: SafeArea(
                child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                SingleChildScrollView(
                  physics: ClampingScrollPhysics(),
                  controller: scrollController,
                  child: Container(
                    color: Colors.white,
                    child: const TabBar(tabs: [
                      Tab(text: 'Foo'),
                      Tab(text: 'Bar'),
                    ]),
                  ),
                ),
                Expanded(
                  child: TabBarView(
                    children: [
                      ListView.builder(
                        controller: scrollController,
                        physics: ClampingScrollPhysics(),
                        padding: EdgeInsets.zero,
                        itemBuilder: (context, index) => Container(
                          color: Colors.primaries[index % 10],
                          height: 150.0,
                        ),
                      ),
                      ListView.builder(
                        // controller: scrollController,
                        physics: ClampingScrollPhysics(),
                        padding: EdgeInsets.zero,
                        itemBuilder: (context, index) => Container(
                          color: Colors.primaries[index % 10],
                          height: 25.0,
                        ),
                      ),
                    ],
                  ),
                )
              ],
            )),
          ),
        ),
      ),
    );
  }
}

enter image description here