向下滚动时隐藏的 Flutter TabBar 和 SliverAppBar [英] Flutter TabBar and SliverAppBar that hides when you scroll down

查看:38
本文介绍了向下滚动时隐藏的 Flutter TabBar 和 SliverAppBar的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个带有顶部应用程序栏和下方标签栏的应用程序.当您向下滚动时,栏应通过移出屏幕而隐藏(但选项卡应保留),当您向上滚动时,应用栏应再次显示.这种行为可以在 WhatsApp 中看到.请参阅

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

这是DefaultTabController的代码:

DefaultTabController(长度:2,孩子:新脚手架(正文:新的 NestedScrollView(headerSliv​​erBuilder:(BuildContext 上下文, bool innerBoxIsScrolled) {返回 <小部件>[新的 SliverAppBar(标题:文本(应用程序"),浮动:真实,固定:真实,snap: true,//<--- 如果我希望在向上滚动时显示应用程序栏,这是必需的底部:新的 TabBar(tabs: [ ... ],//<-- 总共 2 个 tab),),];},正文:新的 TabBarView(children: [ ... ]//<--- 数组项是一个 ListView),),),),

如果需要,完整代码在这个 GitHub 存储库.main.dart这里.

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

我还在 Flutter 的 GitHub 上发现了这个问题.(有人评论说他们正在等待 Flutter 团队解决这个问题.有没有可能没有解决方案?)

这是 flutter doctor -v 的输出:Pastebin.发现了某些问题,但从我了解到的情况来看,它们应该不会产生影响.

这有两个问题:

解决方案

更新 - Sliver App Bar Expanded

如果您想在有人向上滚动时立即看到 Sliver App Bar 展开,即不是一直滚动到顶部而是一点点,那么只需将 snap: false 更改为 snap:在代码中为真 :)


解决方案[修复所有点]

在谷歌、stackoverflow、github 问题、reddit 上冲浪了几个小时之后.我终于可以提出解决以下问题的解决方案:

  1. 标题被隐藏的 Sliver 应用栏,向下滚动后只有标签栏可见.当您到达顶部时,您会再次看到标题.

  2. MAJOR :当您在 Tab 1 & 中滚动时然后导航到选项卡 2,您将看不到任何重叠.Tab 2 的内容不会被 Sliver App bar 挡住.

  3. List 中最顶部元素的 Sliver Padding 为 0.

  4. 保留单个标签中的滚动位置

下面是代码,我会尝试稍微解释一下(dartpad预览):

import 'package:flutter/material.dart';void main() =>runApp(const MyApp());class MyApp 扩展 StatelessWidget {const MyApp({Key? key}) : super(key: key);static const String _title = 'Flutter 代码示例';@覆盖小部件构建(BuildContext 上下文){返回 const MaterialApp(标题:_title,主页:MyStatelessWidget(),);}}class MyStatelessWidget 扩展 StatelessWidget {const MyStatelessWidget({Key? key}) : super(key: key);@覆盖小部件构建(BuildContext 上下文){最终列表<字符串>_tabs = <String>['Tab 1', 'Tab 2'];返回 DefaultTabController(长度:_tabs.length,孩子:脚手架(正文:NestedScrollView(headerSliv​​erBuilder: (BuildContext context, bool innerBoxIsScrolled) {返回 <小部件>[SliverOverlapAbsorber(句柄:NestedScrollView.sliverOverlapAbsorberHandleFor(context),条子:SliverAppBar(标题: const Text('书籍'),浮动:真实,固定:真实,快照:错误,forceElevated:innerBoxIsScrolled,底部:TabBar(标签:_tabs.map((String name) => Tab(text: name)).toList(),),),),];},正文:TabBarView(孩子:_tabs.map((字符串名称){返回安全区域(顶部:假,底部:假,孩子:建造者(构建器:(BuildContext 上下文){返回自定义滚动视图(键:PageStorageKey(名称),条子:<小部件>[SliverOverlapInjector(句柄:NestedScrollView.sliverOverlapAbsorberHandleFor(context),),SliverPadding(填充:const EdgeInsets.all(8.0),条子:条子列表(委托:SliverChildBuilderDelegate((BuildContext 上下文,int 索引){返回 ListTile(标题:文本('项目 $index'),);},childCount: 30,),),),],);},),);}).toList(),),),),);}}

在 dartpad 中测试你想要的一切,一旦你没问题,那么让我们试着了解这里发生了什么.

大部分代码来自 NestedScrollView 的flutter 文档

他们在评论中提到的很好.我不是专家,所以我只想强调我认为解决了大部分问题的方法.

我认为这里有两件事很关键:

  1. SliverOverlapAbsorber &SliverOverlapInjector
  2. 使用SliverList 代替ListView

我们看到的额外空间或者sliver app bar消耗的空间和第一个列表项重叠的空间主要是通过使用以上两点来解决的.

为了记住标签的滚动位置,他们在 CustomScrollView 中添加了 PageStorageKey:

key: PageStorageKey(name),

name 只是一个字符串 ->'标签 1'

他们还在文档中提到我们可以使用 SliverFixedExtentList、SliverGrid,基本上是 Sliver 小部件.现在应该在需要时使用 Sliver 小部件.在其中一个 Flutter Youtube 视频(官方频道)中,他们提到 ListView、GridView 都是 Slivers 的高级实现.所以如果你想超级自定义滚动或外观行为,Slivers 是低级的东西.

如果我遗漏了什么或说错了,请在评论中告诉我.

I am trying to create an app with a top application bar and a tab bar below. When you scroll down, the bar should hide by moving off the screen (but tabs should stay), and when you scroll back up, the application bar should show again. This behaviour can be seen in WhatsApp. Please see this video for a demonstration. (Taken from Material.io). This is a similar behaviour, although the app bar and tab bar are hidden on scroll, so it is not exactly the behaviour I am looking for.

I have been able to achieve the autohiding, however, there are a few issues:

  1. I have to set the snap of the SliverAppBar to true. Without this, the application bar will not show when I scroll back up.

    Although this is works, it is not the behaviour I am looking for. I want the application bar to show smoothly (similar to WhatsApp) rather than coming into view even if you scroll very little.

    To clarify, when I scroll all the way down, even if I scroll up very little, the app bar should come into view. I do not want to have to scroll all the way up to see the app bar.

  2. When I scroll down and change tabs, a little bit of the content is cut out of view.

    Below is a GIF showing the behaviour:

    (See the part when I scroll down on the listView (tab1), then move back to tab2)

Here is the code for the 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
      ),
    ),
  ),
),

In case it is needed, the full code is in this GitHub repository. main.dart is here.

I also found this related question: Hide Appbar on Scroll Flutter?. However, it did not provide the solution. The same problems persist, and when you scroll up, the SliverAppBar will not show. (So snap: true is required)

I also found this issue on Flutter's GitHub. (Edit: someone commented that they are waiting for the Flutter team to fix this. Is there a possibility that there is no solution?)

This is the output of flutter doctor -v: Pastebin. Certain issues are found, but from what I have learned, they should not have an impact.

Edit: There are two issues for this:

解决方案

Update - Sliver App Bar Expanded

If you want to see Sliver App Bar expanded as soon as someone scrolls up i.e. not scrolling all the way to top but just little bit, Then just change snap: false to snap: true in code :)


Solution [Fixing All Points]

After surfing google, stackoverflow, github issues, reddit for hours. I could finally come up with a solution that addresses following:

  1. Sliver App bar with title getting hidden and only tab bar visible after scrolling down. You would see title again when you reach top.

  2. MAJOR : When you scroll in Tab 1 & then Navigate to Tab 2, you would not see any overlap. The content of Tab 2 will not get obstructed by Sliver App bar.

  3. Sliver Padding for top most element in List is 0.

  4. Preserves the Scroll Position in individual Tabs

below is the code, I would attempt to explain in a bit (dartpad preview) :

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: _title,
      home: MyStatelessWidget(),
    );
  }
}

class MyStatelessWidget extends StatelessWidget {
  const MyStatelessWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final List<String> _tabs = <String>['Tab 1', 'Tab 2'];
    return DefaultTabController(
      length: _tabs.length,
      child: Scaffold(
        body: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return <Widget>[
              SliverOverlapAbsorber(
                handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                sliver: SliverAppBar(
                  title: const Text('Books'),
                  floating: true,
                  pinned: true,
                  snap: false,
                  forceElevated: innerBoxIsScrolled,
                  bottom: TabBar(
                    tabs: _tabs.map((String name) => Tab(text: name)).toList(),
                  ),
                ),
              ),
            ];
          },
          body: TabBarView(
            children: _tabs.map((String name) {
              return SafeArea(
                top: false,
                bottom: false,
                child: Builder(
                  builder: (BuildContext context) {
                    return CustomScrollView(
                      key: PageStorageKey<String>(name),
                      slivers: <Widget>[
                        SliverOverlapInjector(
                          handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                        ),
                        SliverPadding(
                          padding: const EdgeInsets.all(8.0),
                          sliver: SliverList(
                            delegate: SliverChildBuilderDelegate(
                              (BuildContext context, int index) {
                                return ListTile(
                                  title: Text('Item $index'),
                                );
                              },
                              childCount: 30,
                            ),
                          ),
                        ),
                      ],
                    );
                  },
                ),
              );
            }).toList(),
          ),
        ),
      ),
    );
  }
}

Test it out all you want in dartpad, once you are fine then lets try to understand what is happening here.

Most of the code is from flutter documentation of NestedScrollView

They have mentioned very nicely in comments. I am no expert so I would just highlight what I think solved most of the issues.

I believe Two things are critical here:

  1. SliverOverlapAbsorber & SliverOverlapInjector
  2. Use of SliverList instead of ListView

Whatever extra space we were seeing or the space which sliver app bar consumed and first list item was overlapped was mainly resolved with the use of above two points.

To remember the scroll position of tabs, they added PageStorageKey inside CustomScrollView:

key: PageStorageKey<String>(name),

name is just a string -> 'Tab 1'

They also mentioned in docs that we can use SliverFixedExtentList, SliverGrid, basically Sliver widgets. Now use of Sliver widgets should be done when needed. In one of the Flutter Youtube videos (official channel) they mentioned that ListView, GridView, are all high level implementation of Slivers. So Slivers is low level stuff if you looking to super customize scrolling or appearance behaviour.

Please let me know in comments if I missed something or said wrong.

这篇关于向下滚动时隐藏的 Flutter TabBar 和 SliverAppBar的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆