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
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()
,