【问题标题】:Pinned SliverPersistentHeader inside CustomScrollView scrolls behind another pinned SliverPersistentHeader inside NestedScrollViewCustomScrollView 内的固定 SliverPersistentHeader 滚动到 NestedScrollView 内的另一个固定 SliverPersistentHeader 后面
【发布时间】:2021-05-15 06:59:11
【问题描述】:

背景

我有两个SliverPersistentHeaders。一个位于NestedScrollView 内部,里面有一个TabBar,它应该在滚动条上始终可见,所以我把它固定了。另外我还有另一个SliverPersistentHeader,它位于CustomScrollView 的其中一个选项卡中。打开此选项卡时,它应该在滚动时始终可见,因此我也将其固定。

这是我的视觉效果。我写的SliverPersistentHeaders是白色和蓝色的:

https://ibb.co/JzZPyqp

问题

我希望两个SliverPersistentHeaders 都被固定而不是滚动。但事实证明,CustomScrollView 中的 SliverPersistentHeader 滚动到 NestedScrollView 中的 SliverPersistentHeader 后面,然后才会像这样被固定:

https://s2.gifyu.com/images/untitled446dfde99b45261c.gif

让我们跳转到代码

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  var _tabController;

  @override
  void initState() {
    _tabController = TabController(
      length: 2,
      vsync: this,
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: NestedScrollView(
          physics: BouncingScrollPhysics(),
          floatHeaderSlivers: true,
          headerSliverBuilder: (context, value) {
            return [
              // here we have the first SliverPersistentHeader
              // with TabBar as content
              // inside of NestedScrollView
              SliverPersistentHeader(
                pinned: true,
                floating: false,
                delegate: _TabBarAsSliverPersistentHeader(_tabController),
              ),
            ];
          },
          body: 
              // BlocProvider might be here in the real project...
          CustomScrollView(
            slivers: [
              // here we have the second SliverPersistentHeader
              // inside of CustomScrollView
              // it has unexpected behaviour as shown in gif above
              SliverPersistentHeader(
                pinned: true,
                floating: false,
                delegate: PersistentHeaderDateFilter(),
              ),
              SliverList(
                delegate: SliverChildBuilderDelegate(
                  (context, index) => Container(
                    height: 100,
                    margin: EdgeInsets.all(10),
                    color: Colors.red,
                    
                  ),
                  childCount: 10,
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class _TabBarAsSliverPersistentHeader extends SliverPersistentHeaderDelegate {
  final TabController _tabController;

  _TabBarAsSliverPersistentHeader(this._tabController);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    // TODO: implement build
    return Container(
      // color: Theme.of(context).backgroundColor,
      child: Stack(
        children: [
          Positioned.fill(
            child: Container(
              decoration: BoxDecoration(
                border: Border(
                  bottom: BorderSide(
                    color: Theme.of(context).dividerColor,
                    width: 1.0,
                  ),
                ),
              ),
            ),
          ),
          Container(
            color: Colors.transparent,
            child: TabBar(
              
              isScrollable: false,
              indicator: UnderlineTabIndicator(
                borderSide: BorderSide(
                  width: 2,
                  color: Colors.blue,
                ),
              ),
              indicatorSize: TabBarIndicatorSize.tab,
              indicatorWeight: 2,
              labelColor: Colors.blue,
              unselectedLabelColor: Colors.grey,
              tabs: [
                Tab(text: 'by rating'),
                Tab(text: 'by date'),
              ],
              controller: _tabController,
            ),
          ),
        ],
      ),
    );
  }

  @override
  double get maxExtent => 46.0;

  @override
  double get minExtent => 46.0;

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

class PersistentHeaderDateFilter extends SliverPersistentHeaderDelegate {
  PersistentHeaderDateFilter();

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    // TODO: implement build
    return Container(
      width: MediaQuery.of(context).size.width,
      padding: EdgeInsets.symmetric(
        vertical: 10,
      ),
      decoration: BoxDecoration(
        color: Colors.blue.shade200,
        border: Border(
          bottom: BorderSide(
            color: Colors.green,
            width: 1.0,
          ),
        ),
      ),
      alignment: Alignment.center,
      child: ListView.builder(
        padding: EdgeInsets.symmetric(horizontal: 20),
        // shrinkWrap: true,
        scrollDirection: Axis.horizontal,
        itemCount: 10,
        // ignore: missing_return
        itemBuilder: (context, index) {
            return Container(
              color: Colors.amber,
              margin: EdgeInsets.all(5),
              width: 50,
              child: Text(index.toString()),
            );
        },
      ),
    );
  }

  @override
  double get maxExtent => 110.0;

  @override
  double get minExtent => 110.0;

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

我尝试了什么

到目前为止,我已经尝试了NestedScrollView 的颤振文档中提到的SliverOverlapAbsorberSliverOverlapInjector,但没有任何好的结果。将TabBar 移动到SliverAppBar 底部属性也没有成功。

【问题讨论】:

  • 你找到什么了吗?

标签: flutter dart flutter-sliver


【解决方案1】:

什么对我有用:

我已经将整个SliverPersistentHeader (my _buildTabNavigation()) 包裹在SliverOverlapAbsorver 中:

 SliverOverlapAbsorber(
    handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
    sliver: _buildTabNavigation(),
 ),

这样做,您的列表将卡在 TabBar 下。然后,您可以将 NestedScrollView 正文包裹在 Container 中并在顶部添加边距。

类似这样的:

return NestedScrollView(
      controller: _scrollController,
      headerSliverBuilder: (context, value) {
        return [
          SliverOverlapAbsorber(
            handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
            sliver: _buildTabNavigation(),
          ),
        ];
      },
      body: Container(
        margin: EdgeInsets.only(top: 50),
        child: TabBarView(
          controller: _tabController,
          children: _tabs,
        ),
      ),
    );

【讨论】:

    猜你喜欢
    • 2020-05-24
    • 2019-04-24
    • 2020-09-22
    • 2019-02-20
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    • 1970-01-01
    • 2023-02-14
    相关资源
    最近更新 更多