The appBar doesn't expand or collapse automatically | Flutter

99 Views Asked by At

What I want to achieve is when I scroll, the appBar should expand or collapse automatically, so that there is no middle state, only collapsed or expanded completely. This feature can be seen in contact or setting app, so I want to reproduce it.

Here is my code:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class CustomAppBar extends StatelessWidget {
  final _controller = ScrollController();

  CustomAppBar({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NotificationListener<ScrollNotification>(
        onNotification: (scrollNotification) {
          if (scrollNotification is ScrollEndNotification &&
              scrollNotification.depth == 0) {
            final minExtent = scrollNotification.metrics.minScrollExtent;
            final maxExtent = scrollNotification.metrics.maxScrollExtent;
            final middle = (maxExtent - minExtent) / 2;
            final pos = scrollNotification.metrics.pixels;
            if (kDebugMode) {
              print(
                  "pos : $pos, maxExtent : $maxExtent, minExtent : $minExtent");
            }
            if (pos <= middle) {
              _controller.animateTo(minExtent,
                  duration: const Duration(milliseconds: 300),
                  curve: Curves.ease);
            } else if (middle > pos) {
              _controller.animateTo(maxExtent,
                  duration: const Duration(milliseconds: 300),
                  curve: Curves.ease);
            }
          }
          return false;
        },
        child: NestedScrollView(
          controller: _controller,
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return <Widget>[
              SliverOverlapAbsorber(
                handle:
                    NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                sliver: const SliverAppBar(
                    expandedHeight: 200.0,
                    pinned: true,
                    flexibleSpace:
                        FlexibleSpaceBar(title: Text("Hello world"))),
              ),
            ];
          },
          body: Builder(builder: (BuildContext context) {
            return CustomScrollView(
              slivers: [
                SliverOverlapInjector(
                    handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
                        context)),
                const SliverToBoxAdapter(
                    child: SizedBox(
                        height: 200, child: Center(child: Text("Slivers"))))
              ],
            );
          }),
        ),
      ),
    );
  }
}

I changed the condition and the animation but it still doesn't work as expected.

1

There are 1 best solutions below

0
Dennis Kinuthia On

I have a work around of some sorts maybe it might help you.

  1. Create a parentScrollController and add it to the NestedScrollView.
  2. Set the scroll physics of the CustomScrollView to ClampingScrollPhysics().
  3. In the onNotification function change the condition to this -
if(
  notification is OverscrollNotification 
  && notification.overscroll < 0
) {
  parentScrollController!.jumpTo(0);
}
  1. Add an event listener to the parent scroll controller and test -
if(_scrollController.position.userScrollDirection == ScrollDirection.reverse) {
  parentScrollController!.jumpTo(
    (MediaQuery.of(context).size.height * 0.2) + kToolbarHeight
  );
}

Hope this helps.