scrollable_list_tab_scroller
Customizable Flutter widget that synchronizes a scrollable list of grouped items with tabs.
Create custom tabs that synchronize with inner ScrollView. The tabs follow the index of scroll view and vice-versa.

Installation
Add dependency for package on your pubspec.yaml:
dependencies:
    scrollable_list_tab_scroller: <latest>
Usage
To use this widget we must first define how our tabs will look like.
ScrollableListTabScroller
| Parameter | Definition | 
|---|---|
| int itemCount | Quantity item that will be built. | 
| IndexedWidgetBuilder itemBuilder | Builder of list item. | 
| IndexedActiveStatusWidgetBuilder tabBuilder | Builder of tab with active status for customization. | 
| HeaderContainerBuilder? headerContainerBuilder | Optional header container builder for customization. | 
| BodyContainerBuilder? bodyContainerBuilder | Optional body container builder for customization. | 
| ItemScrollController? itemScrollController | Optional controller for item list. | 
| ItemPositionsListener? itemPositionsListener | Optional listener of item list positions. | 
| void Function(int)? tabChanged | Optional listener of tab changes. | 
| double earlyChangePositionOffset | Optional vertical offset to change the index before the list item reaches the edge(default = 0). | 
| Duration animationDuration | Optional animation duration | 
| rest of the parameters | Optional scrollable_positioned_list's parameters | 
If you don't want to build your own HeaderContainer with a builder, you can instead use the ScrollableListTabScroller.defaultComponents() constructor, which provides additional properties for customizing the default header container and tab bar.
Example
import 'package:flutter/material.dart';
import 'package:scrollable_list_tab_scroller/scrollable_list_tab_scroller.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Scrollable List Tab Scroller',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Scrollable List Tab Scroller'),
    );
  }
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  final data = {
    "Category A": [
      "Item 1 (A)",
      "Item 2 (A)",
      "Item 3 (A)",
      "Item 4 (A)",
    ],
    "Category B": [
      "Item 1 (B)",
      "Item 2 (B)",
    ],
    "Category C": [
      "Item 1 (C)",
      "Item 2 (C)",
      "Item 3 (C)",
      "Item 4 (C)",
      "Item 5 (C)",
    ],
    "Category D": [
      "Item 1 (D)",
      "Item 2 (D)",
      "Item 3 (D)",
      "Item 4 (D)",
      "Item 5 (D)",
      "Item 6 (D)",
      "Item 7 (D)",
      "Item 8 (D)",
      "Item 9 (D)",
    ],
  };
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ScrollableListTabScroller(
        itemCount: data.length,
        tabBuilder: (BuildContext context, int index, bool active) => Padding(
          padding: EdgeInsets.symmetric(horizontal: 10),
          child: Text(
            data.keys.elementAt(index),
            style: !active
                ? null
                : TextStyle(fontWeight: FontWeight.bold, color: Colors.green),
          ),
        ),
        itemBuilder: (BuildContext context, int index) => Column(
          children: [
            Text(
              data.keys.elementAt(index),
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
            ),
            ...data.values
                .elementAt(index)
                .asMap()
                .map(
                  (index, value) => MapEntry(
                    index,
                    ListTile(
                      leading: Container(
                        height: 40,
                        width: 40,
                        decoration: BoxDecoration(
                            shape: BoxShape.circle, color: Colors.grey),
                        alignment: Alignment.center,
                        child: Text(index.toString()),
                      ),
                      title: Text(value),
                    ),
                  ),
                )
                .values
          ],
        ),
      ),
    );
  }
}
License
This project is licensed under the MIT license, additional knowledge about the license can be found here.