如何创建一个有边界且可滚动的TabBarView

17

我需要在Flutter中实现以下布局。

Layout

当用户滚动时,我希望整个布局都会滚动(隐藏标题和选项卡栏)。但是,我不能将TabBarView嵌套在ListView中,因为TabBarView没有有限的高度,而ListViews不会向其子项提供有限的高度。

我已经看到了这些问题,但所有这些问题都对此用例没有令人满意的答案:

3个回答

21

输入图像描述

class SliverWithTabBar extends StatefulWidget {
  @override
  _SliverWithTabBarState createState() => _SliverWithTabBarState();
}

class _SliverWithTabBarState extends State<SliverWithTabBar> with SingleTickerProviderStateMixin {
  TabController controller;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return [
            SliverAppBar(
              pinned: false,
              backgroundColor: Colors.white,
              flexibleSpace: FlexibleSpaceBar(
                collapseMode: CollapseMode.pin,
                background: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Container(
                      height: 200.0,
                      width: double.infinity,
                      color: Colors.grey,
                      child: FlutterLogo(),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(10.0),
                      child: Text(
                        'Business Office',
                        style: TextStyle(fontSize: 25.0),
                        textAlign: TextAlign.left,
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(10.0),
                      child: Text(
                        'Open now\nStreet Address, 299\nCity, State',
                        style: TextStyle(fontSize: 15.0),
                        textAlign: TextAlign.left,
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.only(right: 10.0),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: <Widget>[
                          Icon(Icons.share),
                          Padding(
                            padding: const EdgeInsets.only(left: 10.0),
                            child: Icon(Icons.favorite),
                          ),
                        ],
                      ),
                    )
                  ],
                ),
              ),
              expandedHeight: 380.0,
              bottom: TabBar(
                indicatorColor: Colors.black,
                labelColor: Colors.black,
                tabs: [
                  Tab(text: 'POSTS'),
                  Tab(text: 'DETAILS'),
                  Tab(text: 'FOLLOWERS'),
                ],
                controller: controller,
              ),
            )
          ];
        },
        body: ListView.builder(
          itemCount: 100,
          itemBuilder: (BuildContext context, int index) {
            return Card(
              color: index % 2 == 0 ? Colors.blue : Colors.green,
              child: Container(
                alignment: Alignment.center,
                width: double.infinity,
                height: 100.0,
                child: Text(
                  'Flutter is awesome',
                  style: TextStyle(fontSize: 18.0),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

您应该寻找Sliver小部件来实现NestedScrollView

这样就可以使用headerSliverBuilder属性来添加一些标题,当滚动body小部件时,您可能会隐藏或固定在屏幕顶部,在这个特定的示例中,使用了ListView

您可能希望查看RenderSliver文档。


2
非常感谢,这个完美地解决了我的问题!我一直觉得slivers有点难以理解,但是你的例子让我清晰明了。我希望文档也能像这样提供例子! - Ozymas
谢谢兄弟。但是我无法滚动选项卡?我已经设置了isScrollable: true。 - Muhammad Imanudin
如何将小部件从左向右或从右向左滑动? - Vinoth Vino
为了使TabBar的选项卡完美工作,可以在NestedScrollView的body中使用TabBarView。 - Darshan Rathod
4
在我看来,实际上这是不正确的!你把一个ListView放在了NestedScrollView的主体中。除非你有一个TabBarView,否则头部区域的TabBar是无用的。 但是,如果你把TabBarView放在主体中->你会遇到这些问题: TabBarView需要有限高度!(它不会自动占据剩余的空间,即使你不能使用Extended widget在列中扩展高度)。 每个选项内容的长度(高度)不同,所以你甚至不能选择一个特定的高度。 - Shamshun

3

在Miguel Ruvio的回答基础上,将主体中的ListView替换为TabBarView即可实现D.R.评论中提到的几乎所有功能。但是当我的一个小部件被包含在一个列中时,我遇到了一些溢出问题。根据这个示例,将其替换为ListView即可解决。

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: Tabs()));
}

class Tabs extends StatefulWidget {
  @override
  _RoomTabsState createState() => _RoomTabsState();
}

class _RoomTabsState extends State<Tabs> with TickerProviderStateMixin {
  var _scrollViewController;
  var _tabController;

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

  @override
  Widget build(BuildContext context) {
    return NestedScrollView(
      controller: _scrollViewController,
      headerSliverBuilder: (context, bool) => [
        SliverAppBar(
          bottom: TabBar(
            controller: _tabController,
            tabs: [
              Tab(text: "All"),
              Tab(text: "Living room"),
            ],
          ),
        ),
      ],
      body: TabBarView(
        controller: _tabController,
        children: [
          ListView(children: [
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
            Text("test"),
          ]),
          Text("test"),
        ],
      ),
    );
  }
}


来自于这个Github issue的讨论,涉及到IT技术相关内容。

1
它起作用了。将每个子项包装在“ListView”中可以消除溢出。 - Michał Dobi Dobrzański
太棒了!将Tabview的子项包裹在Listview中可以解决问题。注意:Listview中的列和行应设置为MainAxisSize.min。 - Godwin Mathias

2
请尝试下面的代码:
     NestedScrollView(
      headerSliverBuilder: (context, value) {
        return [
          SliverToBoxAdapter(
              child: Header()
          ),
          SliverToBoxAdapter(
            child: TabBar(
              controller: _controller,
              tabs: [
                Tab(icon: Icon(Icons.x)),
                Tab(icon: Icon(Icons.y)),
                Tab(icon: Icon(Icons.z)),
              ],
            ),
          ),
        ];
      },
    body: Container(
      child: TabBarView(
        controller: _controller,
        children: <Widget>[
          page1(),
          page2(),
          page3(),
        ],
      ),
    ),
  )


在调试运行时会出现溢出情况 :( - Michał Dobi Dobrzański

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接