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

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

问题描述

我正在尝试使用顶部的应用程序栏和下面的标签栏创建一个应用程序.当您向下滚动时,该栏应通过移离屏幕而隐藏(但选项卡应保留),而当您向上滚动时,该应用程序栏应再次显示.可以在WhatsApp中看到此行为.请参阅演示视频. (摘录自 Material.io ). 是类似的行为,尽管应用程序栏和选项卡栏在滚动中是隐藏的,所以这并不是我要查找的行为.

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. 我必须将SliverAppBarsnap设置为true.没有此功能,当我向上滚动时,应用程序栏将不会显示.

  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.

尽管这是可行的,但这不是我要寻找的行为.我希望应用程序栏显示流畅(类似于WhatsApp),而不是即使滚动很少也不会显示.

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.

当我向下滚动并更改标签时,一小部分内容被遮挡掉了.

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

下面是显示行为的GIF:

Below is a GIF showing the behaviour:

(当我向下滚动listView(tab1),然后移回tab2时,请参阅该部分)

(See the part when I scroll down on the listView (tab1), then move back to 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存储库中. main.dart此处.

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

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

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)

我还在Flutter的GitHub上发现了此问题. (,有人评论说他们正在等待Flutter团队解决此问题.是否有没有解决办法?)

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

这是flutter doctor -v的输出: Pastebin .发现了某些问题,但是据我了解,它们不会产生影响.

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.

有两个问题:

  • https://github.com/flutter/flutter/issues/29561 (closed)
  • https://github.com/flutter/flutter/issues/17518

推荐答案

您需要使用完整代码):

You need to use SliverOverlapAbsorber/SliverOverlapInjector, the following code works for me (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),
                  child: 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(),
            ),
          ),
        ),
      ),
    );
  }

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

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