Skip to content

Combining NestedScrollView with TabBarView has problems. #81619

Closed

Description

@smakarov

Hi! My post is related to this one.
So
1)If I use AutomaticKeepAliveMixin to keep the inner scrollers (and each of their children built by builder: methods), they are kept alive and when changing between the tabs, there are no lags in animation.
However, any scrolling of one of the inner scrollers also affects others and the individual scroll positions are not obeyed. Even though I use different PageStorageKey() for all of the scrollers, if AutomaticKeepAliveMixin is applied, scroll positions are problematic.
You can see this behavior here
2)If I don't apply AutomaticKeepAliveMixin to the inner scrollers, scroll positions have no issues and work as expected. However, this time, because each of the inner scrollers (and their respective children) is completely rebuilt whenever a tab change occurs.
When there are relatively fewer items inside inner scrollers (like 20-30 items per tab), TabBarView tab change animations are fluid and no issues. However, when after a user scrolls and fills lots of children into inner scrollers, then tab change becomes very laggy. This happens because the inner scrollers build from scratch.
You can see this behavior here

flutter doctor -v
[√] Flutter (Channel stable, 2.0.5, on Microsoft Windows [Version 10.0.18363.1500], locale en-US)
    • Flutter version 2.0.5 at C:\src\flutter
    • Framework revision adc687823a (2 weeks ago), 2021-04-16 09:40:20 -0700
    • Engine revision b09f014e96
    • Dart version 2.12.3

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.0-rc2)
    • Android SDK at C:\Users\Teq\AppData\Local\Android\sdk
    • Platform android-30, build-tools 30.0.0-rc2
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

[√] Android Studio (version 4.1.0)
    • Android Studio at C:\Program Files\Android\Android Studio
    • 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-b01)

[√] IntelliJ IDEA Ultimate Edition (version 2020.1)
    • IntelliJ at C:\Program Files\JetBrains\IntelliJ IDEA 2020.1
    • Flutter plugin version 45.1.3
    • Dart plugin version 201.7223.43

[√] VS Code, 64-bit edition (version 1.55.2)
    • VS Code at C:\Program Files\Microsoft VS Code
    • Flutter extension version 3.21.0

[√] Connected device (4 available)
    • Android SDK built for x86 64 (mobile) • emulator-5554 • android-x64    • Android 10 (API 29) (emulator)
    • sdk gphone x86 64 arm64 (mobile)      • emulator-5560 • android-x64    • Android 11 (API 30) (emulator)
    • Chrome (web)                          • chrome        • web-javascript • Google Chrome 90.0.4430.85
    • Edge (web)                            • edge          • web-javascript • Microsoft Edge 89.0.774.77

• No issues found!

Activity

  1. TahaTesser commented on May 3, 2021

    @TahaTesser
    Contributor

    Hi @smakarov
    Thanks for the detailed explanation of the issue and code samples, I can confirm both problems

    1. Inner scroller scroll Issue

    code sample
    import 'dart:math';
    
    import 'package:flutter/material.dart';
    import 'package:flutter/widgets.dart';
    import 'package:flutter/foundation.dart';
    
    void main() {
       runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Test3(),
        );
      }
    }
    
    
    @immutable
    class Test3 extends StatelessWidget {
      Test3({
        Key key,
      }) : super(key: key);
    
      Widget build(BuildContext context) {
        final List<String> _tabs = ['Tab 1', 'Tab 2'];
        return DefaultTabController(
          length: _tabs.length, // This is the number of tabs.
          child: Scaffold(
            body: NestedScrollView(
              headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
                // These are the slivers that show up in the "outer" scroll view.
                return <Widget>[
                  SliverOverlapAbsorber(
                    // This widget takes the overlapping behavior of the SliverAppBar,
                    // and redirects it to the SliverOverlapInjector below. If it is
                    // missing, then it is possible for the nested "inner" scroll view
                    // below to end up under the SliverAppBar even when the inner
                    // scroll view thinks it has not been scrolled.
                    // This is not necessary if the "headerSliverBuilder" only builds
                    // widgets that do not overlap the next sliver.
                    handle:
                        NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                    sliver: SliverAppBar(
                      title:
                          const Text('Books'), // This is the title in the app bar.
                      floating: true,
                      pinned: true,
                      snap: false,
                      primary: true,
                      expandedHeight: 110,
                      // The "forceElevated" property causes the SliverAppBar to show
                      // a shadow. The "innerBoxIsScrolled" parameter is true when the
                      // inner scroll view is scrolled beyond its "zero" point, i.e.
                      // when it appears to be scrolled below the SliverAppBar.
                      // Without this, there are cases where the shadow would appear
                      // or not appear inappropriately, because the SliverAppBar is
                      // not actually aware of the precise position of the inner
                      // scroll views.
                      forceElevated: innerBoxIsScrolled,
                      bottom: TabBar(
                        // These are the widgets to put in each tab in the tab bar.
                        tabs: _tabs.map((String name) => Tab(text: name)).toList(),
                      ),
                    ),
                  ),
                ];
              },
              body: TabBarView(
                // These are the contents of the tab views, below the tabs.
                children: _tabs.map((String name) {
                  return SafeArea(
                    top: false,
                    bottom: false,
                    child: Builder(
                      // This Builder is needed to provide a BuildContext that is
                      // "inside" the NestedScrollView, so that
                      // sliverOverlapAbsorberHandleFor() can find the
                      // NestedScrollView.
                      builder: (BuildContext context) {
                        return CustomScrollView(
                          // The "controller" and "primary" members should be left
                          // unset, so that the NestedScrollView can control this
                          // inner scroll view.
                          // If the "controller" property is set, then this scroll
                          // view will not be associated with the NestedScrollView.
                          // The PageStorageKey should be unique to this ScrollView;
                          // it allows the list to remember its scroll position when
                          // the tab view is not on the screen.
                          key: PageStorageKey<String>(name),
                          slivers: <Widget>[
                            SliverOverlapInjector(
                              // This is the flip side of the SliverOverlapAbsorber
                              // above.
                              handle:
                                  NestedScrollView.sliverOverlapAbsorberHandleFor(
                                      context),
                            ),
                            SliverPadding(
                              padding: const EdgeInsets.all(8.0),
                              // In this example, the inner scroll view has
                              // fixed-height list items, hence the use of
                              // SliverFixedExtentList. However, one could use any
                              // sliver widget here, e.g. SliverList or SliverGrid.
                              sliver: ListTest3(),
                              /*sliver: SliverFixedExtentList(
                                // The items in this example are fixed to 48 pixels
                                // high. This matches the Material Design spec for
                                // ListTile widgets.
                                itemExtent: 48.0,
                                delegate: SliverChildBuilderDelegate(
                                  (BuildContext context, int index) {
                                    // This builder is called for each child.
                                    // In this example, we just number each list item.
                                    return ListTile(
                                      title: Text('Item $index'),
                                    );
                                  },
                                  // The childCount of the SliverChildBuilderDelegate
                                  // specifies how many children this inner list
                                  // has. In this example, each tab has a list of
                                  // exactly 30 items, but this is arbitrary.
                                  childCount: 30,
                                ),
                              ),*/
                            ),
                          ],
                        );
                      },
                    ),
                  );
                }).toList(),
              ),
            ),
          ),
        );
      }
    }
    
    @immutable
    class ListTest3 extends StatefulWidget {
      const ListTest3({
        Key key,
      }) : super(key: key);
    
      @override
      State<ListTest3> createState() => _ListTest3State();
    }
    
    class _ListTest3State extends State<ListTest3>
      with AutomaticKeepAliveClientMixin<ListTest3> {
      @override
      bool get wantKeepAlive => true;
    
      @override
      void initState() {
        super.initState();
        // Первичная инициализация виджета
      }
    
      @override
      void didUpdateWidget(ListTest3 oldWidget) {
        super.didUpdateWidget(oldWidget);
        // Конфигурация виджета изменилась
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        // Изменилась конфигурация InheritedWidget'ов
        // Также вызывается после initState, но до build'а
      }
    
      @override
      void dispose() {
        // Перманетное удаление стейта из дерева
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        super.build(context);
        return SliverFixedExtentList(
          // The items in this example are fixed to 48 pixels
          // high. This matches the Material Design spec for
          // ListTile widgets.
          itemExtent: 48.0,
          delegate: SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              // This builder is called for each child.
              // In this example, we just number each list item.
              return ListTile(
                tileColor: Color((Random().nextDouble() * 0xFFFFFF).toInt() << 0)
                    .withOpacity(1.0),
                title: Text('Item $index'),
              );
            },
            // The childCount of the SliverChildBuilderDelegate
            // specifies how many children this inner list
            // has. In this example, each tab has a list of
            // exactly 30 items, but this is arbitrary.
            childCount: 30,
          ),
        );
      }
    }
    
    

    Preview

    ezgif com-gif-maker (1)

    1. Inner scroller rebuild Issue

    code sample
    import 'dart:math';
    
    import 'package:flutter/material.dart';
    import 'package:flutter/widgets.dart';
    import 'package:flutter/foundation.dart';
    
    void main() {
       runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Test3(),
        );
      }
    }
    
    
    @immutable
    class Test3 extends StatelessWidget {
      Test3({
        Key key,
      }) : super(key: key);
    
      Widget build(BuildContext context) {
        final List<String> _tabs = ['Tab 1', 'Tab 2'];
        return DefaultTabController(
          length: _tabs.length, // This is the number of tabs.
          child: Scaffold(
            body: NestedScrollView(
              headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
                // These are the slivers that show up in the "outer" scroll view.
                return <Widget>[
                  SliverOverlapAbsorber(
                    // This widget takes the overlapping behavior of the SliverAppBar,
                    // and redirects it to the SliverOverlapInjector below. If it is
                    // missing, then it is possible for the nested "inner" scroll view
                    // below to end up under the SliverAppBar even when the inner
                    // scroll view thinks it has not been scrolled.
                    // This is not necessary if the "headerSliverBuilder" only builds
                    // widgets that do not overlap the next sliver.
                    handle:
                        NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                    sliver: SliverAppBar(
                      title:
                          const Text('Books'), // This is the title in the app bar.
                      floating: true,
                      pinned: true,
                      snap: false,
                      primary: true,
                      expandedHeight: 110,
                      // The "forceElevated" property causes the SliverAppBar to show
                      // a shadow. The "innerBoxIsScrolled" parameter is true when the
                      // inner scroll view is scrolled beyond its "zero" point, i.e.
                      // when it appears to be scrolled below the SliverAppBar.
                      // Without this, there are cases where the shadow would appear
                      // or not appear inappropriately, because the SliverAppBar is
                      // not actually aware of the precise position of the inner
                      // scroll views.
                      forceElevated: innerBoxIsScrolled,
                      bottom: TabBar(
                        // These are the widgets to put in each tab in the tab bar.
                        tabs: _tabs.map((String name) => Tab(text: name)).toList(),
                      ),
                    ),
                  ),
                ];
              },
              body: TabBarView(
                // These are the contents of the tab views, below the tabs.
                children: _tabs.map((String name) {
                  return SafeArea(
                    top: false,
                    bottom: false,
                    child: Builder(
                      // This Builder is needed to provide a BuildContext that is
                      // "inside" the NestedScrollView, so that
                      // sliverOverlapAbsorberHandleFor() can find the
                      // NestedScrollView.
                      builder: (BuildContext context) {
                        return CustomScrollView(
                          // The "controller" and "primary" members should be left
                          // unset, so that the NestedScrollView can control this
                          // inner scroll view.
                          // If the "controller" property is set, then this scroll
                          // view will not be associated with the NestedScrollView.
                          // The PageStorageKey should be unique to this ScrollView;
                          // it allows the list to remember its scroll position when
                          // the tab view is not on the screen.
                          key: PageStorageKey<String>(name),
                          slivers: <Widget>[
                            SliverOverlapInjector(
                              // This is the flip side of the SliverOverlapAbsorber
                              // above.
                              handle:
                                  NestedScrollView.sliverOverlapAbsorberHandleFor(
                                      context),
                            ),
                            SliverPadding(
                              padding: const EdgeInsets.all(8.0),
                              // In this example, the inner scroll view has
                              // fixed-height list items, hence the use of
                              // SliverFixedExtentList. However, one could use any
                              // sliver widget here, e.g. SliverList or SliverGrid.
                              sliver: ListTest3(),
                              /*sliver: SliverFixedExtentList(
                                // The items in this example are fixed to 48 pixels
                                // high. This matches the Material Design spec for
                                // ListTile widgets.
                                itemExtent: 48.0,
                                delegate: SliverChildBuilderDelegate(
                                  (BuildContext context, int index) {
                                    // This builder is called for each child.
                                    // In this example, we just number each list item.
                                    return ListTile(
                                      title: Text('Item $index'),
                                    );
                                  },
                                  // The childCount of the SliverChildBuilderDelegate
                                  // specifies how many children this inner list
                                  // has. In this example, each tab has a list of
                                  // exactly 30 items, but this is arbitrary.
                                  childCount: 30,
                                ),
                              ),*/
                            ),
                          ],
                        );
                      },
                    ),
                  );
                }).toList(),
              ),
            ),
          ),
        );
      }
    }
    
    @immutable
    class ListTest3 extends StatefulWidget {
      const ListTest3({
        Key key,
      }) : super(key: key);
    
      @override
      State<ListTest3> createState() => _ListTest3State();
    }
    
    class _ListTest3State extends State<ListTest3> {
      //with AutomaticKeepAliveClientMixin<ListTest3> {
      /*@override
      bool get wantKeepAlive => true;*/
    
      @override
      void initState() {
        super.initState();
        // Первичная инициализация виджета
      }
    
      @override
      void didUpdateWidget(ListTest3 oldWidget) {
        super.didUpdateWidget(oldWidget);
        // Конфигурация виджета изменилась
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        // Изменилась конфигурация InheritedWidget'ов
        // Также вызывается после initState, но до build'а
      }
    
      @override
      void dispose() {
        // Перманетное удаление стейта из дерева
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return SliverFixedExtentList(
          // The items in this example are fixed to 48 pixels
          // high. This matches the Material Design spec for
          // ListTile widgets.
          itemExtent: 48.0,
          delegate: SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              // This builder is called for each child.
              // In this example, we just number each list item.
              return ListTile(
                tileColor: Color((Random().nextDouble() * 0xFFFFFF).toInt() << 0)
                    .withOpacity(1.0),
                title: Text('Item $index'),
              );
            },
            // The childCount of the SliverChildBuilderDelegate
            // specifies how many children this inner list
            // has. In this example, each tab has a list of
            // exactly 30 items, but this is arbitrary.
            childCount: 30,
          ),
        );
      }
    }
    
    

    Preview

    ezgif com-gif-maker (2)

    flutter doctor -v
    [✓] Flutter (Channel stable, 2.0.6, on macOS 11.3 20E232 darwin-x64, locale en-GB)
        • Flutter version 2.0.6 at /Users/tahatesser/Code/flutter_stable
        • Framework revision 1d9032c7e1 (3 days ago), 2021-04-29 17:37:58 -0700
        • Engine revision 05e680e202
        • Dart version 2.12.3
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
        • Android SDK at /Volumes/Extreme/SDK
        • Platform android-30, build-tools 30.0.3
        • ANDROID_HOME = /Volumes/Extreme/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 /Volumes/Extreme/Xcode.app/Contents/Developer
        • Xcode 12.5, Build version 12E262
        • CocoaPods version 1.10.1
    
    [✓] 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)
    
    [✓] VS Code (version 1.55.2)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (2 available)
        • iPhone 12 (mobile) • 4D5BE4C8-BF1D-46CD-B595-FD81A7140B52 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-5
          (simulator)
        • Chrome (web)       • chrome                               • web-javascript • Google Chrome 90.0.4430.93
    
    • No issues found!
    [✓] Flutter (Channel master, 2.3.0-1.0.pre.205, on macOS 11.3 20E232 darwin-x64, locale en-GB)
        • Flutter version 2.3.0-1.0.pre.205 at /Users/tahatesser/Code/flutter_master
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 96e4b47f5d (21 hours ago), 2021-05-02 07:24:01 -0700
        • Engine revision 3e578c6e0d
        • Dart version 2.14.0 (build 2.14.0-48.0.dev)
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
        • Android SDK at /Volumes/Extreme/SDK
        • Platform android-30, build-tools 30.0.3
        • ANDROID_HOME = /Volumes/Extreme/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 /Volumes/Extreme/Xcode.app/Contents/Developer
        • Xcode 12.5, Build version 12E262
        • CocoaPods version 1.10.1
    
    [✓] 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.1)
        • IntelliJ at /Volumes/Extreme/IntelliJ IDEA CE.app
        • Flutter plugin version 55.1.5
        • Dart plugin version 211.7233
    
    [✓] VS Code (version 1.55.2)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (2 available)
        • iPhone 12 (mobile) • 4D5BE4C8-BF1D-46CD-B595-FD81A7140B52 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-5
          (simulator)
        • Chrome (web)       • chrome                               • web-javascript • Google Chrome 90.0.4430.93
    
    • No issues found!
  2. added
    p: material_uimaterial_ui package in flutter/packages
    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
    and removed
    in triagePresently being triaged by the triage team
    on May 3, 2021
  3. Patrick386 commented on Aug 4, 2021

    @Patrick386

    Flutter Web:

    Using ExpansionPanelList gives me an error.

    image

  4. aitsuki commented on Aug 13, 2021

    @aitsuki

    have any workaround?

  5. Patrick386 commented on Aug 14, 2021

    @Patrick386

    have any workaround?

    Not resolved yet.

  6. Patrick386 commented on Aug 17, 2021

    @Patrick386

    have any workaround?

    I looked into this issue a bit more today, and I fixed it.
    Simply just use a SingleChildScrollView at the root of body:

    NestedScrollView(
    ...
    body: _TabBarView()
    
    _TabBarView(
    children:[
       _detail()
    ]);
    
    _detail(
    return SingleChildScrollView( // <<-------- Add New
    chile: Column(
    children:[
      ExpansionPanelList( .... ) // << -----Expansion
    ]
    
  7. DFelten commented on Nov 8, 2021

    @DFelten

    Same issue here. With the official example code from the NestedScrollView there is a bug. Both tabs can be scrolled down individually and keep their position. However, as soon as you scroll up again, the second tab loses its position and starts at the top again.

    You can see the behavior in this video:

    Simulator.Screen.Recording.-.iPhone.13.-.2021-11-08.at.22.08.51.mp4

    Is there maybe a workaround? Currently it's working when removing floating from the SliverAppBar:

    Simulator.Screen.Recording.-.iPhone.13.-.2021-11-08.at.22.12.11.mp4
    Code from the example:
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    
    class PostListTabTest extends StatefulWidget {
      const PostListTabTest();
    
      @override
      _PostListTabState createState() => _PostListTabState();
    }
    
    class _PostListTabState extends State<PostListTabTest> {
      late final ScrollController nestedScrollView = ScrollController();
      late final ScrollController scrollController1 = ScrollController();
      late final ScrollController scrollController2 = ScrollController();
    
      @override
      Widget build(BuildContext context) {
        final List<String> _tabs = <String>['Tab 1', 'Tab 2'];
        return DefaultTabController(
          length: _tabs.length, // This is the number of tabs.
          child: Scaffold(
            body: NestedScrollView(
              floatHeaderSlivers: true,
              headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
                // These are the slivers that show up in the "outer" scroll view.
                return <Widget>[
                  SliverOverlapAbsorber(
                    // This widget takes the overlapping behavior of the SliverAppBar,
                    // and redirects it to the SliverOverlapInjector below. If it is
                    // missing, then it is possible for the nested "inner" scroll view
                    // below to end up under the SliverAppBar even when the inner
                    // scroll view thinks it has not been scrolled.
                    // This is not necessary if the "headerSliverBuilder" only builds
                    // widgets that do not overlap the next sliver.
                    handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                    sliver: SliverAppBar(
                      title: const Text('Books'), // This is the title in the app bar.
                      pinned: true,
                      floating: true,
                      // The "forceElevated" property causes the SliverAppBar to show
                      // a shadow. The "innerBoxIsScrolled" parameter is true when the
                      // inner scroll view is scrolled beyond its "zero" point, i.e.
                      // when it appears to be scrolled below the SliverAppBar.
                      // Without this, there are cases where the shadow would appear
                      // or not appear inappropriately, because the SliverAppBar is
                      // not actually aware of the precise position of the inner
                      // scroll views.
                      forceElevated: innerBoxIsScrolled,
                      bottom: TabBar(
                        unselectedLabelColor: Colors.grey,
                        labelColor: Colors.black,
                        labelStyle: const TextStyle(),
                        // These are the widgets to put in each tab in the tab bar.
                        tabs: _tabs.map((String name) => Tab(text: name)).toList(),
                      ),
                    ),
                  ),
                ];
              },
              body: TabBarView(
                // These are the contents of the tab views, below the tabs.
                children: _tabs.map((String name) {
                  return SafeArea(
                    top: false,
                    bottom: false,
                    child: Builder(
                      // This Builder is needed to provide a BuildContext that is
                      // "inside" the NestedScrollView, so that
                      // sliverOverlapAbsorberHandleFor() can find the
                      // NestedScrollView.
                      builder: (BuildContext context) {
                        return CustomScrollView(
                          // The "controller" and "primary" members should be left
                          // unset, so that the NestedScrollView can control this
                          // inner scroll view.
                          // If the "controller" property is set, then this scroll
                          // view will not be associated with the NestedScrollView.
                          // The PageStorageKey should be unique to this ScrollView;
                          // it allows the list to remember its scroll position when
                          // the tab view is not on the screen.
                          key: PageStorageKey<String>(name),
                          slivers: <Widget>[
                            SliverOverlapInjector(
                              // This is the flip side of the SliverOverlapAbsorber
                              // above.
                              handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                            ),
                            SliverPadding(
                              padding: const EdgeInsets.all(8.0),
                              // In this example, the inner scroll view has
                              // fixed-height list items, hence the use of
                              // SliverFixedExtentList. However, one could use any
                              // sliver widget here, e.g. SliverList or SliverGrid.
                              sliver: SliverFixedExtentList(
                                // The items in this example are fixed to 48 pixels
                                // high. This matches the Material Design spec for
                                // ListTile widgets.
                                itemExtent: 48.0,
                                delegate: SliverChildBuilderDelegate(
                                  (BuildContext context, int index) {
                                    // This builder is called for each child.
                                    // In this example, we just number each list item.
                                    return ListTile(
                                      title: Text('Item $index'),
                                    );
                                  },
                                  // The childCount of the SliverChildBuilderDelegate
                                  // specifies how many children this inner list
                                  // has. In this example, each tab has a list of
                                  // exactly 30 items, but this is arbitrary.
                                  childCount: 30,
                                ),
                              ),
                            ),
                          ],
                        );
                      },
                    ),
                  );
                }).toList(),
              ),
            ),
          ),
        );
      }
    }
    
  8. smakarov commented on Nov 9, 2021

    @smakarov
    Author

    @DFelten I'm using extended_nested_scroll_view package for fix.

  9. 6 remaining items

  10. techouse commented on Sep 13, 2022

    @techouse
    Contributor

    For anyone still looking for an answer, check out extended_nested_scroll_view. It fixed all my problems.

  11. akbulutdora commented on Sep 14, 2022

    @akbulutdora

    For anyone still looking for an answer, check out extended_nested_scroll_view. It fixed all my problems.

    How did you handle infinite scroll, pull-to-refresh, and other scrolling actions (that require ScrollController)? Flutter candies seem to have their own packages but I cannot combine it with my state management approaches.

  12. techouse commented on Sep 14, 2022

    @techouse
    Contributor
  13. SaswatOfficial commented on Sep 23, 2022

    @SaswatOfficial

    For anyone still looking for an answer, check out extended_nested_scroll_view. It fixed all my problems.

    can you provide the whole code including the tab bar with extended nested scroll view? I saw the documentation but am not able to implement it.

  14. marcellocamara commented on Oct 16, 2022

    @marcellocamara

    This bug is still reproducible on Flutter 3.3.4 with Dart 2.18.2.

    I'm not using AutomaticKeepAliveClientMixin, just DefaultTabController with NestedScrollViewand TabBar with TabView on the body.

    The TabView children does not have AutomaticKeepAliveClientMixin. Just a stateless widget.

    All the pages scroll together, scrolling only one. And sometimes have this crash:

    ======== Exception caught by gesture ===============================================================
    The following assertion was thrown while handling a gesture:
    'package:flutter/src/widgets/scroll_position.dart': Failed assertion: line 932 pos 12: 'activity!.isScrolling': is not true.
    
    
    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      ScrollPosition.didOverscrollBy (package:flutter/src/widgets/scroll_position.dart:932:12)
    #3      _NestedScrollPosition.applyFullDragUpdate (package:flutter/src/widgets/nested_scroll_view.dart:1290:7)
    #4      _NestedScrollCoordinator.applyUserOffset (package:flutter/src/widgets/nested_scroll_view.dart:1077:31)
    #5      ScrollDragController.update (package:flutter/src/widgets/scroll_activity.dart:386:14)
    #6      ScrollableState._handleDragUpdate (package:flutter/src/widgets/scrollable.dart:671:12)
    #7      DragGestureRecognizer._checkUpdate.<anonymous closure> (package:flutter/src/gestures/monodrag.dart:483:55)
    #8      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:253:24)
    #9      DragGestureRecognizer._checkUpdate (package:flutter/src/gestures/monodrag.dart:483:7)
    

    No workarounds yet? I don't want to use any package to "avoid" it.

  15. farmerswalker commented on Feb 28, 2023

    @farmerswalker

    anything update?

  16. dam-ease commented on May 8, 2023

    @dam-ease

    I am able to reproduce this issue on the latest master and stable channels.
    Code Sample

    flutter doctor -v
    [✓] Flutter (Channel master, 3.11.0-2.0.pre.55, on macOS 13.3 22E252 darwin-arm64, locale en-NG)
        • Flutter version 3.11.0-2.0.pre.55 on channel master at /Users/dammya/fvm/versions/master
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 835b892d7f (2 days ago), 2023-05-07 01:30:22 +0530
        • Engine revision 23f730efbf
        • Dart version 3.1.0 (build 3.1.0-83.0.dev)
        • DevTools version 2.23.1
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
        • Android SDK at /Users/dammya/Library/Android/sdk
        • Platform android-33, build-tools 32.1.0-rc1
        • ANDROID_HOME = /Users/dammya/Library/Android/sdk
        • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 14.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Build 14E222b
        • CocoaPods version 1.11.3
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 2021.2)
        • 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 11.0.12+0-b1504.28-7817840)
    
    [✓] IntelliJ IDEA Community Edition (version 2022.3.2)
        • IntelliJ at /Applications/IntelliJ IDEA CE.app
        • 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
    
    [✓] VS Code (version 1.78.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.50.0
    
    [✓] VS Code (version 1.78.0)
        • VS Code at /Users/dammya/.Trash/Visual Studio Code 20.07.05.app/Contents
        • Flutter extension version 3.50.0
    
    [✓] Connected device (4 available)
        • sdk gphone arm64 (mobile)  • emulator-5554                        • android-arm64  • Android 11 (API 30) (emulator)
        • iPhone 14 Pro Max (mobile) • D7F3302D-D66F-4B89-B8CF-DBEE1CDB5449 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-16-4 (simulator)
        • macOS (desktop)            • macos                                • darwin-arm64   • macOS 13.3 22E252 darwin-arm64
        • Chrome (web)               • chrome                               • web-javascript • Google Chrome 112.0.5615.137
    
    [✓] Network resources
        • All expected network resources are available.
    
    • No issues found!
    
    [✓] Flutter (Channel stable, 3.7.12, on macOS 13.3 22E252 darwin-arm64, locale
        en-NG)
        • Flutter version 3.7.12 on channel stable at /Users/dammya/sdks/flutter
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 4d9e56e694 (3 weeks ago), 2023-04-17 21:47:46 -0400
        • Engine revision 1a65d409c7
        • Dart version 2.19.6
        • DevTools version 2.20.1
    
    [✓] Android toolchain - develop for Android devices (Android SDK version
        32.1.0-rc1)
        • Android SDK at /Users/dammya/Library/Android/sdk
        • Platform android-33, build-tools 32.1.0-rc1
        • ANDROID_HOME = /Users/dammya/Library/Android/sdk
        • Java binary at: /Applications/Android
          Studio.app/Contents/jre/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build
          11.0.12+0-b1504.28-7817840)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 14.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Build 14E222b
        • CocoaPods version 1.11.3
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 2021.2)
        • 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
          11.0.12+0-b1504.28-7817840)
    
    [✓] IntelliJ IDEA Community Edition (version 2022.3.2)
        • IntelliJ at /Applications/IntelliJ IDEA CE.app
        • 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
    
    [✓] VS Code (version 1.78.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.50.0
    
    [✓] VS Code (version 1.78.0)
        • VS Code at /Users/dammya/.Trash/Visual Studio Code 20.07.05.app/Contents
        • Flutter extension version 3.50.0
    
    [✓] Connected device (4 available)
        • sdk gphone arm64 (mobile)  • emulator-5554                        •
          android-arm64  • Android 11 (API 30) (emulator)
        • iPhone 14 Pro Max (mobile) • D7F3302D-D66F-4B89-B8CF-DBEE1CDB5449 • ios
          • com.apple.CoreSimulator.SimRuntime.iOS-16-4 (simulator)
        • macOS (desktop)            • macos                                •
          darwin-arm64   • macOS 13.3 22E252 darwin-arm64
        • Chrome (web)               • chrome                               •
          web-javascript • Google Chrome 112.0.5615.137
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    • No issues found!
    
  17. Piinks commented on May 30, 2023

    @Piinks
    Contributor

    I am going to close this to refer to #40740, which is the root issue here and has the most upvotes and information. Thank you for providing more test cases!

  18. github-actions commented on Jun 13, 2023

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  19. locked as resolved and limited conversation to collaborators on Jun 13, 2023
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 listf: scrollingViewports, list views, slivers, etc.found in release: 3.11Found to occur in 3.11found in release: 3.7Found to occur in 3.7frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: material_uimaterial_ui package in flutter/packages

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions