Skip to content

PrimaryScrollController attached to multiple scroll views in TabBarView's keep-alive tabs #81152

Open

Description

@narumi147

Recently, scroll views will auto generate a scrollbar on desktop. (?) In TabBarView, if we set every tab(with scrollable child like listview) keep-alive, then the auto generated scrollbar controller will attached to every tab. Then it will raise ScrollController attached to multiple scroll views. if we drag scrollbar after switching tab once.

Steps to Reproduce:

Run example code on desktop(windows/macOS):

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        bottom: TabBar(
          tabs: [Tab(text: 'TAB A'), Tab(text: 'TAB B')],
          controller: _tabController,
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          KeepAliveBuilder(builder: (ctx) => ChildPage(name: 'Tab A')),
          KeepAliveBuilder(builder: (ctx) => ChildPage(name: 'Tab B')),
        ],
      ),
    );
  }
}

class ChildPage extends StatelessWidget {
  final String name;

  const ChildPage({Key? key, required this.name}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: List.generate(
        20,
        (index) => ListTile(
          title: Text('$name: $index'),
        ),
      ),
    );
  }
}

class KeepAliveBuilder extends StatefulWidget {
  final WidgetBuilder builder;
  final bool wantKeepAlive;

  const KeepAliveBuilder(
      {Key? key, required this.builder, this.wantKeepAlive = true})
      : super(key: key);

  @override
  _KeepAliveBuilderState createState() => _KeepAliveBuilderState(wantKeepAlive);
}

class _KeepAliveBuilderState extends State<KeepAliveBuilder>
    with AutomaticKeepAliveClientMixin {
  bool _wantKeepAlive;

  _KeepAliveBuilderState(this._wantKeepAlive);

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return widget.builder(context);
  }

  @override
  bool get wantKeepAlive => _wantKeepAlive;
}

Expected results:

Actual results:

Logs

error

======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
ScrollController attached to multiple scroll views.
'package:flutter/src/widgets/scroll_controller.dart':
Failed assertion: line 109 pos 12: '_positions.length == 1'


Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.md

When the exception was thrown, this was the stack: 
#2      ScrollController.position (package:flutter/src/widgets/scroll_controller.dart:109:12)
#3      RawScrollbarState.getScrollbarDirection (package:flutter/src/widgets/scrollbar.dart:1080:34)
#4      RawScrollbarState.handleThumbPressStart (package:flutter/src/widgets/scrollbar.dart:1103:29)
#5      _MaterialScrollbarState.handleThumbPressStart (package:flutter/src/material/scrollbar.dart:388:11)
#6      RawScrollbarState._gestures.<anonymous closure>.<anonymous closure> (package:flutter/src/widgets/scrollbar.dart:1239:74)
...
Handler: "onLongPressStart"
Recognizer: _ThumbPressGestureRecognizer#0c12c
  debugOwner: _MaterialScrollbarState#6eb61(tickers: tracking 2 tickers)
  state: possible
====================================================================================================

flutter doctor -v

[✓] Flutter (Channel master, 2.2.0-11.0.pre.274, on macOS 11.2.2 20D80 darwin-x64, locale en-CN)
    • Flutter version 2.2.0-11.0.pre.274 at /Users/narumi/Library/flutter-master
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision d1f73a1065 (33 hours ago), 2021-04-24 00:44:02 -0400
    • Engine revision eba9efd1c3
    • Dart version 2.14.0 (build 2.14.0-37.0.dev)
    • Pub download mirror https://pub.flutter-io.cn
    • Flutter download mirror https://storage.flutter-io.cn

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
    • Android SDK at /Users/narumi/Library/Android/sdk
    • Platform android-30, build-tools 30.0.2
    • Java binary at: /Users/narumi/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.6953283/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.4, Build version 12D4e
    • CocoaPods version 1.10.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio
    • Android Studio at /Users/narumi/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-1/202.7094744/Android Studio 4.2
      Preview.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6916264)

[✓] Android Studio (version 4.1)
    • Android Studio at /Users/narumi/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.6953283/Android
      Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)

[✓] VS Code (version 1.55.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.21.0

[!] Proxy Configuration
    • HTTP_PROXY is set
    ! NO_PROXY is not set

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-x64     • macOS 11.2.2 20D80 darwin-x64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 90.0.4430.85

Activity

  1. pedromassangocode commented on Apr 26, 2021

    @pedromassangocode

    Hi @narumishi
    Can you please provide the steps to reproduce this issue? I cannot reproduce this on latest master/stable channel.
    Thank you

  2. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Apr 26, 2021
  3. narumi147 commented on Apr 26, 2021

    @narumi147
    Author

    @pedromassango please use the example code and build on desktop(macOS/windows tested). It seems scrollbar is not auto implemented on mobile.

  4. pedromassangocode commented on Apr 26, 2021

    @pedromassangocode

    Hi @narumishi
    I just build on MacOS desktop but I was unable to reproduce this issue (I got no crash). Tested on master and stable channel.

  5. narumi147 commented on Apr 26, 2021

    @narumi147
    Author

    @pedromassangocode
    So strange, I have tested on both macOS and windows, debug/release mode, master channel. And they all raise such error.

    Please check the detail steps:

    1. Run with example code
    2. There are two tabs, every tab is a ListView with an auto-generated scrollbar which will be visible when scrolling.
    3. Don't switch tab. In Tab A, drag scrollbar, it works.
    4. Switch to Tab B, drag scrollbar, error raised.
    5. Back to Tab A, drag again, error raised.
  6. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Apr 26, 2021
  7. pedromassangocode commented on Apr 27, 2021

    @pedromassangocode

    Thanks.

    I was able to reproduce the issue following the steps in the comment above.

    logs
    ======== Exception caught by gesture ===============================================================
    The following assertion was thrown while handling a gesture:
    ScrollController attached to multiple scroll views.
    'package:flutter/src/widgets/scroll_controller.dart':
    Failed assertion: line 109 pos 12: '_positions.length == 1'
    
    
    Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
    In either case, please report this assertion by filing a bug on GitHub:
      https://github.com/flutter/flutter/issues/new?template=2_bug.md
    
    When the exception was thrown, this was the stack: 
    #2      ScrollController.position (package:flutter/src/widgets/scroll_controller.dart:109:12)
    #3      RawScrollbarState.getScrollbarDirection (package:flutter/src/widgets/scrollbar.dart:1080:34)
    #4      RawScrollbarState.handleThumbPressStart (package:flutter/src/widgets/scrollbar.dart:1103:29)
    #5      _MaterialScrollbarState.handleThumbPressStart (package:flutter/src/material/scrollbar.dart:388:11)
    #6      RawScrollbarState._gestures.<anonymous closure>.<anonymous closure> (package:flutter/src/widgets/scrollbar.dart:1239:74)
    ...
    Handler: "onLongPressStart"
    Recognizer: _ThumbPressGestureRecognizer#9cbee
      debugOwner: _MaterialScrollbarState#8e7ff(tickers: tracking 2 tickers)
      state: possible
    ====================================================================================================
    flutter doctor -v
    [✓] Flutter (Channel master, 2.3.0-1.0.pre.101, on macOS 11.2.3 20D91 darwin-x64, locale en-AO)
        • Flutter version 2.3.0-1.0.pre.101 at /Users/pedromassango/Code/flutter_master
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision ff2dde2c7f (68 minutes ago), 2021-04-26 22:35:45 -0700
        • Engine revision afd9c397a5
        • Dart version 2.14.0 (build 2.14.0-43.0.dev)
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
        • Android SDK at /Users/pedromassango/Library/Android/sdk
        • Platform android-30, build-tools 30.0.2
        • ANDROID_HOME = /Users/pedromassango/Library/Android/sdk
        • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
        • All Android licenses accepted.
    
    [!] Xcode - develop for iOS and macOS
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Xcode 12.4, Build version 12D4e
        ! CocoaPods 1.9.3 out of date (1.10.0 is recommended).
            CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
            Without CocoaPods, plugins will not work on iOS or macOS.
            For more info, see https://flutter.dev/platform-plugins
          To upgrade see https://guides.cocoapods.org/using/getting-started.html#installation for instructions.
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 4.1)
        • Android Studio at /Applications/Android Studio.app/Contents
        • Flutter plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/9212-flutter
        • Dart plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/6351-dart
        • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    
    [✓] IntelliJ IDEA Community Edition (version 2021.1)
        • IntelliJ at /Applications/IntelliJ IDEA CE.app
        • Flutter plugin version 55.1.5
        • Dart plugin version 211.7179
    
    [✓] VS Code (version 1.55.2)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.16.0
    
    [✓] Connected device (2 available)
        • macOS (desktop) • macos  • darwin-x64     • macOS 11.2.3 20D91 darwin-x64
        • Chrome (web)    • chrome • web-javascript • Google Chrome 90.0.4430.85
    
    ! Doctor found issues in 1 category.
  8. added
    f: scrollingViewports, list views, slivers, etc.
    frameworkflutter/packages/flutter repository. See also f: labels.
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    c: crashStack traces logged to the console
    and removed
    in triagePresently being triaged by the triage team
    on Apr 27, 2021
  9. goderbauer commented on Apr 28, 2021

    @goderbauer
    Member
  10. 24 remaining items

  11. AhmedLSayed9 commented on Apr 7, 2024

    @AhmedLSayed9
  12. DavidOrakpo commented on Jul 10, 2024

    @DavidOrakpo
  13. rusyaev-dk commented on Aug 2, 2024

    @rusyaev-dk
  14. qwezey commented on Oct 3, 2024

    @qwezey
  15. Piinks commented on Oct 3, 2024

    @Piinks
  16. AhmedLSayed9 commented on Mar 19, 2025

    @AhmedLSayed9
    Contributor

    I can't reproduce this anymore using the latest stable version.
    I think this is fixed by #157756 (available on flutter v3.29.0)

  17. kobi666 commented on Mar 19, 2025

    @kobi666

    same behavior is replicated on v3.29.1

  18. AhmedLSayed9 commented on Mar 19, 2025

    @AhmedLSayed9
    Contributor

    same behavior is replicated on v3.29.1

    I've testing this on Flutter v3.29.2, on both iOS & macOS (debug mode) and can't get any errors.

  19. AhmedLSayed9 commented on Mar 20, 2025

    @AhmedLSayed9
    Contributor

    Well, the error has gone but the issue of scrolling in one tab causes the other tabs to scroll still exists. We've another issue for it too #159123

  20. jasminder commented on Mar 26, 2025

    @jasminder

    There are deeper issues related to this:
    I am trying to scroll to a position. I have a NestedScrollView, with a tabbarbview. So, if I am on the first tab. and scroll it using an action. It works on that tab. However, if I change the tabbar and come back to the main tab and then try to scroll again. It gives me the error that:
    ScrollController attached to multiple scroll views.

    So, I guess both tabbarviews have this controller. I am using the following code to access the scrollController inside the body of NestedScrollView:
    final innerScrollController = PrimaryScrollController.of(context);

  21. jasminder commented on Apr 2, 2025

    @jasminder

    I think that is for a specific case where you know the itemExtent for CustomScrollView. If you are using an other scrollview and you have dynamic height rows, scrolling gets messed up. It does solve the other issue, but it creates a new one as when you try to scroll back to top, it starts showing SliverToBoxAdapter (header) event before inner scroll view is all the way to top. I have a very specific use case, where on the first tab, I have various widgets of different heights, then a reorderablelistview which further has different height rows. If I use CustomScrollView, I get error that SliverOverlapInjector has found no absorbed extent to inject.

  22. surajgju commented on Aug 11, 2026

    @surajgju

    Hi @narumi147 and Flutter team 👋

    I have identified the root cause of this issue and developed a fix with full test coverage.

    Root Cause

    When TabBarView children use AutomaticKeepAliveClientMixin, inactive tabs remain mounted in the element tree. Previously, TabBarView passed down the ancestor PrimaryScrollController to all tabs without scoping. As a result, scroll views in both active and kept-alive inactive tabs attached to the ancestor PrimaryScrollController simultaneously. When any interaction accessed PrimaryScrollController.position (such as desktop scrollbar dragging or iOS status-bar tap), an assertion error was thrown: ScrollController attached to multiple scroll views.

    Solution

    Scope PrimaryScrollController inside _TabBarViewState._updateChildren():

    • Active tab (tabIndex == _currentIndex): Inherits and forwards the ancestor PrimaryScrollController.
    • Inactive tabs (tabIndex != _currentIndex): Wrapped in PrimaryScrollController.none(child: child).

    Wrapping both active and inactive tabs in PrimaryScrollController widgets keeps the widget tree depth (KeyedSubtree -> Semantics -> PrimaryScrollController -> child) identical across active and inactive states. Switching tabs updates the PrimaryScrollController properties in place without unmounting or disposing kept-alive tab states.

    PR is being submitted shortly!

  23. surajgju commented on Aug 11, 2026

    @surajgju

    similiar open issues those can be fixed with this pr
    #148317
    #89864
    #159123

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: error messageError messages from the Flutter frameworkc: crashStack traces logged to the consoled: api docsIssues with https://api.flutter.dev/d: stackoverflowGood question for Stack Overflowf: scrollingViewports, list views, slivers, etc.found in release: 2.2Found to occur in 2.2frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions