How to pin multiple SliverAppBars to the top from different scroll views

Viewed 714

Here is the case, the first sliverAppBar pins to the top correctly when scrolling. Within customScrollView are two tabs that have its' own scroll view. Inside the first tab, is another sliverAppBar that is supposed to pin under the first one. However, it slides beneath the first sliverAppBar. Now since our complex view does not allow only one customScrollView to fix the problem, is there any other way?

See image here (Notice how first AppBar overlaps the second AppBar)

enter image description here

Desired Effect Before Scroll

enter image description here

Desired Effect After Scroll

enter image description here

import 'package:flutter/material.dart';
 
void main() => runApp(MyApp());
 
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Material App',
      home: Test(),
    );
  }
}
 
class Test extends StatelessWidget {
  const Test({Key? key}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    final List<String> _tabs = ['Tab 1', 'Tab 2'];
    return DefaultTabController(
      length: _tabs.length,
      child: Scaffold(
        body: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return [
              SliverAppBar(
                title: const Text('1st App Bar'),
                pinned: true,
                forceElevated: innerBoxIsScrolled,
                bottom: TabBar(
                  tabs: _tabs.map((String name) => Tab(text: name)).toList(),
                ),
              ),
            ];
          },
          body: TabBarView(
            children: [
              CustomScrollView(
                slivers: [
                  SliverPadding(
                    padding: const EdgeInsets.all(8.0),
                    sliver: SliverFixedExtentList(
                      itemExtent: 48.0,
                      delegate: SliverChildBuilderDelegate(
                        (context, index) {
                          return ListTile(
                            title: Text('Item $index'),
                          );
                        },
                        childCount: 3,
                      ),
                    ),
                  ),
                  const SliverAppBar(
                    toolbarHeight: 150,
                    pinned: true,
                    backgroundColor: Colors.purple,
                    title: Text('2nd App Bar'),
                  ),
                  SliverPadding(
                    padding: const EdgeInsets.all(8.0),
                    sliver: SliverFixedExtentList(
                      itemExtent: 48.0,
                      delegate: SliverChildBuilderDelegate(
                        (BuildContext context, int index) {
                          return ListTile(
                            title: Text('Item $index'),
                          );
                        },
                        childCount: 20,
                      ),
                    ),
                  ),
                ],
              ),
              Placeholder(),
            ],
          ),
        ),
      ),
    );
  }
}
2 Answers

We can use Scaffold to hold 1st appBar. And inside tab we can make 2nd appBar as pinned. Let me know if you wish any changes.

Output

enter image description here


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

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

class _TestState extends State<Test> with SingleTickerProviderStateMixin {
  static const List<Tab> myTabs = <Tab>[
    Tab(text: 'Tab1'),
    Tab(text: 'Tab2'),
  ];

  late TabController controller;

  @override
  void initState() {
    super.initState();
    controller = TabController(length: myTabs.length, vsync: this);
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Appbar 1"),
        bottom: TabBar(
          controller: controller,
          tabs: myTabs,
        ),
      ),
      body: TabBarView(
        controller: controller,
        children: [
          CustomScrollView(
            slivers: [
              SliverAppBar(
                title: Text("AppBar 2"),
                pinned: true,
              ),
              SliverPadding(
                padding: const EdgeInsets.all(8.0),
                sliver: SliverFixedExtentList(
                  itemExtent: 48.0,
                  delegate: SliverChildBuilderDelegate(
                    (context, index) {
                      return ListTile(
                        title: Text('Item $index'),
                      );
                    },
                    childCount: 213,
                  ),
                ),
              ),
            ],
          ),
          Container(
            color: Colors.pink,
          ),
        ],
      ),
    );
  }
}

I think u can use SliverPersistentHeader and SliverAppBar.

Related