Search code examples
flutterflutter-layoutflutter-sliver

How to achieve sliver in flutter when collapsing content is dynamic


I have a requirement to develop a screen where there is collapsible content to be achieved using sliver.

However, the height of collapsible content is dynamic and depends on the number of dynamic widgets applicable to the user (some may not have both dynamic widgets, some have one, some have both). These dynamic widgets load as parallel service to backend and not in sequential manner. Otherwise I would have calculated the height one by one.

Help would be appreciated since all examples on internet point to have a fixed header height for slivers

Example image attached of what i am trying to achieve.

enter image description here


Solution

  • Try with the silver appbar and make sure that your toolbarHeight is 0. Here I used just fixed height for a single element and the total height will be changed based on the number of elements or widgets you have.

    import 'package:flutter/material.dart';
    
    class DynamicAppbar extends StatefulWidget {
      const DynamicAppbar({Key key}) : super(key: key);
    
      @override
      _DynamicAppbarState createState() => _DynamicAppbarState();
    }
    
    class _DynamicAppbarState extends State<DynamicAppbar> {
      //set the height fixed for each widget
      double fixedHeight = 50;
    
      // replace with coming elements
      List<String> items = [
        "dynamicWidget1",
        "dynamicWidget2",
        "dynamicWidget3",
        "dynamicWidget4",
      ];
    
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
            appBar: AppBar(
              title: Text("My App Bar"),
            ),
            body: DefaultTabController(
              length: 2,
              child: NestedScrollView(
                headerSliverBuilder:
                    (BuildContext context, bool innerBoxIsScrolled) {
                  return <Widget>[
                    SliverAppBar(
                      expandedHeight: fixedHeight * items.length,
                      floating: false,
                      pinned: true,
                      snap: false,
                      toolbarHeight: 0,
                      flexibleSpace: FlexibleSpaceBar(
                        background: Column(
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: List<Widget>.generate(
                            items.length,
                            (index) {
                              return Container(
                                height: fixedHeight,
                                child: Center(
                                    child: Text(
                                  items[index],
                                  textAlign: TextAlign.center,
                                  style: TextStyle(
                                      fontSize: 24, fontWeight: FontWeight.bold),
                                )),
                              );
                            },
                          ),
                        ),
                      ),
                    ),
                    SliverPersistentHeader(
                      delegate: _SliverAppBarDelegate(
                        TabBar(
                          labelColor: Colors.black87,
                          unselectedLabelColor: Colors.grey,
                          tabs: [
                            Tab(icon: Icon(Icons.info), text: "Tab 1"),
                            Tab(icon: Icon(Icons.lightbulb_outline), text: "Tab 2"),
                          ],
                        ),
                      ),
                      pinned: true,
                    ),
                  ];
                },
                body: Center(
                  child: Text("Sample text"),
                ),
              ),
            ),
          ),
        );
      }
    }
    
    class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
      _SliverAppBarDelegate(this._tabBar);
    
      final TabBar _tabBar;
    
      @override
      double get minExtent => _tabBar.preferredSize.height;
    
      @override
      double get maxExtent => _tabBar.preferredSize.height;
    
      @override
      Widget build(
          BuildContext context, double shrinkOffset, bool overlapsContent) {
        return new Container(
          child: _tabBar,
        );
      }
    
      @override
      bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
        return false;
      }
    }
    
    
    

    Output:

    Before and after scrolling

    enter image description here