flutter SliverPersistentHeaderDelegate应在剩余小部件滚动到其下之前折叠

qqrboqgw  于 2023-06-30  发布在  Flutter
关注(0)|答案(1)|浏览(141)

我试图使SliverPersistentHeaderDelegate先折叠,然后屏幕的其余部分滚动。
这是密码。

class MyHomePage extends StatelessWidget {
  const MyHomePage({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        physics: const PageScrollPhysics(),
        slivers: [
          SliverPersistentHeader(
            pinned: true,
            delegate: SliverAppBarDelegate(),
          ),
          const SliverToBoxAdapter(
            child: SizedBox(height: 24),
          ),
          SliverPersistentHeader(
            pinned: true,
            delegate: SliverGreenMenuBarDelegate(),
          ),
          const SliverPadding(padding: EdgeInsets.only(bottom: 15)),
          SliverFillRemaining(
            child: SizedBox(
              height: 200,
              child: Container(
                color: Colors.amber,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  SliverAppBarDelegate();

  var expandedHeight = 220.0;

  @override
  double get maxExtent => expandedHeight;

  @override
  double get minExtent => kToolbarHeight + 20;

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    var colorScheme = Theme.of(context).colorScheme;

    double radius() {
      if (!overlapsContent && shrinkOffset == 0.0) {
        return 20.0;
      } else {
        return 20 - shrinkOffset / 11;
      }
    }

    final Widget appBar = FlexibleSpaceBar.createSettings(
      minExtent: minExtent,
      maxExtent: maxExtent,
      currentExtent: math.max(minExtent, maxExtent - shrinkOffset),
      child: AppBar(
        actions: [
          IconButton(
            padding: const EdgeInsets.all(0),
            constraints: const BoxConstraints(),
            onPressed: () {},
            icon: const Icon(Icons.abc),
          ),
          Padding(
            padding: const EdgeInsets.only(right: 16, left: 35),
            child: IconButton(
              padding: const EdgeInsets.all(0),
              constraints: const BoxConstraints(),
              onPressed: () {},
              icon: const Icon(Icons.ac_unit_sharp),
            ),
          ),
        ],
        flexibleSpace: const FlexibleSpaceBar(
          expandedTitleScale: 1,
          titlePadding: EdgeInsets.only(top: 4, left: 56, right: 56),
          title: SafeArea(
            child: Text("Hello world"),
          ),
        ),
        elevation: 10,
        shadowColor: colorScheme.secondary.withOpacity(0.2),
        primary: true,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.only(
            bottomLeft: Radius.circular(radius()),
            bottomRight: Radius.circular(radius()),
          ),
        ),
      ),
    );
    return appBar;
  }
}

class SliverGreenMenuBarDelegate extends SliverPersistentHeaderDelegate {
  @override
  double get maxExtent => 75.0;

  @override
  double get minExtent => 40.0;

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }

  @override
  Widget build(
    BuildContext context,
    double shrinkOffset,
    bool overlapsContent,
  ) {
    double horizontalPadding() {
      if (!overlapsContent && shrinkOffset == 0.0) {
        return 25.0;
      } else {
        return 25 - (shrinkOffset / 3);
      }
    }

    double cornerRadius() {
      if (!overlapsContent && shrinkOffset == 0.0) {
        return 15.0;
      } else {
        return 15 - shrinkOffset / 5;
      }
    }

    return FlexibleSpaceBar.createSettings(
      maxExtent: maxExtent,
      minExtent: minExtent,
      currentExtent: math.max(minExtent, maxExtent - shrinkOffset),
      isScrolledUnder: true,
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: horizontalPadding()),
        child: Container(
          decoration: BoxDecoration(
            color: Colors.green,
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(cornerRadius()),
              topRight: Radius.circular(cornerRadius()),
              bottomLeft: const Radius.circular(15),
              bottomRight: const Radius.circular(15),
            ),
          ),
        ),
      ),
    );
  }
}

这是视频

基本上,我想要的是绿色的部分先崩溃,黄色的部分在它下面。
我如何才能做到这一点?
最终结果应该如下面的屏幕截图所示。
| 在scrool之后| After scrool |
| --| ------------ |
| | |
|

|

|
提前感谢您的帮助!

t2a7ltrp

t2a7ltrp1#

  • 我添加了一个 AnimationController 和一个Animation来处理动画。还有一个ScrollController来监视滚动位置。
  • 在initState方法中,我初始化动画控制器并使用Tween定义动画范围。
  • 补间定义动画的开始值和结束值。我还在滚动控制器上设置了一个监听器,以根据滚动偏移量来确定是向前还是向后播放动画。
import 'package:flutter/material.dart';
    
    class DiffSliver extends StatefulWidget {
      const DiffSliver({Key? key}) : super(key: key);
    
      @override
      State<DiffSliver> createState() => _DiffSliverState();
    }
    
    class _DiffSliverState extends State<DiffSliver>
        with SingleTickerProviderStateMixin {
      late AnimationController _animationController;
      late Animation<double> _animation;
      final ScrollController _scrollController = ScrollController();
    
      @override
      void initState() {
        super.initState();
        _animationController = AnimationController(
          vsync: this,
          duration: Duration(milliseconds: 250),
        );
        _animation = Tween<double>(begin: 0.0, end: 1.0).animate(
          CurvedAnimation(
            parent: _animationController,
            curve: Curves.easeInOut,
          ),
        );
    
        _scrollController.addListener(() {
          if (_scrollController.offset > 150) {
            _animationController.forward();
          } else {
            _animationController.reverse();
          }
        });
      }
    
      @override
      void dispose() {
        _animationController.dispose();
        _scrollController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.white,
          body: SafeArea(
            child: NestedScrollView(
              controller: _scrollController,
              headerSliverBuilder: (context, innerBoxIsScrolled) {
                return [
                  SliverAppBar(
                    pinned: true,
                    expandedHeight: 200,
                    flexibleSpace: FlexibleSpaceBar(
                      background: Container(
                        width: 100,
                        height: 100,
                        color: Colors.blue,
                      ),
                    ),
                  ),
                  SliverPersistentHeader(
                    pinned: true,
                    delegate: AnimatedContainerDelegate(animation: _animation),
                  ),
                  SliverPersistentHeader(
                    pinned: true,
                    delegate: ContainerDelegate(),
                  )
                ];
              },
              body: Container(
                color: Colors.white,
              ),
            ),
          ),
        );
      }
    }
    
    class ContainerDelegate extends SliverPersistentHeaderDelegate {
      @override
      Widget build(
          BuildContext context, double shrinkOffset, bool overlapsContent) {
        return Padding(
          padding: const EdgeInsets.all(20.0),
          child: Container(
            width: 150,
            height: 250,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(20),
              color: Colors.orange,
            ),
          ),
        );
      }
    
      @override
      // TODO: implement maxExtent
      double get maxExtent => 250;
    
      @override
      // TODO: implement minExtent
      double get minExtent => 250;
    
      @override
      bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
        return false;
      }
    }
    
    class AnimatedContainerDelegate extends SliverPersistentHeaderDelegate {
      final Animation<double> animation;
    
      AnimatedContainerDelegate({required this.animation});
    
      @override
      Widget build(
        BuildContext context,
        double shrinkOffset,
        bool overlapsContent,
      ) {
        return AnimatedBuilder(
          animation: animation,
          builder: (BuildContext context, Widget? child) {
            final double paddingValue = 40.0 * (1 - animation.value);
            final EdgeInsets padding = EdgeInsets.all(paddingValue);
            final double borderRadiusValue = 20.0 * animation.value;
            final BorderRadius borderRadius = BorderRadius.only(
              bottomLeft: Radius.circular(borderRadiusValue),
              bottomRight: Radius.circular(borderRadiusValue),
            );
    
            return Container(
              margin: padding,
              decoration: BoxDecoration(
                color: Colors.yellow,
                borderRadius: borderRadius,
              ),
            );
          },
        );
      }
    
      @override
      double get maxExtent => 200;
    
      @override
      double get minExtent => 50;
    
      @override
      bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
        return true;
      }
    }

This video is output

相关问题