Home > OS >  How to scroll with NestedScrollView to use stack Circle Avatar over SliverAppBar?
How to scroll with NestedScrollView to use stack Circle Avatar over SliverAppBar?

Time:12-11

I am dealing with a Flutter project recently and I have such a problem.

See photo 1: here's my code: the photo and text should be between the TabBar widget and the red background like in design (blue).

Here you can see my actual code:

class Main

   body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            PortfolioSliverAppBar(_pages[_tabController.index].item1),
            SliverPersistentHeader(
              delegate: SliverPersistentHeaderDelegateImpl(
                tabBar: TabBar(
                  padding: EdgeInsets.only(top: 15.0),
                  labelColor: Colors.black,
                  indicatorColor: Colors.black,
                  indicator: BoxDecoration(
                      borderRadius: BorderRadius.all(Radius.circular(18)),
                      color: Colors.blue),
                  controller: _tabController,
                  tabs: _pages
                      .map<Tab>((Tuple3 page) => Tab(text: page.item1))
                      .toList(),
                ),
              ),
            ),
          ];
        },
        body: Container(
          margin: EdgeInsets.only(top: 20.0),
          child: TabBarView(
            controller: _tabController,
            children: _pages.map<Widget>((Tuple3 page) => page.item2).toList(),
          ),
        ),

class Silver

class SliverPersistentHeaderDelegateImpl extends SliverPersistentHeaderDelegate {
  final TabBar tabBar;
  final Color color;

  const SliverPersistentHeaderDelegateImpl({
    Color color = Colors.transparent,
    @required this.tabBar,
  }) : this.color = color;

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(
      color: color,
      child: tabBar,
    );
  }

See photo 2: here's the given design:

enter image description here


class NestedScrollWithTabs extends StatefulWidget {
  const NestedScrollWithTabs({Key key}) : super(key: key);

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

class _NestedScrollWithTabsState extends State<NestedScrollWithTabs>
    with TickerProviderStateMixin {
  
  var animation;
  var controller;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: DefaultTabController(
          length: 2,
          child: NestedScrollView(
            physics: NeverScrollableScrollPhysics(),
            headerSliverBuilder: (headerCtx, innnerBoxIsScrolled) {
              if (innnerBoxIsScrolled) {
                /* Animation */
                controller = AnimationController(
                  vsync: this,
                  duration: Duration(
                    seconds: 1,
                  ),
                );
                animation = Tween(
                  begin: 0.0,
                  end: 1.0,
                ).animate(controller);
                /* Animation */
                controller.forward();
              }

              return <Widget>[
                SliverAppBar(
                  expandedHeight: ScreenUtil().setHeight(185.0),
                  floating: false,
                  pinned: true,
                  backgroundColor: Colors.white,
                  automaticallyImplyLeading: false,
                  titleSpacing: 0.0,
                  centerTitle: true,
                  elevation: 0.0,
                  leadingWidth: 0.0,
                  title: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: [
                    
                      if (innnerBoxIsScrolled != null &&
                          innnerBoxIsScrolled == true)
                        FadeTransition(
                          opacity: animation,
                          child: Text(
                            "Title",
                            style: TextStyle(
                              color: Colors.black,
                              fontSize: 22,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                        ),
                     
                    ],
                  ),
                  flexibleSpace: FlexibleSpaceBar(
                    background: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Stack(
                          alignment: Alignment.center,
                          clipBehavior: Clip.none,
                          children: [
                            Image.network(
                              "https://images.pexels.com/photos/10181294/pexels-photo-10181294.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" ??
                                  "",
                              fit: BoxFit.fitWidth,
                              height: ScreenUtil().setHeight(126.0),
                              width: ScreenUtil().screenWidth,
                              filterQuality: FilterQuality.low,
                              loadingBuilder: (BuildContext context,
                                  Widget child,
                                  ImageChunkEvent loadingProgress) {
                                if (loadingProgress == null) return child;
                                return Container(
                                  height: ScreenUtil().setHeight(126.0),
                                  width: ScreenUtil().screenWidth,
                                  color: Colors.grey,
                                );
                              },
                              errorBuilder: (context, error, stackTrace) {
                                return SizedBox(
                                  height: ScreenUtil().setHeight(126.0),
                                  width: ScreenUtil().screenWidth,
                                  child: Container(
                                    width: ScreenUtil().screenWidth,
                                  ),
                                );
                              },
                            ),
                            Positioned(
                              top: ScreenUtil().setHeight(92.0),
                              // left: ScreenUtil().setWidth(20.0),
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.center,
                                children: [
                                  CircleAvatar(
                                    backgroundColor: Colors.transparent,
                                    radius: 30.0,
                                    child: ClipRRect(
                                      borderRadius: BorderRadius.circular(
                                        45.0,
                                      ),
                                      child: Image.network(
                                        "https://images.pexels.com/photos/10181294/pexels-photo-10181294.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" ??
                                            "",
                                        fit: BoxFit.fill,
                                        height: ScreenUtil().setHeight(72.0),
                                        width: ScreenUtil().screenWidth,
                                        filterQuality: FilterQuality.low,
                                        loadingBuilder: (BuildContext context,
                                            Widget child,
                                            ImageChunkEvent loadingProgress) {
                                          if (loadingProgress == null)
                                            return child;
                                          return Container(
                                            height:
                                                ScreenUtil().setHeight(72.0),
                                            width: ScreenUtil().screenWidth,
                                            color: Colors.grey,
                                          );
                                        },
                                        errorBuilder:
                                            (context, error, stackTrace) {
                                          return SizedBox(
                                            height:
                                                ScreenUtil().setHeight(72.0),
                                            width: ScreenUtil().screenWidth,
                                            child: Container(
                                              width: ScreenUtil().screenWidth,
                                            ),
                                          );
                                        },
                                      ),
                                    ),
                                  ),
                                  Text("Name"),
                                  Text("Place"),
                                ],
                              ),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
                SliverOverlapAbsorber(
                  handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
                      headerCtx),
                  sliver: SliverPersistentHeader(
                    delegate: SliverAppBarDelegate(TabBar(
                      labelColor: Colors.blue,
                      unselectedLabelColor: Colors.black,
                      labelStyle: TextStyle(
                        fontSize: 15.0,
                      ),
                      unselectedLabelStyle: TextStyle(
                        fontSize: 15.0,
                      ),
                      labelPadding: EdgeInsets.zero,
                      indicatorColor: Colors.blue,
                      indicatorPadding: EdgeInsets.zero,
                      physics: NeverScrollableScrollPhysics(),
                      tabs: [
                        Tab(
                          text: "Tab 1",
                        ),
                        Tab(
                          text: "Tab 2",
                        ),
                      ],
                    )),
                    pinned: false,
                  ),
                ),
              ];
            },
            body: TabBarView(
              children: [
                /* Tab 1 */
                Container(
                  color: Colors.white,
                  child: ListView.builder(
                    itemCount: 100,
                    physics: NeverScrollableScrollPhysics(),
                    shrinkWrap: true,
                    itemBuilder: (BuildContext context, int index) {
                      return Padding(
                        padding: const EdgeInsets.all(4.0),
                        child: Text("Index value: $index"),
                      );
                    },
                  ),
                ),
                /* Tab 2 */
                Container(
                  color: Colors.white,
                  child: ListView.builder(
                    itemCount: 10,
                    physics: NeverScrollableScrollPhysics(),
                    shrinkWrap: true,
                    itemBuilder: (BuildContext context, int index) {
                      return Padding(
                        padding: const EdgeInsets.all(4.0),
                        child: Text("Index value of Tab 2: $index"),
                      );
                    },
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  SliverAppBarDelegate(this.tabBars);

  final TabBar tabBars;

  @override
  double get minExtent => 60.0;

  @override
  double get maxExtent => 60.0;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    // shinkOffsetPerValue.value = shrinkOffset;
    return new Container(
      color: Colors.white,
      child: Column(
        children: [
          tabBars,
        ],
      ),
    );
  }

  @override
  bool shouldRebuild(SliverAppBarDelegate oldDelegate) {
    return false;
  }
}

CodePudding user response:

try to place your Avatar and text in bottom of appbar:

AppBar(
  bottom: AvatarWidget(image: '', text: ''),


class AvatarWidget extends StatelessWidget implements PreferredSizeWidget {
  final String image;
  final String text;

  AvatarWidget({required this.image, required this.text});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        //ImageWidget,
        //TextWidget,
      ]
    );
  }

  @override
  Size get preferredSize => Size.fromHeight(100.0);
}
  • Related