如何将两个类似浮动的 Sliver 放入具有类似 SliverAppBar 行为的 CustomScrollView 中?

ven*_*nir 5 dart flutter

假设我们有以下简单的内容CustomScrollView

  • ASliverAppBar
  • 小工具1
  • 小工具2
  • 小工具3

前面的示例必须能够满足以下要求:

  1. 必须SliverAppBarfloating: true,所以当我们再次向上滚动时它就会出现。够简单的;
  2. 小部件 1 和小部件 2 的行为应该类似于 SliverAppBar. 让我详细说明一下:
    1. 这两个小部件不是AppBar,这意味着不应在它们上绘制任何抽屉,不应保留任何插图或其他任何内容。它们是普通的小部件,我想在那里使用我自己的实现;
    2. 当我向下滚动时,我希望这两个小部件能够滚动并消失……同样,很简单;
    3. 但是,当我再次向上滚动时,这两个小部件应该在屏幕上一个接一个地出现,如下所示:SliverAppBar -> Widget 1 -> Widget 2,而不需要滚动到顶部,即行为就像SliverAppBar使用选项一样floating:true他们必须遵守上述滚动顺序。
  3. 小部件 1 和小部件 2是可隐藏的:参见下面的示例;
  4. 小部件 3 只是实际内容,其行为类似于普通的可滚动小部件。

这是我现在拥有的代码。我尝试用不同的方法实现 Widget 1 和 Widget 2:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatefulWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      var showGreen = false;
      var showRed = false;
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            drawer: const Drawer(),
            body: Center(
              child: CustomScrollView(
                slivers: [
                  SliverAppBar(
                    title: const Text('My app bar'),
                    floating: true,
                    actions: [
                      IconButton(
                        icon: const Icon(Icons.grade_outlined),
                        tooltip: 'Add',
                        onPressed: () {
                          setState(() {
                            showGreen = !showGreen;
                          });
                        },
                      ),
                      IconButton(
                        icon: const Icon(Icons.grade),
                        tooltip: 'Remove',
                        onPressed: () {
                          setState(() {
                            showRed = !showRed;
                          });
                        },
                      ),
                    ],
                  ),
                  if (showGreen)
                    SliverAppBar(
                      toolbarHeight:
                          200, // I don't want hard-coded values in here!!
                      leadingWidth: 0,
                      titleSpacing: 0,
                      floating: true,
                      title: Container(
                        height: 200, // I don't want hard-coded values in here!!
                        color: Colors.green,
                        child: const Placeholder(),
                      ),
                    ),
                  SliverPersistentHeader(
                    floating: true,
                    delegate: MyDelegate(showRed),
                  ),
                  SliverToBoxAdapter(
                    child: Container(
                      color: Colors.blue,
                      height: 2500,
                      child: const Placeholder(),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    
    class MyDelegate extends SliverPersistentHeaderDelegate {
      final bool showContents;
      const MyDelegate(this.showContents) : super();
    
      @override
      Widget build(
          BuildContext context, double shrinkOffset, bool overlapsContent) {
        return showContents
            ? Container(
                color: Colors.red,
                child: const Text(
                  "my contents",
                  style: TextStyle(fontSize: 36),
                ),
              )
            : const SizedBox.shrink();
      }
    
      @override
      // But this is not what I want! I want the maxExtent to be as much as the build() method needs!
      // I DO NOT want to hard code heights here!
      double get maxExtent => 200;
    
      @override
      // For some reason... the content isn't disappearing right away and instead I get an overflow error?
      double get minExtent => 0;
    
      @override
      bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
        return false;
      }
    }
Run Code Online (Sandbox Code Playgroud)

这是我到目前为止所尝试过的,但没有运气:

  • 我尝试将 Widget 1 和 Widget 2 实现为两个 SliverAppBar,但是:
    • 删除抽屉很麻烦(参见示例:您必须强制标题展开):它实际上存在但没有显示,还是框架完全删除了该按钮?
    • 我想让这些小部件的高度不受限制(不,我不想固定任何东西,我需要它们的高度以某种方式适应内容Flexible);插入除占位符之外的任何内容都会破坏 AppBar,因为我无法事先知道我的内容需要多少!
    • 这仍然感觉像是一个老套的解决方案。从语义上讲,不想有三个 (!)SliverAppBars
    • 这似乎是“尽可能接近”的行为,但是
  • 我尝试过SliverListsSliverGrids...使用“开箱即用”小部件不可能重现该行为;
  • 我尝试使用 aSliverPersistentHeader及其SliverPersistentHeaderDelegate,但没有运气。我似乎无法理解如何使用 maxExtent 和 minExtent 参数一致地重现我想要的内容:我不太关心固定值(正如您在示例中看到的,它只是不能很好地工作)。而且,这种floating行为根本不存在。

我对这个很迷失。有人能回答这个问题吗?