Search code examples
flutterlayoutscrollflutter-layout

Scroll ListView inside PageView from SingleChildScrollView in Flutter


I have a tab bar with a PageView inside each TabBarView. Each PageView has a ListView and when I scroll it, how can I scroll from the Scaffold's SingleChildScrollView rather than just scrolling the ListView inside the TabBarView?

Currently, the tab bar stays on the same position when I scroll the TabBarView, which looks terrible. How can I scroll individual TabBarView from SingleChildScrollView?

I tried tweaking with physics, but didn't turn out the way I wanted it to.

IMAGE :

enter image description here

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  int currIndex = 0;
  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController =
        TabController(vsync: this, length: 3, initialIndex: currIndex);

    _tabController.addListener(() {
      _handleTabSelection();
    });
  }

  void _handleTabSelection() {
    setState(() {
      currIndex = _tabController.index == null ? 0 : _tabController.index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: DefaultTabController(
          length: 3,
          initialIndex: 0,
          child: SingleChildScrollView(
            child: Container(
              height: MediaQuery.of(context).size.height,
              child: Column(
                children: [
                  Container(
                    child: Column(
                      children: [
                        Padding(
                          padding: const EdgeInsets.all(8),
                          child: Container(
                              child: Text(
                            "HOME",
                            style: TextStyle(fontSize: 25),
                          )),
                        ),
                        _buildTabBar(context),
                      ],
                    ),
                  ),
                  Expanded(
                    child: _buildTabBarView(
                      context,
                    ),
                  )
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

  TabBarView _buildTabBarView(BuildContext context) {
    return TabBarView(
        controller: _tabController,
        children: List.generate(
            3,
            (index) => Container(
                  color: Colors.red,
                  child: ListView.builder(
                    itemCount: 50,
                    itemBuilder: (context, index) {
                      return Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Container(
                            height: 60,
                            color: Colors.blue,
                            child: Center(child: Text("$index"))),
                      );
                    },
                  ),
                )));
  }

  Widget _buildTabBar(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(left: 8.0, right: 8),
      child: Container(
        height: 45,
        width: double.infinity,
        decoration: buildTabBarStyle(),
        child: TabBar(
          controller: _tabController,
          isScrollable: false,
          tabs: List.generate(
              3,
              (index) => Center(
                    child: Text(
                      "$index",
                      style: TextStyle(color: Colors.black),
                    ),
                  )),
        ),
      ),
    );
  }

  BoxDecoration buildTabBarStyle() {
    return BoxDecoration(
      color: Color.fromARGB(255, 230, 248, 255),
      border: Border.all(
        width: 1,
        color: Colors.black,
      ),
      borderRadius: BorderRadius.all(Radius.circular(10)),
    );
  }

}


Solution

  • Here is a different approach to do it simply.

    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
      late final TabController controller;
    
      @override
      void initState() {
        super.initState();
        controller = TabController(length: 3, vsync: this);
      }
    
      BoxDecoration buildTabBarStyle() {
        return BoxDecoration(
          color: Color.fromARGB(255, 230, 248, 255),
          border: Border.all(
            width: 1,
            color: Colors.black,
          ),
          borderRadius: BorderRadius.all(Radius.circular(10)),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
            body: NestedScrollView(
              headerSliverBuilder: (context, innerBoxIsScrolled) => [
                SliverAppBar(
                  snap: true,
                  floating: true,
                  pinned: false,
                  toolbarHeight: 80,
    
                  title: Text("Home"),
                  // title: Search(),
                  centerTitle: true,
                  bottom: PreferredSize(
                    preferredSize: Size(0.0, 48.0),
                    child: Container(
                      decoration: buildTabBarStyle(),
                      alignment: Alignment.center,
                      width: double.infinity,
                      child: TabBar(
                        controller: controller,
                        isScrollable: true,
                        labelColor: Colors.green,
                        unselectedLabelColor: Colors.grey,
                        labelStyle:
                            TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0),
                        tabs: [
                          Tab(text: "1"),
                          Tab(text: "2"),
                          Tab(text: "3"),
                        ],
                      ),
                    ),
                  ),
                ),
              ],
              body: TabBarView(
                controller: controller,
                children: [
                  ...List.generate(
                    3,
                    (t) => Container(
                      color: Colors.red,
                      child: ListView.builder(
                        itemCount: 50,
                        itemBuilder: (context, index) {
                          return Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: Container(
                              height: 60,
                              color: Colors.blue,
                              child: Center(
                                child: Text("tab $t  index $index"),
                              ),
                            ),
                          );
                        },
                      ),
                    ),
                  ).toList(),
                ],
              ),
            ),
          ),
        );
      }
    }