如何阻止固定的SliverAppBar覆盖浮动SliverPersistentHeader

时间:2020-06-03 12:43:52

标签: flutter dart flutter-layout

我正在学习Flutter,目前正在尝试制作具有出色滚动效果的主页。我正在尝试使用3个元素实现CustomScrollView:SliverAppBar,水平滚动列表和SliverList。前两个很简单,经过一番挣扎之后,我设法使用SliverPersistentHeader实现了水平滚动列表。

但是,我遇到了一个问题。我希望将SliverAppBar固定,并且将包含水平滚动列表的SliverPersistentHeader浮动。一切正常,除了向下滚动后向上滚动时,浮动元素被固定的元素覆盖。我基本上希望浮动元素“知道”其上方还有另一个元素,并在向上滚动时偏移自身。

您可以在此查看问题,以及我的代码: https://dartpad.dev/32d3f2a890d4a676decb014744fcc9ba

请确保您单击并拖动滚动以查看问题!

我该如何解决?我缺少任何导致此问题的东西吗?

感谢您的光临!

1 个答案:

答案 0 :(得分:0)

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

    void main() {
      runApp(new MyApp());
    }

    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }

    class _MyAppState extends State<MyApp> {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          home: Home(),
        );
      }
    }


// I had to change this class to a StatefulWidget to be able to listen to the scroll event
    class Home extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _HomeState();
      }
    }

    class _HomeState extends State<Home>  {
// Here I declared the ScrollController for the CustomScrollView
      ScrollController _controller;

// And here is a boolean to check when the user scrolls up or down the view
      bool sliverPersistentHeader = false;

      @override
      void initState() {
        super.initState();
// The ScrollController is initialized in the initState and listens for when the user starts scrolling up and changes the boolean value accordingly
        _controller = ScrollController();
        _controller.addListener(() {
          if (_controller.position.userScrollDirection == ScrollDirection.reverse) {
            setState(() {
              sliverPersistentHeader = false;
            });
          } else {
            setState(() {
              sliverPersistentHeader = true;
            });
          }
        });
      }

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

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: CustomScrollView(
            controller: _controller,
            slivers: <Widget>[
              SliverAppBar(
                floating: true,
                pinned: true,
                expandedHeight: 200.0,
                flexibleSpace: FlexibleSpaceBar(
                  centerTitle: true,
                  title: Text('App Title'),
                ),
              ),
              SliverPersistentHeader(
// The SliverPersisitentHeader checks the boolean value and either pins or unpins the the Header
                pinned: sliverPersistentHeader ? true : false,
                delegate: CustomSliver(
                  expandedHeight: 150.0,
                ),
              ),
              SliverList(
                delegate: SliverChildBuilderDelegate(
                  (_, index) => Padding(
                    padding: EdgeInsets.symmetric(vertical: 10.0),
                    child: Container(
                      height: 50.0,
                      color: Colors.amber,
                    ),
                  ),
                ),
              ),
            ],
          ),
          bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                title: Text('Tab1'),
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                title: Text('Tab2'),
              ),
              BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Tab3'))
            ],
            currentIndex: 0,
          ),
        );
      }
    }

    class CustomSliver extends SliverPersistentHeaderDelegate {
      final double expandedHeight;

      CustomSliver({@required this.expandedHeight});

      @override
      Widget build(
          BuildContext context, double shrinkOffset, bool overlapsContent) {
        return Scrollbar(
          child: Container(
              color: Theme.of(context).canvasColor,
              padding: EdgeInsets.fromLTRB(10.0, 15.0, 0, 5.0),
              child: ListView.separated(
                shrinkWrap: true,
                physics: BouncingScrollPhysics(),
                scrollDirection: Axis.horizontal,
                itemCount: 10,
                itemBuilder: (BuildContext context, int index) {
                  return Padding(
                    padding: EdgeInsets.only(right: 10.0, top: 10.0, bottom: 10.0),
                    child: Container(
                      width: 100,
                      decoration: BoxDecoration(
                          color: Colors.red,
                          borderRadius: BorderRadius.all(Radius.circular(20.0)),
                          boxShadow: [
                            BoxShadow(
                                color: Colors.black.withOpacity(0.16),
                                offset: Offset(0, 3.0),
                                blurRadius: 6.0),
                          ]),
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: <Widget>[
                          Icon(Icons.navigation),
                          Text(
                            'Category',
                            textAlign: TextAlign.center,
                            style: TextStyle(color: Colors.white),
                          ),
                        ],
                      ),
                    ),
                  );
                },
                separatorBuilder: (BuildContext context, int index) {
                  return SizedBox(width: 5.0);
                },
              )),
        );
      }

      @override
      double get maxExtent => expandedHeight;

      @override
      double get minExtent => 150.0;

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

我唯一没有做的就是将SliverPersistentHeader设置为动画,希望您可以自己实现。我确定还有其他方法可以实现此目的,但是该解决方案应该可以为您工作。