0

I want to achieve the following behavior in Flutter.

this

Do you know if there is a built-in widget in Flutter that provides that functionality out of the box?

Umair M
  • 10,298
  • 6
  • 42
  • 74
  • https://api.flutter.dev/flutter/material/SliverAppBar-class.html – creativecreatorormaybenot Sep 24 '19 at 15:09
  • 1
    Thanks for the suggestion. I have already tried with SliverAppBar, but using it I was not able to observe any scalling of the FlexibleSpaceBar. Is there something that needs to be enabled in order to make the content scale-in and out while scrolling? – APopatanassov Sep 24 '19 at 15:15
  • Welcome to SO. Attach the code, please. It's recommended to add what you achieved so far. for more info, you can visit StackOverflow code of conduct – Kiran Maniya Sep 24 '19 at 15:23
  • 1
    see [SliverPersistentHeader](https://api.flutter.dev/flutter/widgets/SliverPersistentHeader-class.html) - the docs say: *"A sliver whose size varies when the sliver is scrolled to the leading edge of the viewport."* – pskink Sep 24 '19 at 15:28
  • Thank you. I believe SliverPersistenHeader is what I need. :) Sorry, for not providing any code, but the current state of the code is I believe a mess and could be confusing and misleading, because of the I only provided the gif with the desired behavior. – APopatanassov Sep 24 '19 at 16:05
  • all you need is to create a custom `SliverPersistentHeaderDelegate` class and implement its 4 simple methods – pskink Sep 25 '19 at 04:44
  • The final approach I used was Transform.scale in combination with GestureDetector - using the onVerticalDragUpdate. Some calculations were needed for the scale but finally everything worked as expected. Animation for scaling out was also used to achieve the desired scaling when the drag ends. Thanks all for the help. – APopatanassov Sep 25 '19 at 08:49
  • no no no, you dont need any `Transform`, nor `GestureDetector` - i will add some answer in 10 minutes – pskink Sep 25 '19 at 11:25

2 Answers2

1

try this CustomScrollView:

LayoutBuilder(
  builder: (context, constraints) {
    return CustomScrollView(
      slivers: <Widget>[
        SliverPersistentHeader(
          pinned: true,
          delegate: Delegate(),
        ),
        SliverToBoxAdapter(
          child: Container(
            decoration: BoxDecoration(
              borderRadius:  BorderRadius.all(Radius.circular(30)),
              border:  Border.all(
                width: 2.0,
                color:  Colors.deepPurple,
              ),
            ),
            height: constraints.biggest.height,
            child: Center(
              child: Text('Drag me down (or up) in order to see (or hide) the red icon on the top',
                textAlign: TextAlign.center,
                textScaleFactor: 5.0,
              ),
            ),
          ),
        ),
      ],
    );
  }
),

the "delegate" class is as follows:

class Delegate extends SliverPersistentHeaderDelegate {
  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    // print(shrinkOffset);
    return Opacity(
      opacity: 1 - shrinkOffset / maxExtent,
      child: FittedBox(child: Icon(Icons.alarm, color: Colors.red,), fit: BoxFit.contain),
    );
  }
  @override double get maxExtent => 300;
  @override double get minExtent => 100;
  @override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}

now try to drag the text up and down

EDIT: and this is a little modification to be similar like your example:

LayoutBuilder(
  builder: (context, constraints) {
    return CustomScrollView(
      slivers: <Widget>[
        SliverPersistentHeader(
          pinned: true,
          delegate: Delegate(),
        ),
        SliverToBoxAdapter(
          child: Container(
            padding: EdgeInsets.only(top: 75),
            height: constraints.biggest.height,
            child: Text('Drag me down (or up) in order to make the red icon bigger (or smaller)',
              textAlign: TextAlign.center,
              textScaleFactor: 5.0,
            ),
          ),
        ),
      ],
    );
  }
),

and the modified delegate class:

class Delegate extends SliverPersistentHeaderDelegate {
  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    // print(shrinkOffset);
    return OverflowBox(
      maxHeight: 400,
      alignment: Alignment.topCenter,
      child: Container(
        height: 400 - shrinkOffset,
        child:  FittedBox(child: Icon(Icons.alarm, color: Colors.red,), fit: BoxFit.contain),
      ), 
    );
  }
  @override double get maxExtent => 300;
  @override double get minExtent => 1;
  @override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
pskink
  • 23,874
  • 6
  • 66
  • 77
0

SliverAppBar now provides this feature out of the box using the stretch property:

SliverAppBar(
    stretch: true, // <- this
    flexibleSpace: FlexibleSpaceBar(
        background: Image.network('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg', fit: BoxFit.cover),
        title: Text('Hello again, SliverAppBar'),
        stretchModes: <StretchMode>[
            StretchMode.zoomBackground, // <- and this
        ],
    ),
);

Ref:

Umair M
  • 10,298
  • 6
  • 42
  • 74