Search code examples
flutterdartflutter-layouttabview

How to scroll to next tab using TabBarView into TabBarView


I have TabBarView into TabBarView like following

//stful
late final TabController _controller1 = TabController( length: 3, vsync: this,initialIndex: 0);
 bottom:  TabBar(
  controller: _controller1,

 tabs: const [
 Tab(text: "Stful1",),
Tab(text: "Stfu2",),
Tab(text: "Stfu3",),

 ],
),

    TabBarView(
    controller: _controller1
    children: [
    Stful1()
    Stful2()
    Stful3()
   ]
   )

Now I have 3 Stfl Class and every Stful has also TabBarView with some tabs, I am going to use here Stful3 Tab to make it simple

Stful3() =>
    late final TabController _controller2 = TabController( length: 3, vsync: this,initialIndex: 0);
     bottom:  TabBar(
      controller: _controller2,
    
     tabs: const [
     Tab(text: "page1",),
    Tab(text: "page2",),
    Tab(text: "page3",),
    
     ],
    ),
    
        TabBarView(
        controller: _controller2
        children: [
        page1()
        page2()
        page3()
       ]
       )

Now lets say i am in page 1 and scrolling free with page1 and page3 but when i access to extent swip right or left where there is no tabs in current TabBarView it does not moving me to other tabs which they are in the main TabBarView .

does it possible in flutter frame work ? How can achieve this ?

EDIT

here is the full code

import 'package:flutter/material.dart';

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

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

class _MyMainTabVarViewState extends State<MyMainTabVarView> with SingleTickerProviderStateMixin{

  late final TabController controllerForMainTabVarView = TabController( length: 3, vsync: this,initialIndex: 0 );

  @override
  Widget build(BuildContext context) {
    return Scaffold(

      backgroundColor: Colors.blue,
      body: Column(
        children:  [
          Expanded(
            child: TabBarView(
              controller: controllerForMainTabVarView,
              children: const [
                MyStful1(),
                MyStful2(),
                MyStful3(),
              ],
            ),
          ),
          TabBar( // here i am use TabBar at the bottom of the screen instead of bottom Navigation Bar
            controller:controllerForMainTabVarView,
            tabs: const [
              Tab(text: "My Stful 1",),
              Tab(text: "My Stful 2",),
              Tab(text: "My Stful 3",),
            ],
          )
        ],
      ),
    );
  }
}



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

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

class _MyStful1State extends State<MyStful1> with SingleTickerProviderStateMixin{

  late final TabController controllerForMyStful1 = TabController( length: 2, vsync: this,initialIndex: 0 );

  @override
  Widget build(BuildContext context) {
    return  Scaffold(
      appBar: AppBar(
        bottom: TabBar(
          controller:controllerForMyStful1,
          tabs: const [
            Tab(text: "Page1",),
            Tab(text: "Page2",),
          ],
        ),
      ),
      body: TabBarView(
        controller: controllerForMyStful1,
        children: const [
          Center(child: Text('Page1')),
          Center(child: Text('Page2')),
        ],
      ),
    );
  }
}



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

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

class _MyStful2State extends State<MyStful2> with SingleTickerProviderStateMixin{

  late final TabController controllerForMyStful2 = TabController( length: 2, vsync: this,initialIndex: 0 );

  @override
  Widget build(BuildContext context) {
    return  Scaffold(
      appBar: AppBar(
        bottom: TabBar(
          controller:controllerForMyStful2,
          tabs: const [
            Tab(text: "Page1",),
            Tab(text: "Page2",),
          ],
        ),
      ),
      body: TabBarView(
        controller: controllerForMyStful2,
        children: const [
          Center(child: Text('Page1')),
          Center(child: Text('Page2')),
        ],
      ),
    );
  }
}


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

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

class _MyStful3State extends State<MyStful3>with SingleTickerProviderStateMixin {

  late final TabController controllerForMyStful3 = TabController( length: 2, vsync: this,initialIndex: 0 );

  @override
  Widget build(BuildContext context) {
    return  Scaffold(
      appBar: AppBar(
        bottom: TabBar(
          controller:controllerForMyStful3,
          tabs: const [
            Tab(text: "Page1",),
            Tab(text: "Page2",),
          ],
        ),
      ),
      body: TabBarView(
        controller: controllerForMyStful3,
        children: const [
          Center(child: Text('Page1')),
          Center(child: Text('Page2')),
        ],
      ),
    );
  }
}

Solution

  • This logic can be simplified using PageView, same approach can apply on other widgets like IndexedStack.

    Full snippet on dartPad.

    class MyMainTabVarView extends StatefulWidget {
      const MyMainTabVarView({Key? key}) : super(key: key);
    
      @override
      _MyMainTabVarViewState createState() => _MyMainTabVarViewState();
    }
    
    class _MyMainTabVarViewState extends State<MyMainTabVarView>
        with TickerProviderStateMixin {
      late final TabController controllerForMainTabVarView =
          TabController(length: 3, vsync: this, initialIndex: 0);
      late final TabController topTabBarController =
          TabController(length: 2, vsync: this, initialIndex: 0);
    
      late PageController pageController = PageController();
    
      onPageChange(int index) {
        debugPrint("page num $index");
        controllerForMainTabVarView.animateTo(
          index ~/ 2,
          duration: const Duration(milliseconds: 400),
          curve: Curves.ease,
        );
        topTabBarController.animateTo(
          index % 2,
          duration: const Duration(milliseconds: 400),
          curve: Curves.ease,
        );
    
        setState(() {});
      }
    
      void navigation(bool isTopController) {
        //skip 1st build
        WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
          int mainTabBarIndex = controllerForMainTabVarView.index;
          int topTabBarIndex = topTabBarController.index;
    
          /// switch to TopTabBar index=0
          if (!isTopController) {
            topTabBarIndex = 0;
            topTabBarController.index = 0;
          }
          debugPrint("main $mainTabBarIndex top $topTabBarIndex");
    
          pageController.animateToPage(
            topTabBarIndex + mainTabBarIndex * 2,
            duration: const Duration(milliseconds: 400),
            curve: Curves.ease,
          );
          setState(() {});
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.blue,
          body: Column(
            children: [
              TabBar(
                  controller: topTabBarController,
                  tabs: const [
                    Tab(text: "Page1"),
                    Tab(text: "Page2"),
                  ],
                  onTap: (_) {
                    navigation(true);
                  }),
              Expanded(
                child: Container(
                  color: Theme.of(context).scaffoldBackgroundColor,
                  child: PageView(
                    controller: pageController,
                    onPageChanged: onPageChange,
                    children: const [
                      Center(child: Text(' MyStful1 Page1')),
                      Center(child: Text(' MyStful1 Page2')),
                      Center(child: Text(' MyStful2 Page1')),
                      Center(child: Text(' MyStful2 Page2')),
                      Center(child: Text(' MyStful3 Page1')),
                      Center(child: Text(' MyStful3 Page2')),
                    ],
                  ),
                ),
              ),
              TabBar(
                // here i am use TabBar at the bottom of the screen instead of bottom Navigation Bar
                controller: controllerForMainTabVarView,
                onTap: (_) {
                  navigation(false);
                },
                tabs: const [
                  Tab(
                    text: "My Stful 1",
                  ),
                  Tab(
                    text: "My Stful 2",
                  ),
                  Tab(
                    text: "My Stful 3",
                  ),
                ],
              )
            ],
          ),
        );
      }
    }