How to reset scrollstate everytime I switch Tabs?

112 Views Asked by At

enter image description here

Whenever, I switch from About to Recs or References tab my scroll position in the tab is maintained across all tabs. I want it to reset everytime I switch tabs. i.e Scrolling should start from top and not where I left in my previous tab before switching.

Here is my code:

// ignore_for_file: prefer_const_constructors


import 'package:flutter/material.dart';

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

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

class _JobDetailsState extends State<JobDetails>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;


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

  }

  @override
  void dispose() {
    _tabController.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (context, isScrolled) {
          return [
            SliverPersistentHeader(
              pinned: true,
              delegate: MyHeaderDelegate(),
            ),
            
            // SliverPadding(
            //   padding: EdgeInsets.symmetric(vertical: 26, horizontal: 18),
            SliverPadding(
              padding: EdgeInsets.symmetric(horizontal: 18),
              sliver: SliverAppBar(
                shape: ContinuousRectangleBorder(
                  borderRadius: BorderRadius.all(Radius.circular(20)),
                ),
                automaticallyImplyLeading: false,
                toolbarHeight: kTextTabBarHeight,
                primary: false,
                backgroundColor: Colors.grey[200],
                pinned: true,
                titleSpacing: 0,
                title: TabBar(
                  indicator: BoxDecoration(
                      border: Border.all(
                        color: AppConstants.primaryColor,
                      ),
                      borderRadius: BorderRadius.circular(7),
                      color: AppConstants.primaryColor),
                  unselectedLabelColor: const Color(0x66181D1A),
                  splashBorderRadius: BorderRadius.circular(7),
                  controller: _tabController,
                  //labelPadding: EdgeInsets.only(bottom: 20),
                  tabs: const [
                    Tab(
                      child: Text('About'),
                    ),
                    Tab(text: 'Recs'),
                    Tab(text: 'References'),
                  ],
                ),
              ),
            ),
          ];
        },
        body: TabBarView(
          controller: _tabController,
          children: [
            SingleChildScrollView(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  // Content for Tab 1
                  Padding(
                    padding: EdgeInsets.all(18.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        
                        Text('Tab1')
                        
                       
                      ],
                    ),
                  ),
                ],
              ),
            ),
            SingleChildScrollView(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: const [
                  // Content for Tab 2
                  Padding(
                    padding: EdgeInsets.all(18.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        SizedBox(height: 12,),
                        Text('Tab 2 Content'),
                        // Add your content for Tab 2 here
                      ],
                    ),
                  ),
                ],
              ),
            ),
             SingleChildScrollView(
               child: Column(
                 crossAxisAlignment: CrossAxisAlignment.start,
                 children: [
                   // Content for Tab 3
                   Padding(
                     padding: EdgeInsets.all(18.0),
                     child: Column(
                       crossAxisAlignment: CrossAxisAlignment.start,
                       children: [
                        SizedBox(height: 12,),
                        Text('Tab3')
                         // Add your content for Tab 3 here
                       ],
                     ),
                   ),
                 ],
               ),
             ),
          ],
        ),
      ),
    );
  }
}

class MyHeaderDelegate extends SliverPersistentHeaderDelegate {
  //delegate implementation
}

Tried using a scroll controller and resetting the state for every page change by adding a custom function after init, but did not work

1

There are 1 best solutions below

1
Texv On BEST ANSWER

The issue with your code is that all your TabBarView children are within the same class of JobDetails, this causes all of the tabs to load during build time even when not selected.

This is not the best practice because unnecessary code is executing even when the tab is not selected, and therefore your app could be slower during build time and can create unnecessary read/write to your database if you have any. Also there is no separate key that will reset the scroll of the widgets.

It would be better practice to load the TabBarView children only and only if the tab is selected by doing this:

body: TabBarView(
          controller: _tabController,
          children: [
            Widget1
            Widget2
            Widget3
          ]),

class Widget1 extends..... return SingleChildSrollView(...)

This would cause your scrolls to reset when switching Tabs because the children is being re-build (code executes) only after the specific tab is selected and the way to save the scroll position would be by using key: const PageStorageKey<String>('key') in the scrollable widget

However if you're still insisting on doing it your way, then you need to add a ScrollController and a Listener to listen for tab changes.

ScrollController _scrollController = ScrollController();

@override
  void initState() {
    _tabController = TabController(length: 3, vsync: this);
    _tabController.addListener(_handleTabChange);
    super.initState();
  }

  @override
  void dispose() {
    _tabController.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  void _handleTabChange() {
    if (!_tabController.indexIsChanging) {
      _resetScroll();
    }
  }

  void _resetScroll() {
    _scrollController.jumpTo(0.0);
    //or use .animateTo() for a smooth scroll
  }

Now add controller: _scrollController in every SingleChildScrollView() widget.

Keep in mind that the scroll of NestedScrollView() widget could takeover the SingleChildScrollView() widget. If you are facing issues with that, you can either add controller: _scrollController to the NestedScrollView widget too or make it unscrollable by adding the parameter: physics: const NeverScrollableScrollPhysics(),