Search code examples
flutterdartflutter-animationflutter-sliver

Add image to top left of screen (FLUTTER SCROLL)


So I have a pretty simple issue. I have an image. Lets say its 'assets/images/user.png'. I want that image to appear in the top left of the app (In the middle of the white circle). When the user scrolls downward, the title, and description both disappear and then the Go Premium button get pinned to the top. I want a user png to appear in the TOP LEFT corner in the MIDDlE of the white circle, and then when the user scrolls down, I want the user png to go down a bit and position itself to the left of the Go Premium button, in the small gap. I want this to work for different phones too, so hardcoding is allowed IF it would work for a smaller phone. Thanks! Here is an image of my app before scroll: enter image description here Here is one after scroll: enter image description here

          CustomScrollView(
            slivers: [
              SliverAppBar(
                backgroundColor:
                    const Color.fromARGB(12, 14, 13, 3).withOpacity(1),
                centerTitle: false,
                bottom: PreferredSize(
                  preferredSize: const Size.fromHeight(0),
                  child: SizedBox(
                    width: size.width * .7,
                    child: ElevatedButton(
                      onPressed: () {},
                      style: ElevatedButton.styleFrom(
                          shape: const StadiumBorder(),
                          backgroundColor: Color.fromARGB(255, 255, 0, 0)),
                      child: const Text(
                        'Go Premium!',
                        style: TextStyle(
                            color: Colors.black,
                            fontWeight: FontWeight.bold),
                      ),
                    ),
                  ),
                ),
                pinned: true,
                expandedHeight: 165,
                titleSpacing: 15,
                flexibleSpace: FlexibleSpaceBar(
                  collapseMode: CollapseMode.pin,
                  background: Stack(
                    children: [
                      Positioned(
                        top: -size.width * .08,
                        left: -size.width * .08,
                        child: Transform.translate(
                          offset: const Offset(-30, -30),
                          child: const CircleButton(),
                        ),
                      ),
                      Positioned(
                        left: size.width * .2,
                        top: size.height * .09,
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            const Text('JustLift',
                                style: TextStyle(
                                    color:
                                        Color.fromARGB(255, 252, 250, 250),
                                    fontWeight: FontWeight.bold,
                                    fontSize: 50)),
                            Text(
                              element,
                              style: TextStyle(
                                color: Color.fromARGB(255, 158, 154, 154),
                                fontWeight: FontWeight.bold,
                                fontSize: 20,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),

Solution

  • You can use lerp method from SliverPersistentHeaderDelegate. Try to play with widget

    class CustomSliverPersistentHeaderDelegate
        extends SliverPersistentHeaderDelegate {
      final VoidCallback onTap;
    
      CustomSliverPersistentHeaderDelegate({required this.onTap});
    
      @override
      Widget build(
          BuildContext context, double shrinkOffset, bool overlapsContent) {
        final t = shrinkOffset / maxExtent;
        final avatarSize = lerpDouble(100, 48, t);
        return Material(
          color: Colors.black,
          child: Stack(
            children: [
              Positioned(
                top: lerpDouble(maxExtent / 2, -60, t),
                left: 0,
                right: 0,
                child: Text(
                  'Hello',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    color: Colors.white,
                  ),
                ),
              ),
              Positioned(
                top: lerpDouble(-20, 0, t),
                left: lerpDouble(-20, 4, t),
                child: Container(
                  width: avatarSize,
                  height: avatarSize,
                  decoration: const ShapeDecoration(
                    shape: CircleBorder(),
                    color: Colors.red,
                  ),
                ),
              ),
              Positioned(
                bottom: 8,
                left: 0,
                right: 0,
                child: Center(
                  child: ElevatedButton(
                    onPressed: onTap,
                    child: Text('Button'),
                  ),
                ),
              ),
            ],
          ),
        );
      }
    
      @override
      double get maxExtent => 200;
    
      @override
      double get minExtent => kToolbarHeight;
    
      @override
      bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
        return true;
      }
    }
    

    And use like

    return Scaffold(
        body: CustomScrollView(
      slivers: [
        SliverPersistentHeader(
          pinned: true,
          delegate: CustomSliverPersistentHeaderDelegate(
            onTap: () {},
          ),
        ),
        SliverToBoxAdapter(
          child: SizedBox(
            height: 2222,
          ),
        )
      ],
    ));
    

    If you need more accuracy on overlapping, You can use CompositedTransformTarget