android - 向下滚动时隐藏的 Flutter TabBar 和 SliverAppBar

标签 android dart flutter flutter-layout flutter-sliver

我正在尝试创建一个带有顶部应用程序栏和下方标签栏的应用程序。当您向下滚动时,该栏应通过移出屏幕来隐藏(但选项卡应保留),并且当您向上滚动时,应用程序栏应再次显示。这种行为可以在 WhatsApp 中看到。请看 this演示视频。 (取自 Material.io)。 This是类似的行为,虽然应用栏和标签栏在滚动时隐藏,所以这不是我正在寻找的行为。

我已经能够实现自动隐藏,但是有几个问题:

  1. 我必须将 SliverAppBarsnap 设置为 true。没有这个,当我向上滚动时,应用程序栏将不会显示。

    虽然这是可行的,但这不是我正在寻找的行为。我希望应用程序栏能够流畅地显示(类似于 WhatsApp),而不是即使滚动很少也会出现。

    澄清一下,当我一直向下滚动时,即使我向上滚动很少,应用栏也应该会出现。我不想一直向上滚动才能看到应用栏。

  2. 当我向下滚动并更改选项卡时,有一部分内容会被剪掉。

    下面是显示行为的 GIF:

    GIF demonstrating output

    (当我在listView(tab1)上向下滚动时查看该部分,然后移回tab2)

这是 DefaultTabController 的代码:

DefaultTabController(
  length: 2,
  child: new Scaffold(
    body: new NestedScrollView(
      headerSliverBuilder:
          (BuildContext context, bool innerBoxIsScrolled) {
        return <Widget>[
          new SliverAppBar(
            title: Text("Application"),
            floating: true,
            pinned: true,
            snap: true,    // <--- this is required if I want the application bar to show when I scroll up
            bottom: new TabBar(
              tabs: [ ... ],    // <-- total of 2 tabs
            ),
          ),
        ];
      },
      body: new TabBarView(
        children: [ ... ]    // <--- the array item is a ListView
      ),
    ),
  ),
),

如果需要,完整代码在此GitHub repository . main.darthere .

我还发现了这个相关问题:Hide Appbar on Scroll Flutter? .但是,它没有提供解决方案。同样的问题仍然存在,当您向上滚动时,SliverAppBar 将不会显示。 (所以 snap: true 是必需的)

我还找到了 this issue在 Flutter 的 GitHub 上。 (编辑:有人评论说他们正在等待Flutter团队解决这个问题。有没有可能没有解决方案?)

这是 flutter doctor -v 的输出:Pastebin .发现了一些问题,但据我所知,它们应该不会产生影响。

编辑:这有两个问题:

最佳答案

您需要使用 SliverOverlapAbsorber/SliverOverlapInjector ,以下代码适用于我 (Full Code ):

@override
  Widget build(BuildContext context) {
    return Material(
      child: Scaffold(
        body: DefaultTabController(
          length: _tabs.length, // This is the number of tabs.
          child: NestedScrollView(
            headerSliverBuilder:
                (BuildContext context, bool innerBoxIsScrolled) {
              // These are the slivers that show up in the "outer" scroll view.
              return <Widget>[
                SliverOverlapAbsorber(
                  // This widget takes the overlapping behavior of the SliverAppBar,
                  // and redirects it to the SliverOverlapInjector below. If it is
                  // missing, then it is possible for the nested "inner" scroll view
                  // below to end up under the SliverAppBar even when the inner
                  // scroll view thinks it has not been scrolled.
                  // This is not necessary if the "headerSliverBuilder" only builds
                  // widgets that do not overlap the next sliver.
                  handle:
                      NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                  sliver: SliverSafeArea(
                    top: false,
                    sliver: SliverAppBar(
                      title: const Text('Books'),
                      floating: true,
                      pinned: true,
                      snap: false,
                      primary: true,
                      forceElevated: innerBoxIsScrolled,
                      bottom: TabBar(
                        // These are the widgets to put in each tab in the tab bar.
                        tabs: _tabs.map((String name) => Tab(text: name)).toList(),
                      ),
                    ),
                  ),
                ),
              ];
            },
            body: TabBarView(
              // These are the contents of the tab views, below the tabs.
              children: _tabs.map((String name) {
                return SafeArea(
                  top: false,
                  bottom: false,
                  child: Builder(
                    // This Builder is needed to provide a BuildContext that is "inside"
                    // the NestedScrollView, so that sliverOverlapAbsorberHandleFor() can
                    // find the NestedScrollView.
                    builder: (BuildContext context) {
                      return CustomScrollView(
                        // The "controller" and "primary" members should be left
                        // unset, so that the NestedScrollView can control this
                        // inner scroll view.
                        // If the "controller" property is set, then this scroll
                        // view will not be associated with the NestedScrollView.
                        // The PageStorageKey should be unique to this ScrollView;
                        // it allows the list to remember its scroll position when
                        // the tab view is not on the screen.
                        key: PageStorageKey<String>(name),
                        slivers: <Widget>[
                          SliverOverlapInjector(
                            // This is the flip side of the SliverOverlapAbsorber above.
                            handle:
                                NestedScrollView.sliverOverlapAbsorberHandleFor(
                                    context),
                          ),
                          SliverPadding(
                            padding: const EdgeInsets.all(8.0),
                            // In this example, the inner scroll view has
                            // fixed-height list items, hence the use of
                            // SliverFixedExtentList. However, one could use any
                            // sliver widget here, e.g. SliverList or SliverGrid.
                            sliver: SliverFixedExtentList(
                              // The items in this example are fixed to 48 pixels
                              // high. This matches the Material Design spec for
                              // ListTile widgets.
                              itemExtent: 60.0,
                              delegate: SliverChildBuilderDelegate(
                                (BuildContext context, int index) {
                                  // This builder is called for each child.
                                  // In this example, we just number each list item.
                                  return Container(
                                      color: Color((math.Random().nextDouble() *
                                                      0xFFFFFF)
                                                  .toInt() <<
                                              0)
                                          .withOpacity(1.0));
                                },
                                // The childCount of the SliverChildBuilderDelegate
                                // specifies how many children this inner list
                                // has. In this example, each tab has a list of
                                // exactly 30 items, but this is arbitrary.
                                childCount: 30,
                              ),
                            ),
                          ),
                        ],
                      );
                    },
                  ),
                );
              }).toList(),
            ),
          ),
        ),
      ),
    );
  }

关于android - 向下滚动时隐藏的 Flutter TabBar 和 SliverAppBar,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55187332/

相关文章:

php - Android 的 Json 输出

html - 根据媒体加载特定的 .dart 文件

android - AppAuth 登录重定向适用于 iOS,但不适用于 Android

android - 任务 ':app:checkDebugAarMetadata' (Android Studio) 执行失败

android - 从我的 Android 应用打开 Chromecast YouTube 视频

amazon-web-services - 将图像作为 image/jpeg mime/type 从 Flutter 上传到 S3 存储桶

dart - dart:json使用Dart编程语言

flutter - 如何在Flutter中使用Hive将图像存储在localDB中?

flutter - 在 Flutter 中,当从屏幕 B 弹回屏幕 A 时调用哪个方法

java - 了解导致信号 11 (SIGSEGV)、代码 1 (SEGV_MAPERR) 几秒钟后出现错误的 Android 问题