Search code examples
flutterdartscrolloverlapflutter-sliverappbar

flutter 2 SliverPersistentHeader - avoid overlap during scroll


Here is my code :

import 'package:all_in_one/cooking/pages/recipe/header/search_bar_header.dart';
import 'package:all_in_one/cooking/pages/recipe/header/welcome_header.dart';
import 'package:flutter/material.dart';

class MyRecipePage extends StatefulWidget {
  final String title;

  MyRecipePage({Key? key, required this.title}) : super(key: key);

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

class _MyRecipePageState extends State<MyRecipePage> {

 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          WelcomeHeader(),
          SearchBarHeader(),
          SliverGrid.count(),
        ],
      ) ,

    );
  }
}

class WelcomeHeader extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return
        SliverPersistentHeader(
          floating: true,
          delegate: SliverAppBarDelegate(
            minHeight: 0,
            maxHeight: 100,
            child: Container(
              color: Colors.white,
              child: _MyWelcomingHeader(),
            ),
          ),


    );
  }
}

class _MyWelcomingHeader extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Flexible(
          child: CircleAvatar(
            radius: 57,
            backgroundColor: Colors.grey.shade50,
            child: Image.asset("assets/emoji-food.jpg"),
          ),
        ),
        Flexible(
          child: Padding(
            padding: const EdgeInsets.all(10.0),
            child: Text(
              'Enjoy the recipes',
              style: TextStyle(
                color: Colors.black,
                fontSize: 26.0,
                fontWeight: FontWeight.bold,
              ),
            ),
          ),
        ),
      ],
    );
  }
}


class SearchBarHeader extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverPersistentHeader(
      pinned: true,
      delegate: SliverAppBarDelegate(
        minHeight: 50,
        maxHeight: 50,
        child: Container(
          color: Colors.white,
          child: _MySearchBar(),
        ),
      ),
    );
  }
}

class _MySearchBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        const SizedBox(width: 10),
        const Icon(Icons.search, color: Colors.grey, size: 30),
        const SizedBox(width: 5),
        Text("Search product",
            style: TextStyle(
                color: Colors.grey.shade500, fontSize: 12, fontWeight: FontWeight.w200))
      ],
    );
  }
}

The code of the silver bar Delegate is from this stackoverflow post

import 'package:flutter/material.dart';
import 'dart:math' as math;

class SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  final double minHeight;
  final double maxHeight;
  final Widget child;

  SliverAppBarDelegate({
    required this.minHeight,
    required this.maxHeight,
    required this.child,
  });


  @override
  double get minExtent => minHeight;
  @override
  double get maxExtent => math.max(maxHeight, minHeight);
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}

My goal is to have 2 SliverPersistentHeader, one pinned and one floating. The one floating (the 1st one) should resize the text and the image while scrolling..

In the following screenshot, we can see that the 2nd SliverPersistentHeader is overlapping the 1st one. How can I do to make the Text resize itself. I try to use Flexible like I did for the CircleAvatar but I can't succeed :/

second-SliverPersistentHeader-overlap-first-SliverPersistentHeader

Thanks


Solution

  • I found a solution using the opacity, so my WelcomeHeader becomes :

    import 'package:flutter/material.dart';
    import 'dart:math' as math;
    
    class WelcomeHeader extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return SliverAppBar(
          backgroundColor: Colors.white,
          pinned: false,
          floating: false,
          snap: false,
          expandedHeight: 120,
          flexibleSpace: _MyWelcomingHeader()
        );
      }
    }
    
    class _MyWelcomingHeader extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return LayoutBuilder(
            builder: (context, c) {
              final settings = context
                  .dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>();
              final deltaExtent = settings!.maxExtent - settings.minExtent;
              final t =
              (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent)
                  .clamp(0.0, 1.0);
              final fadeStart = math.max(0.0, 1.0 - kToolbarHeight / deltaExtent);
              const fadeEnd = 1.0;
              final opacity = 1.0 - Interval(fadeStart, fadeEnd).transform(t);
    
              return Padding(
                padding: const EdgeInsets.all(8.0),
                child: Opacity(
                  opacity: opacity,
                  child: Column(
                    children: [
                      Flexible(
                        child: CircleAvatar(
                          radius: 57,
                          backgroundColor: Colors.grey.shade50,
                          child: Image.asset("assets/emoji-food.jpg"),
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Text(
                          'Enjoy the recipes !',
                          style: TextStyle(
                            color: Colors.black,
                            fontSize: 26.0,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              );
            });
      }
    }