0

I am facing a problem of 2 listViews scrolled at the same time :

DefaultTabController(
        length: 2,
        child: NestedScrollView
        (
        
        headerSliverBuilder: (context, innerBoxIsScrolled)
        {
          return
          [
            SliverAppBar(
              title: Text('HOME', style: Theme.of(context).textTheme.headline5,),
              floating: true,
              pinned: false,
              snap: true,
              titleSpacing: 0.1,
              

              bottom: TabBar(
                isScrollable: true,
                indicatorSize: TabBarIndicatorSize.label,
                labelPadding: EdgeInsets.symmetric(horizontal: 60),
                controller: tabController,
                
                
                tabs: 
                [
                  Tab(child: Text('Tab 1', style: Theme.of(context).textTheme.headline6,)),
                  Tab(child: Text('Tab 2', style: Theme.of(context).textTheme.headline6)),
                ],
              ),
            ),
          ];
        }, //header sliver builder
        
        body: TabBarView(
          controller: tabController,
          children: 
          [
            ListViews1, //ListView.builder
            ListViews2  //ListView.builder
          ],
        ),
      )
    ),

The tabbar controller listens to change when swiped or clicked on the tab. Indexed are being returned correctly. 0 for 1st tab and 1 for 2nd tab

tabController = TabController(length: 2, vsync: this);

tabController.addListener(_handleTabSelection);

void _handleTabSelection() {

if(!tabController.indexIsChanging)
{
  log('CALLED : ${tabController.index}');
}

}

Both classes are kept alive so that the scroll position shouldn't get lost:

class _ListView1State extends State<ListView1> with AutomaticKeepAliveClientMixin<ListView1>{
 @override
  bool get wantKeepAlive => true;


class _ListView2State extends State<ListView2> with AutomaticKeepAliveClientMixin<ListView2>{
   @override
  bool get wantKeepAlive => true;

PROBLEM : When i scroll the first list, the 2nd list also gets scrolled automatically and vice versa. What is the problem here ?

Fiaz Ali
  • 123
  • 11

1 Answers1

1

Both of them are being kept alive and both of them read the controller from the primary controller (which is created by NestedScrollView) when controller = null and primary = true (which seems the case). I dont' have a way to test it but here is what I could do:

simply create a ScrollController in each state and check if they preserve their value independently

class _ListView1State extends State<ListView1> with AutomaticKeepAliveClientMixin<ListView1>{
 late final ScrollController _scrollController;

 @override
 void initState() {
   _scrollController = ScrollController();
   super.initState();
 }

 @override
 void dispose() {
   _scrollcontroller.dispose();
   super.dispose();
 }

 @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    return .....
    //// pass the controller to the scroll widget you use
  }

}

Repeat with _ListView2State and try it, now they should preserve their own state in the scrolling too

EDIT

What about reading the tabController and switching to a scrollController when is not being displayed

class _ListView1State extends State<ListView1> with AutomaticKeepAliveClientMixin<ListView1>{
 ScrollController? _scrollController;
 double? _offset;
 TabController tabController;

 ///And repeat with index tabController.index != 1 for ListView2
 void setController() {
   if (tabController.index != 0 && scrollController == null) {
     _offset = PrimaryScrollController.of(context).controller.offset;
     scrollController = ScrollController(initialScrollOffset: _offset ?? 0);
     setState(() {});
   } else if (tabController.index == 0 && scrollController != null) {
      final primary = PrimaryScrollController.of(context).controller;
      if (_offset != null && primary.offset != _offset) primary .jumpTo(_offset);
      setState(() {
        _offset = scrollController.offset;
        scrollController = null;
      });
     }
 }

 @override
 void didChangeDependencies() {
   super.didChangeDependencies();
   final _tabController = DefaultTabController.of(context);
   if (tabController != null &&_tabController != tabController) {
     tabController.removeListener(setController);
   }
   tabController = _tabController;
   tabController.addListener(setController);

 }

 @override
 void dispose() {
   _scrollcontroller?.dispose();
   tabController.removeListener(setController);
   super.dispose();
 }

 @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    return .....
    //// pass the controller to the scroll widget you use
  }

}
EdwynZN
  • 4,895
  • 2
  • 12
  • 15
  • Will check in a moment. – Fiaz Ali Oct 08 '21 at 03:15
  • 1
    By adding scroll controllers in both classes, both listviews stopped scrolling beacuse i was using them as NeverScrollableScrollPhysics(). I removed it and now the both listviews are maintaining their position but the appbar is not working. The appbar hides automatically when we scroll downwards and re-appears when scroll upwards. This functionality seems to get lost. – Fiaz Ali Oct 08 '21 at 03:18
  • Both listViews were getting scrolled by NestedScrollView that's why i was using NeverScrollableScrollPhysics() within both listView classes. How can we tell NestedScrollView to scroll only that listview whoese TabBar is active. – Fiaz Ali Oct 08 '21 at 03:24
  • mmm it seems that it would require some trick with tabController listening in both lists and make the one that is not being displayed a NeverScrollableScrollPhysics() – EdwynZN Oct 08 '21 at 03:27
  • Both the lists are NeverScrollableScrollPhysics(). This is how the main NestedScrollView controls both of them and gets to hide and show automatically. the Tabbar listner only indicates which tab is selected bases on both click or swipe. How can we Tell NestedScrollView to scroll a specific list and not both of them. – Fiaz Ali Oct 08 '21 at 03:32
  • is there a reason you want to keep them alive and not give them a PageStorageKey to remember its position when switching tabs? – EdwynZN Oct 08 '21 at 03:46
  • Listviews have images like posts. All the data is coming from Firebase. The real dilemma here is that if i haven't kept them alive then i would never have known that both the lists are getting scrolled at the same time. The recommended way was to use AutomaticKeepAliveClientMixin and not the PageStorageKey but i am pretty sure that if i use the PageStorageKey then the result will also be same. – Fiaz Ali Oct 08 '21 at 03:50
  • I would recommend to try with PageStorageKey before testing my other answer, I think PageStorageKey fit what you want – EdwynZN Oct 08 '21 at 04:08
  • will check it and get back to you. – Fiaz Ali Oct 08 '21 at 04:10
  • i tested it with PageStorageKey. Both listviews hold their respective positions but in some cases they do lost their positions. – Fiaz Ali Oct 08 '21 at 05:56
  • Does these values needs to come from NestedScrollView : ScrollController? _scrollController; double? _offset; TabController tabController. I mean from the above class who is handling both listViews. – Fiaz Ali Oct 08 '21 at 06:14
  • Nope the above code isn't working. The scrolling positions of both lists are still changing with each other. – Fiaz Ali Oct 08 '21 at 06:25
  • It seems like i am not the only one stuck with this issue. Found the solution [Update - Sliver App Bar Expanded](https://stackoverflow.com/questions/55187332/flutter-tabbar-and-sliverappbar-that-hides-when-you-scroll-down). There is still some issue when 1 listview gets to the top then the other listview gets reset also. Otherwise the solution works perfectly. Thanks [Krupesh](https://stackoverflow.com/users/7475099/krupesh-anadkat) for the best possible answer. – Fiaz Ali Oct 08 '21 at 13:30
  • 1
    Also, thanks for all your help [EdwynZN](https://stackoverflow.com/users/3547212/edwynzn). Its been a long day. Glad we found a solution – Fiaz Ali Oct 08 '21 at 13:31