如何在Flutter的SliverAppBar中添加Tabbar?

4

我们能在SliverAppBar中添加TabBar吗?

由于SliverAppBar有bottom属性,我认为我们可以在SliverAppBar中添加TabBar,但问题是TabBar需要DefaultTabbarController,而DefaultTabbarController仅适用于Material Widget,而SliverAppBar仅适用于Scaffold Body,而不适用于我的appbar,但我需要我的Scaffold Body具有TabView。这是什么原因?


你想要实现什么并不清楚,请发布代码。 - ap14
我想做的是制作一个应用栏,当用户滚动时会自动隐藏。我在网上找到了使用SliverAppBar可以实现这个功能,但现在我想在我的SliverAppBar中添加Tabbar。因此,当用户滚动时,它将隐藏SliverAppBar并只保留TabBar,就像WhatsApp中一样。 - Theodorus Agum Gumilang
请查看此答案 https://dev59.com/alUL5IYBdhLWcg3wYXJ8#50853287 - marp
3个回答

9

是的,你可以使用NestedScrollView在SliverAppBar中添加Tabbar。你还可以自定义AppBar,使其类似于WhatsApp中的浮动AppBar。请确保在NestedScrollView中添加floatHeaderSlivers: true。

可工作代码链接
import 'dart:math';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: CustomSliverAppbar(),
    );
  }
}

class CustomSliverAppbar extends StatefulWidget {
  @override
  _CustomSliverAppbarState createState() => _CustomSliverAppbarState();
}

class _CustomSliverAppbarState extends State<CustomSliverAppbar>
    with SingleTickerProviderStateMixin {
  TabController _tabController;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        floatHeaderSlivers: true,
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverAppBar(
              title: Text(
                "WhatsApp type sliver appbar",
              ),
              centerTitle: true,
              pinned: true,
              floating: true,
              bottom: TabBar(
                  indicatorColor: Colors.black,
                  labelPadding: const EdgeInsets.only(
                    bottom: 16,
                  ),
                  controller: _tabController,
                  tabs: [
                    Text("TAB A"),
                    Text("TAB B"),
                  ]),
            ),
          ];
        },
        body: TabBarView(
          controller: _tabController,
          children: [
            TabA(),
            const Center(
              child: Text('Display Tab 2',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }
}

class TabA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scrollbar(
      child: ListView.separated(
        separatorBuilder: (context, child) => Divider(
          height: 1,
        ),
        padding: EdgeInsets.all(0.0),
        itemCount: 30,
        itemBuilder: (context, i) {
          return Container(
            height: 100,
            width: double.infinity,
            color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
          );
        },
      ),
    );
  }
}

enter image description here


请勿仅回答链接。请考虑在此处发布代码。 - Manos Kounelakis
@ManosKounelakis 对不起,我没有添加代码,这是我的第二个答案。以后会记得添加代码。 - Kapil Sahu

4
您可以使用CustomScrollView包装SilverAppBar(其中底部有TabBar)和SilverFillRemaining(包裹TabBarView)。然后将CustomScrollView设置为Scaffold的正文。为此,您需要创建一个TabController
完整示例请参见:
import 'package:flutter/material.dart';

class SilverAppBarWithTabBarScreen extends StatefulWidget {
  @override
  _SilverAppBarWithTabBarState createState() => _SilverAppBarWithTabBarState();
}

class _SilverAppBarWithTabBarState extends State<SilverAppBarWithTabBarScreen>
    with SingleTickerProviderStateMixin {
  TabController controller;

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

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new CustomScrollView(
        slivers: <Widget>[
          new SliverAppBar(
            title: Text("Silver AppBar With ToolBar"),
            pinned: true,
            expandedHeight: 160.0,
            bottom: new TabBar(
              tabs: [
                new Tab(text: 'Tab 1'),
                new Tab(text: 'Tab 2'),
                new Tab(text: 'Tab 3'),
              ],
              controller: controller,
            ),
          ),
          new SliverFillRemaining(
            child: TabBarView(
              controller: controller,
              children: <Widget>[
                Text("Tab 1"),
                Text("Tab 2"),
                Text("Tab 3"),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

1
嗨,感谢您的回答,我有一个问题。为什么当我在我的TabbBarView中滚动时,我的应用栏不会自动隐藏呢?难道SliverAppBar不是为了实现这些目的而创建的吗?或者我需要在这里添加一些内容吗? - Theodorus Agum Gumilang
现在的问题是我的应用栏在列表视图滚动时不起作用,但当我的选项卡视图只有文本时它工作得很好。另外,我能否仅隐藏我的应用栏而不隐藏底部(选项卡栏)? - Theodorus Agum Gumilang

1
我能够达成你所要求的目标,但我只有一个问题,即当我在TabView中添加滚动小部件时,它不能产生所需的结果。
我已在GitHub上发布了 一个问题
这是我的代码:
class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => HomePageState();
}

class HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  TabController tabController;
  @override
  void initState() {
    super.initState();
    tabController = TabController(length: 2, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    Color tabColor = Theme.of(context).primaryColorDark;
    TextStyle tabStyle = TextStyle(color: tabColor);
    return SafeArea(
      child: Scaffold(
        body: CustomScrollView(
          slivers: <Widget>[
            SliverAppBar(
              title: Text("AppBar"),
              floating: true,
              primary: true,
              pinned: false,
            ),
            SliverFillRemaining(
              child: Scaffold(
                appBar: TabBar(
                controller: tabController,
                tabs: <Widget>[
                  Tab(
                    child: Text(
                      'Tab1',
                     style: tabStyle,
                    ),
                  ),
                  Tab(
                    child: Text(
                      'Tab2',
                    style: tabStyle,
                    ),
                  ),
                ],
              ),
                body: TabBarView(
                  controller: tabController,
                  children: <Widget>[
                    Scaffold(
                      body: Text('Tab One'),
                    ),
                    Scaffold(
                      body: Text('Tab Two'),
                    ),
                  ],
                ),
              ),
              ),
          ],
        ),
      ),
    );
  }
}

Emulator Screen


谢谢你的回答,是的,正如我在另一个答案中提到的,我不知道为什么当我返回listview时它不起作用。希望他们能尽快修复它。 - Theodorus Agum Gumilang

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