Flutter SliverAppbar in NestedScrollView header does not stretch and zoom like it would stretch in CustomScrollView

5,106

As mentioned in the comment, there is an open bug in GitHub for this.

The reason for this behavior was discussed in this thread:

The reason the stretch does not happen is because the _NestedScrollViewCoodinator currently prioritizes the inner scrollable for any overscroll from the user drag. I am thinking we can add a flag like, NestedScrollView.stretchHeaderSlivers, which will flip that to send overscroll to the outer scrollable. That part should be easy.

The _NestedScrollViewCoordinator.createOuterBallisticScrollActivity will need to be refactored since it currently assumes that the outer scrollable will never overscroll, which may or may not be as easy.

And they have mentioned that this is a huge refactoring and there is no suitable solutions yet:

I've taken a crack at this a few different times and have not found a suitable solution yet. The NestedScrollView bases much more than I though on the assumption that the outer scroll view will never overscroll. I believe this will take a larger refactoring/re-design of the widget in order to support, maybe work we can schedule for in the new year. For now, I won't be actively working on this anymore. I am un-assigning myself for now in case some one else would like to try solving this in the meantime. :)

Since I'm not able to view your screen recording, I'll just post a sample view of how the bug looks like. It was taken from the GitHub issue thread.

Sample code:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Material App',
      theme: ThemeData.dark(),
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: 2,
        child: Scaffold(
          body: NestedScrollView(
              physics: BouncingScrollPhysics(),
              headerSliverBuilder: (context, innerScrolled) => <Widget>[
                    SliverOverlapAbsorber(
                      handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
                          context),
                      sliver: SliverAppBar(
                          pinned: true,
                          stretch: true,
                          title: Text('username'),
                          expandedHeight: 325,
                          flexibleSpace: FlexibleSpaceBar(
                              stretchModes: <StretchMode>[
                                StretchMode.zoomBackground,
                                StretchMode.blurBackground,
                              ],
                              background: Image.network(
                                  'https://i.imgur.com/QCNbOAo.png',
                                  fit: BoxFit.cover)),
                          bottom: TabBar(
                              tabs: <Widget>[Text('test1'), Text('test2')])),
                    )
                  ],
              body: TabBarView(children: [
                Center(
                  child: Builder(
                    builder: (context) => CustomScrollView(
                      slivers: <Widget>[
                        SliverOverlapInjector(
                            handle:
                                NestedScrollView.sliverOverlapAbsorberHandleFor(
                                    context)),
                        SliverFixedExtentList(
                            delegate: SliverChildBuilderDelegate(
                                (_, index) => Text('not working'),
                                childCount: 100),
                            itemExtent: 25)
                      ],
                    ),
                  ),
                ),
                Center(child: Text('working'))
              ])),
        ));
  }
}

Output:

enter image description here

Share:
5,106
Hasan Ahmed
Author by

Hasan Ahmed

Updated on November 25, 2022

Comments

  • Hasan Ahmed
    Hasan Ahmed over 1 year

    The SliverAppbar will stretch and zoom if it is in a customScrollView but it does not when it is in NestedScrollView. Only the body of NestedScrollView has a BouncingScrollPhysics, how can the header of NestedScrollView also have the BouncingScrollPhysics so it can stretch and zoom the SliverAppBar.

    However, if i add scrollController in the body/inner scrollView then the header SliverAppBar stretches, but then they become two separate scroll views and the header does not scroll up when the body is scrolled. Check the Screen recording

          @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: DefaultTabController(
            length: 3,
            child: NestedScrollView(
              physics: BouncingScrollPhysics(),
              controller: mainScroller,
              headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
                return <Widget>[
                  SliverAppBar(
                    stretch: true,
                    pinned: true,
                    floating: false,
                    elevation: 0,
                    onStretchTrigger: () {
                      print('stretch');
                      return;
                    },
                    title: isFlexibleSpaceVisible ? null : Text(widget.name),
                    expandedHeight: containerHeight - kToolbarHeight,
                    flexibleSpace: FlexibleSpaceBar(
                      stretchModes: <StretchMode>[
                        StretchMode.zoomBackground,
                        StretchMode.blurBackground,
                      ],
                      collapseMode: CollapseMode.pin,
                      background: Container(
                        color: Theme.of(context).canvasColor,
                        child: Stack(
                          children: <Widget>[
                            Container(
                              height: headerImageHeight,
                              width: double.infinity,
                              decoration: BoxDecoration(
                                color: kBackgroundColor,
                                image: DecorationImage(
                                  fit: BoxFit.cover,
                                  colorFilter: ColorFilter.mode(
                                    Colors.black54,
                                    BlendMode.darken,
                                  ),
                                  image: NetworkImage(
                                      'https://picsum.photos/seed/${Random().nextInt(100)}/${MediaQuery.of(context).size.width.toInt()}'),
                                ),
                              ),
                            ),
                            Container(
                              height: headerSpace,
                              margin: EdgeInsets.only(
                                  top: headerImageHeight - bringImageUpMargin),
                              padding: EdgeInsets.only(left: 8, right: 8),
                              child: Row(
                                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                crossAxisAlignment: CrossAxisAlignment.center,
                                children: <Widget>[
                                  Container(
                                    decoration: BoxDecoration(
                                        color: Theme.of(context).canvasColor,
                                        shape: BoxShape.circle),
                                    padding: EdgeInsets.all(6),
                                    child: CircleAvatar(
                                      backgroundColor: kCardColor,
                                      backgroundImage: NetworkImage(
                                          'https://picsum.photos/seed/${Random().nextInt(100)}/200'),
                                      radius: profileImageRadius,
                                    ),
                                  ),
                                  Expanded(
                                    child: Container(
                                      margin: EdgeInsets.symmetric(
                                          horizontal: 4, vertical: 12),
                                      child: Column(
                                        mainAxisAlignment: MainAxisAlignment.end,
                                        crossAxisAlignment:
                                            CrossAxisAlignment.stretch,
                                        children: <Widget>[
                                          Text(
                                            widget.name,
                                            style: TextStyle(
                                                fontSize: 16,
                                                fontWeight: FontWeight.w700),
                                          ),
                                          SizedBox(height: 2),
                                          Wrap(
                                            alignment: WrapAlignment.spaceBetween,
                                            children: <Widget>[
                                              Container(
                                                padding: EdgeInsets.symmetric(
                                                    horizontal: 6, vertical: 2),
                                                decoration: BoxDecoration(
                                                    color: kPrimaryColor,
                                                    borderRadius:
                                                        BorderRadius.circular(4)),
                                                child: Text(
                                                  widget.skill,
                                                  style: TextStyle(
                                                      fontSize: 12,
                                                      color: Colors.white),
                                                ),
                                              ),
                                              Container(
                                                padding: EdgeInsets.symmetric(
                                                    vertical: 2, horizontal: 6),
                                                decoration: BoxDecoration(
                                                    color: kCardColor,
                                                    borderRadius:
                                                        BorderRadius.circular(4)),
                                                child: Text(
                                                  '\$${widget.rate} / ${widget.per}',
                                                  style: TextStyle(
                                                      color: kPrimaryColor,
                                                      fontSize: 12),
                                                ),
                                              ),
                                            ],
                                          ),
                                        ],
                                      ),
                                    ),
                                  )
                                ],
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                  SliverToBoxAdapter(
                    child: Container(
                      padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                      child: Column(
                        children: <Widget>[
                          Row(
                            children: <Widget>[
                              Container(
                                  width: 22,
                                  height: 18,
                                  decoration: BoxDecoration(
                                    borderRadius: BorderRadius.circular(4),
                                    color: kSecondaryColor,
                                  ),
                                  child: Icon(
                                    Icons.star,
                                    color: Colors.white,
                                    size: 14,
                                  )),
                              SizedBox(
                                width: 6,
                              ),
                              Text('Do you recommend ${widget.name} ?'),
                            ],
                          ),
                          Row(
                            children: <Widget>[
                              Expanded(
                                child: OutlineButton(
                                  highlightedBorderColor: kSecondaryColor,
                                  color: kSecondaryColor,
                                  borderSide: BorderSide(color: kSecondaryColor),
                                  textColor: kSecondaryColor,
                                  onPressed: () => null,
                                  child: Text('NO'),
                                ),
                              ),
                              SizedBox(
                                width: 8,
                              ),
                              Expanded(
                                child: RaisedButton(
                                  onPressed: () => null,
                                  child: Text('YES'),
                                ),
                              ),
                            ],
                          )
                        ],
                      ),
                    ),
                  ),
                  SliverPersistentHeader(
                    pinned: true,
                    delegate: SliverProfileTabs(),
                  )
                ];
              },
              body: ProfileTabsView(data: widget),
            ),
          ),
        );
      }
    }
    
  • Aryan Sethi
    Aryan Sethi about 2 years
    any solutions for this yet ?
  • MαπμQμαπkγVπ.0
    MαπμQμαπkγVπ.0 about 2 years