Skip to content

NestedScrollView does not work well with floated app bars on the outside or pinned app bars on the inside #11222

Closed

Description

@nonameden

Video with issue:

I made app bar background semi transparent to see that inner scroll container scrolled under pinned part of AppBar

https://youtu.be/d5nMB8eJrdw

Code to Reproduce

class TestPage2 extends StatefulWidget {

  @override
  _TestPageState2 createState() => new _TestPageState2();
}

class _TestPageState2 extends State<TestPage2> {

  Widget _buildItem(BuildContext context, int index) {
    return new ListTile(
      title: new Text('Title $index'),
      subtitle: new Text('Subtitle $index'),
    );
  }

  Widget _buildTab(int listSize) {
    return new TabBarView(
      children: [
        new Stack(
          children: <Widget>[
            new Positioned(
              top: 0.0,
              left: 0.0,
              right: 0.0,
              child: new Material(
                elevation: 4.0,
                color: Colors.white,
                child: new Container(
                  height: 48.0,
                  child: new Center(
                      child: const Text('Here my fixed header 1')),
                ),
              ),
            ),
            new Positioned(
              top: 48.0,
              left: 0.0,
              right: 0.0,
              bottom: 0.0,
              child: new RefreshIndicator(
                  onRefresh: () {},
                  child: new ListView.builder(
                    itemBuilder: _buildItem,
                    itemCount: listSize,
                  )
              ),
            ),
          ],
        ),
        new Stack(
          children: <Widget>[
            new Positioned(
              top: 0.0,
              left: 0.0,
              right: 0.0,
              child: new Material(
                elevation: 4.0,
                color: Colors.white,
                child: new Container(
                  height: 48.0,
                  child: new Center(
                      child: const Text('Here my fixed header 2')),
                ),
              ),
            ),
            new Positioned(
              top: 48.0,
              left: 0.0,
              right: 0.0,
              bottom: 0.0,
              child: new RefreshIndicator(
                  onRefresh: () {},
                  child: new ListView.builder(
                    itemBuilder: _buildItem,
                    itemCount: 5,
                  )
              ),
            ),
          ],
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return new DefaultTabController(
      length: 2,
      child: new Scaffold(
        body: new NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return [
              new SliverAppBar(
                backgroundColor: Colors.black26,
                title: new Text('Here title'),
                pinned: true,
                floating: true,
                bottom: new TabBar(
                    tabs: [
                      new Tab(text: 'Tab1'),
                      new Tab(text: 'Tab2'),
                    ]
                ),
              ),
            ];
          },
          body: new TabBarView(
            children: [
              _buildTab(30),
              _buildTab(5),
            ],
          ),
        ),
      ),
    );
  }
}

Flutter Doctor

[✓] Flutter (on Mac OS X 10.12.5 16F73, locale en-NZ, channel master)
    • Flutter at /Users/nonameden/Documents/flutter
    • Framework revision cf96c7db4a (2 hours ago), 2017-07-13 16:19:11 -0700
    • Engine revision 431a251151
    • Tools Dart version 1.25.0-dev.4.0

[✓] Android toolchain - develop for Android devices (Android SDK 26.0.0)
    • Android SDK at /Users/nonameden/Library/Android/sdk
    • Platform android-26, build-tools 26.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_112-release-b06)

[✓] iOS toolchain - develop for iOS devices (Xcode 8.3.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 8.3.3, Build version 8E3004b
    • ios-deploy 1.9.1
    • CocoaPods version 1.2.1

[✓] Android Studio
    • Android Studio at /Applications/Android Studio 2.4 Preview.app/Contents
    • Java version OpenJDK Runtime Environment (build 1.8.0_112-release-b736)

[✓] Android Studio
    • Android Studio at /Applications/Android Studio 3.0 Preview.app/Contents
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-884-b01)

[✓] Android Studio (version 2.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Java version OpenJDK Runtime Environment (build 1.8.0_112-release-b06)

[✓] IntelliJ IDEA Ultimate Edition (version 2017.1.5)
    • Flutter plugin version 15.1
    • Dart plugin version 171.4694.29

[✓] Connected devices
    • Android SDK built for x86 • emulator-5554 • android-x86 • Android 7.0 (API 24) (emulator)

Might be a Fix

I was playing with Nested Scroll View and found that scrollExtent for outer scroll does not respect minExtent from SliverAppBar delegate so I modified RenderSliverFloatingPinnedPersistentHeader:

abstract class RenderSliverFloatingPinnedPersistentHeader extends RenderSliverFloatingPersistentHeader {
  RenderSliverFloatingPinnedPersistentHeader({
    RenderBox child,
    FloatingHeaderSnapConfiguration snapConfiguration,
  }) : super(child: child, snapConfiguration: snapConfiguration);

  @override
  double updateGeometry() {
    final double minExtent = this.maxExtent;
    final double maxExtent = this.maxExtent;
    final double paintExtent = (maxExtent - _effectiveScrollOffset);
    final double layoutExtent = (maxExtent - constraints.scrollOffset);
    geometry = new SliverGeometry(
      scrollExtent: maxExtent,
      paintExtent: paintExtent.clamp(minExtent, constraints.remainingPaintExtent),
      layoutExtent: layoutExtent.clamp(0.0, constraints.remainingPaintExtent - minExtent),
      maxPaintExtent: maxExtent,
      hasVisualOverflow: true, // Conservatively say we do have overflow to avoid complexity.
    );
    return 0.0;
  }
}

to

abstract class RenderSliverFloatingPinnedPersistentHeader extends RenderSliverFloatingPersistentHeader {
  RenderSliverFloatingPinnedPersistentHeader({
    RenderBox child,
    FloatingHeaderSnapConfiguration snapConfiguration,
  }) : super(child: child, snapConfiguration: snapConfiguration);

  @override
  double updateGeometry() {
    final double minExtent = this.minExtent;
    final double maxExtent = this.maxExtent;
    final double paintExtent = (maxExtent - _effectiveScrollOffset);
    final double layoutExtent = (maxExtent - constraints.scrollOffset);
    geometry = new SliverGeometry(
      scrollExtent: maxExtent - minExtent,
      paintExtent: paintExtent.clamp(minExtent, constraints.remainingPaintExtent),
      layoutExtent: layoutExtent.clamp(0.0, constraints.remainingPaintExtent - minExtent),
      maxPaintExtent: maxExtent,
      hasVisualOverflow: true, // Conservatively say we do have overflow to avoid complexity.
    );
    return 0.0;
  }
}

result for this on video: https://youtu.be/1y210APVEwo

Activity

  1. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    on Jul 14, 2017
  2. self-assigned this
    on Jul 14, 2017
  3. HansMuller commented on Jul 14, 2017

    @HansMuller
    Contributor

    Thanks for the test-case and suggested fix, I'm looking at this now.

  4. HansMuller commented on Jul 14, 2017

    @HansMuller
    Contributor

    I think the effect your change produces is correct. I extended it to handle pinned but not floating persistent header slivers which caused a bunch of tests to fail. I'll be looking into that, as well writing some new tests to verify the new behavior., next week.

    I put a slightly modified version of your test app here: https://gist.github.com/HansMuller/facae02ce97892eaa2f159ea50c45e61

    It just has one TabBarView, I'm guessing that you didn't mean to nest them.

  5. nonameden commented on Jul 15, 2017

    @nonameden
    Author

    Thank you @HansMuller , yep it supposed to be one TabBarView, I just made a mistake during modifing code here after pasting, sorry for confusing.

  6. changed the title [-]Can't position anything to the top of tab content when using SliverAppBar with pinned+floating mode[/-] [+]NestedScrollView does not work well with floated app bars on the outside or pinned app bars on the inside[/+] on Feb 2, 2018
  7. added this to the milestone on Feb 2, 2018
  8. Hixie commented on Feb 2, 2018

    @Hixie
    Contributor

    I looked at this also and it seems that additional work is going to be needed to make NestedScrollView really work properly with pinned app bars and so forth.

    Maybe the absorber needs to absorb more, e.g. it should probably absorb the overlap and inject it on the inside, so that pinned app bars can follow an injector correctly...

    http://junkyard.damowmow.com/589 is what I had when I stopped working on this bug.

  9. goderbauer commented on Dec 28, 2018

    @goderbauer
    Member

    Closing this one in favor of the more popular issue #17518.

  10. added a commit that references this issue on Aug 19, 2019
  11. github-actions commented on Aug 15, 2021

    @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.

  12. locked as resolved and limited conversation to collaborators on Aug 15, 2021
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

    f: scrollingViewports, list views, slivers, etc.frameworkflutter/packages/flutter repository. See also f: labels.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions