1

How can I switch tabs on a vertical scroll as shown above. What would be the best way to do this? And upon clicking on certain tab, control should be transfer to selected tab content. I tried to find solution on google but in vain.

Currently my code looks something like this but I know thats not it.

class SingleRestView extends StatefulWidget {
  Restaurant singleRestaurant;
  SingleRestView({@required this.singleRestaurant});
  @override
  _SingleRestViewState createState() => _SingleRestViewState();
}

class _SingleRestViewState extends State<SingleRestView>
    with SingleTickerProviderStateMixin {
  var _tabController;

  @override
  void initState() {
    super.initState();

    _tabController = TabController(length: 3, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    var tabBar = TabBar(
      controller: _tabController,
      //This property will allow your tabs to be horizontally scrollable
      isScrollable: true,
      indicatorColor: Colors.black,
      labelColor: Colors.black,
      tabs: [
       Text("Tab 1"),
       Text("Tab 2"),
       Text("Tab 2"),
      ],
    );

    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (context, isScrolled) => [
          SliverAppBar(
            expandedHeight: 300,
            pinned: true,
            flexibleSpace: FlexibleSpaceBar(
              title: Text('Title'),
              //Without this padding the title appears behind the tabs.
              //This is the whole reason why the tabBar is in a local variable, to
              //be able to use its height here.
              titlePadding: EdgeInsetsDirectional.only(
                  start: 72, bottom: tabBar.preferredSize.height + 16),
              // background:
            ),
            // I did this this way to have a white bottom bar like in your photo,
            // however, you could just pass the tabBar. The background image would
            //be behind it.
            bottom: PreferredSize(
              child: Container(
                //This will keep your tabs centered if you have not enough to fill
                //the display's width
                alignment: Alignment.center,
                width: double.infinity,
                color: Colors.white,
                child: tabBar,
              ),
              preferredSize: Size(double.infinity, tabBar.preferredSize.height),
            ),
          ),
        ],
        body: TabBarView(
          controller: _tabController,
          children: <Widget>[
           Container(height: 50,width: 100,color: Colors.red,),
           Container(height: 50,width: 100,color: Colors.blue,),
           Container(height: 50,width: 100,color: Colors.green,),
          ],
        ),
      ),
    );
  }
}

Video of what i want to achieve

James Z
  • 12,209
  • 10
  • 24
  • 44
Faizan Kamal
  • 1,732
  • 3
  • 27
  • 56
  • Hi Faizan , I have listed out some of the possible options & also attached some further references for reading, I would suggest that you should definitely check them out. Thanks for your question :) – anirudh Jun 30 '21 at 14:19

2 Answers2

1

nestedScrollController has a property called offset which will give you the current offset.

nestedScrollController.offset

But remember, screen sizes can be different for different devices. So using only offset will not help.


1. Naive Option using MediaQuery

With MediaQuery you can get to know the height of your device & then you can globally change the tab_index based on a comparison between offset & the height of the device. You can use some threshold value after which horizontal tab_index changes. Setting proper offset values might be a tough job !

double new_height = MediaQuery.of(context).size.height;

if(new_height/2<=offset)
{
//change my tab_index to 2
}

2. Using Listeners in Controller

Combining listeners & offsets

 _TabController.addListener(() {
    if (_controller.position.atEdge) {
      if (_controller.position.pixels == 0) {
        // You're at the top so tab_index=1 
      }

      //At the bottom of screen

      if(_controller.offset>=_controller.position.maxScrollExtent)
      {
         //set your tab_index
      }

      //middle of screen multiply by 0.5
      
      if(_controller.offset>=_controller.position.maxScrollExtent*0.5)
      {
         //set your tab_index
      } 
      
     //3/4 of screen multiply by 0.75
     
     if(_controller.offset>=_controller.position.maxScrollExtent*0.75)
      {
         //set your tab_index
      } 

    }

Further Reading -

  1. https://medium.com/@diegoveloper/flutter-lets-know-the-scrollcontroller-and-scrollnotification-652b2685a4ac
  2. Scroll Position Class
  3. How to check if scroll position is at top or bottom in ListView?
anirudh
  • 1,436
  • 5
  • 18
1

Maybe you can try to use this plugin => https://pub.dev/packages/vertical_scrollable_tabview/score

import 'package:vertical_scrollable_tabview/vertical_scrollable_tabview.dart';

// Required it
TabBar(
    onTap: (index) {
        VerticalScrollableTabBarStatus.setIndex(index); <- Required 
    },
)

and

VerticalScrollableTabView(
    tabController: tabController,                             <- Required TabBarController
    listItemData: data,                                       <- Required List<dynamic>
    eachItemChild: (object,index){
        return CategorySection(category: object as Category); <- Object and index
    },
    verticalScrollPosition: VerticalScrollPosition.begin,
),

I think this plugin can solve it.

邱文也
  • 11
  • 1
  • 2
  • Thanks for reply. The problem with this is that i can't move directly to the tab bar content upon clicking the single tab. Below is what I'm trying to say https://drive.google.com/file/d/1IrhQ37145ipjdzVMWaiMFmr3dhqFSrQT/view?usp=sharing – Faizan Kamal Sep 17 '21 at 19:29