gaith
gaith

Reputation: 11

Inconsistent behavior of SABT widget when changing scroll direction in Flutter

If I scroll down, the collapsed app bar becomes visible and the first child is also visible. That's good. However, if I suddenly change the scrolling direction to up, even though the app bar is still collapsed, the second child is displayed. Why does this happen? Which part of the code might be causing the problem?

CODE:

class SABT extends StatefulWidget {
  final Widget firstChild;
  final Widget secondChild;

  const SABT({
    Key? key,
    required this.firstChild,
    required this.secondChild,
  }) : super(key: key);

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

class _SABTState extends State<SABT> {
  ScrollPosition? _position;
  bool _visibleFirst = true;

  @override
  void dispose() {
    _removeListener();
    super.dispose();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    _removeListener();
    _addListener();
  }

  void _addListener() {
    _position = Scrollable.of(context)?.position;
    _position?.addListener(_positionListener);
    _positionListener();
  }

  void _removeListener() {
    _position?.removeListener(_positionListener);
  }

  void _positionListener() {
    final FlexibleSpaceBarSettings? settings =
        context.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>();

    bool visibleFirst =
        settings == null || settings.currentExtent <= settings.minExtent;

    if (_visibleFirst != visibleFirst) {
      setState(() {
        _visibleFirst = visibleFirst;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Visibility(
      visible: _visibleFirst,
      child: widget.firstChild,
      replacement: Visibility(
        visible: !_visibleFirst,
        child: widget.secondChild,
      ),
    );
  }
}
          SliverAppBar(
           pinned: true,
           elevation: 0,
           stretch: true,
            expandedHeight: 500,
title: SABT(firstChild: Text("first title"),secondChild: Text("second title")),
            flexibleSpace: FlexibleSpaceBar(
              background: Container(
                color: Colors.purple,
              ),
            ),
          ),

I have examined the code for the SABT widget and tested it in my Flutter project. To troubleshoot the issue, I checked the scroll position listener and the visibility logic. Additionally, I reviewed the implementation of the SliverAppBar that contains the SABT widget. However, despite my efforts, I couldn't identify the exact cause of the problem.

I was expecting the SABT widget to maintain the visibility of the first child when the scroll direction is suddenly changed to up, even if the SliverAppBar remains collapsed. However, currently, when I change the scroll direction to up, the second child is unexpectedly displayed instead. I would like the SABT widget to consistently show the first child when the scroll direction changes, as long as the SliverAppBar is still collapsed.

Upvotes: 1

Views: 99

Answers (2)

Niko
Niko

Reputation: 605

what Flutter version and what kind of test device are you using?

I could not reproduce the issue with a simple CustomScrollView on the newest Flutter version with an android device:

return CustomScrollView(
 physics: const BouncingScrollPhysics(),
 reverse: false,
 slivers: <Widget>[
  SliverAppBar(
    pinned: true,
    elevation: 0,
    stretch: true,
    expandedHeight: 500,
    title: const SABT(firstChild: Text("first title"), secondChild: Text("second title")),
    flexibleSpace: FlexibleSpaceBar(background: Container(color: Colors.purple)),
  ),
  SliverList(
    delegate: SliverChildBuilderDelegate(
      (BuildContext context, int index) => ListTile(title: Text('Item #$index')),
      childCount: 30,
    ),
  ),
 ],
);

You can also convert your SABT into a StatelessWidget and remove the dependency on the scroll position.

class SABT extends StatelessWidget {
  final Widget firstChild;
  final Widget secondChild;

  const SABT({
    Key? key,
    required this.firstChild,
    required this.secondChild,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final FlexibleSpaceBarSettings? settings = context.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>();
    final bool visibleFirst = settings == null || settings.currentExtent <= settings.minExtent;
    return Visibility(
      visible: visibleFirst,
      replacement: Visibility(
        visible: !visibleFirst,
        child: secondChild,
      ),
      child: firstChild,
    );
  }
}

Upvotes: 1

Kaushik Chandru
Kaushik Chandru

Reputation: 17732

In replacement you dont have to again add visibility

replacement:  widget.secondChild,

Replacement is displayed when the first child is hidden

Upvotes: 0

Related Questions