首页 > 解决方案 > 如何阻止固定的 SliverAppBar 覆盖浮动的 SliverPersistentHeader

问题描述

我正在学习 Flutter,目前正在尝试制作一个具有炫酷滚动效果的主页。我正在尝试实现一个具有 3 个元素的 CustomScrollView:一个 SliverAppBar、一个水平滚动列表和一个 SliverList。前两个很简单,经过一番努力,我设法通过使用 SliverPersistentHeader 来实现水平滚动列表。

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

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

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

我怎样才能解决这个问题?我有什么遗漏会导致这个问题吗?

感谢您的时间!

标签: flutterdartflutter-layout

解决方案


    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 动画化到视图中,希望您可以自己实现这一点。我确信还有其他方法可以实现这一点,但这个解决方案应该适合你。


推荐阅读