没有 AppBar 的 Flutter TabBar

Posted

技术标签:

【中文标题】没有 AppBar 的 Flutter TabBar【英文标题】:Flutter TabBar Without AppBar 【发布时间】:2018-11-09 13:53:13 【问题描述】:

我正在尝试创建一个没有 AppBar 的选项卡式栏布局屏幕。我已经在此链接上提到了解决方案:how to create the tab bar without app bar in flutter? 但它对我不起作用。当我将 TabBar 放在 appbar: 参数中时,我的屏幕如下所示:

我的 TabBar 已移至状态栏下方的左上角,并且全部挤在一个角落。就好像它根本不存在一样。

当我使用 AppBar 类但只传递 bottom: 参数时,会发生以下情况:

TabBar 顶部有一个丑陋的空间,显然是为 AppBar 标题设计的。这是我的代码:

return new Scaffold(
      appBar: new TabBar(
        tabs: widget._tabs.map((_Page page)
          return Text(page.tabTitle);
        ).toList(),
        controller: _tabController,
        isScrollable: true,

      ),
      backgroundColor: Colors.white,
      body: new TabBarView(
          controller: _tabController,
          children: widget._tabs.map((_Page page)
            return new SafeArea(
                top:false,
                bottom: false,
                child: (page.page == Pages.cart?new CartHomeScreen():_lunchesLayout())
            );
          ).toList()
      ),
    );

我怎样才能让 TabBar 顶部没有那个空间?是否可以让两个选项卡项及其指示器拉伸并填充侧面空间?

【问题讨论】:

你能发布预期的结果截图吗? 【参考方案1】:

您的第一个屏幕截图实际上显示它运行良好 - 问题是“良好”并不是您所期望的。标签栏的默认文本颜色为白色,因此您的标签不会显示,而只是底线,这就是您在左上角看到的内容。此外,TabBar 已经是首选尺寸小部件,但它与 AppBar 的高度不同,因此如果您要这样做,它看起来不会像它。

这是一个使它看起来像应用栏的示例。 kToolbarHeight 与 AppBar 使用的常量相同。

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => new MyAppState();


class MyAppState extends State<MyApp> 
  @override
  Widget build(BuildContext context) 
    return new MaterialApp(
      title: 'msc',
      home: new DefaultTabController(
        length: 2,
        child: new Scaffold(
          appBar: new PreferredSize(
            preferredSize: Size.fromHeight(kToolbarHeight),
            child: new Container(
              color: Colors.green,
              child: new SafeArea(
                child: Column(
                  children: <Widget>[
                    new Expanded(child: new Container()),
                    new TabBar(
                      tabs: [new Text("Lunches"), new Text("Cart")],
                    ),
                  ],
                ),
              ),
            ),
          ),
          body: new TabBarView(
            children: <Widget>[
              new Column(
                children: <Widget>[new Text("Lunches Page")],
              ),
              new Column(
                children: <Widget>[new Text("Cart Page")],
              )
            ],
          ),
        ),
      ),
    );
  

结果如下:

编辑:

正如 cmets 和 this github issue 中所述,您还可以使用 AppBar 的 flexibleSpace 属性来保持标签栏的效果基本相同:

return new DefaultTabController(
  length: 3,
  child: new Scaffold(
    appBar: new AppBar(
      flexibleSpace: new Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          new TabBar(
            tabs: [
              new Tab(icon: new Icon(Icons.directions_car)),
              new Tab(icon: new Icon(Icons.directions_transit)),
              new Tab(icon: new Icon(Icons.directions_bike)),
            ],
          ),
        ],
      ),
    ),
  ),
);

【讨论】:

你能解释一下给tabbar一些提升或阴影效果看起来像一个appbar吗? @JayMungara 如果您查看the source code of appbar,您会发现他们使用Material 并设置了高程属性来显示阴影。你也可以这样做——用Material 替换容器并以相同的方式设置高度可能是最简单的。如果您对此有疑问,这似乎是一个新问题的候选者。 这个答案很好,但我更喜欢使用flexibleSpace 而不是 appBar (我的用例)。看看github.com/flutter/flutter/issues/17459#issuecomment-387984398 @Touch 感谢您的建议,我已将其添加到答案中,因为 cmets 有时会迷路 =D【参考方案2】:

按照下面的代码进行

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: MyHomePage(),
    );
  


class MyHomePage extends StatefulWidget 
  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin 
  TabController _tabController;

  @override
  void initState() 
    _tabController = new TabController(length: 2, vsync: this);
    super.initState();
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: Container(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Container(
              height: MediaQuery.of(context).size.height / 2,
              child: Center(
                child: Text(
                  "Tabbar with out Appbar",
                  style: TextStyle(
                      color: Colors.white, fontWeight: FontWeight.bold),
                ),
              ),
              color: Colors.blue,
            ),
            TabBar(
              unselectedLabelColor: Colors.black,
              labelColor: Colors.red,
              tabs: [
                Tab(
                  text: '1st tab',
                ),
                Tab(
                  text: '2 nd tab',
                )
              ],
              controller: _tabController,
              indicatorSize: TabBarIndicatorSize.tab,
            ),
            Expanded(
              child: TabBarView(
                children: [
                  Container(child: Center(child: Text('people'))),
                  Text('Person')
                ],
                controller: _tabController,
              ),
            ),
          ],
        ),
      ),
    );
  

查看结果

【讨论】:

这正是我所需要的。谢谢你。但记得处理掉tabController 谢谢!这就是 TabBar 没有 Scaffold 的答案。【参考方案3】:

我从 rmtmckenzie 获取代码,但只是为了创建一个没有材料应用程序的小部件

return new DefaultTabController(
      length: 3,
      child: new Scaffold(
        appBar: new PreferredSize(
          preferredSize: Size.fromHeight(kToolbarHeight),
          child: new Container(
            height: 50.0,
            child: new TabBar(
              tabs: [
                Tab(icon: Icon(Icons.directions_car,color: Colors.grey,)),
                Tab(icon: Icon(Icons.directions_transit,color: Colors.grey)),
                Tab(icon: Icon(Icons.directions_bike,color: Colors.grey)),
              ],
            ),
          ),
        ),
        body: TabBarView(
          children: [
            Icon(Icons.directions_car),
            Icon(Icons.directions_transit),
            Icon(Icons.directions_bike),
          ],
        ),
      ),
    ); 

【讨论】:

我得到“底部溢出 2 个像素” 没有scaffold()有没有办法使用tabbar?【参考方案4】:

您不需要使用appBar 属性:

Scaffold(
  body: Column(
    children: [
      TabBar( // <-- Your TabBar
        tabs: [
          Tab(icon: Icon(Icons.camera)),
          Tab(icon: Icon(Icons.settings)),
        ],
      ),
      Expanded(
        child: TabBarView( // <-- Your TabBarView
          children: [
            Container(color: Colors.blue),
            Container(color: Colors.red),
          ],
        ),
      ),
    ],
  ),
)

【讨论】:

【参考方案5】:

只使用flexiblespace 道具而不是appBar 的底部道具。 全部在 safeArea 小部件中,您可以添加一些垂直填充。

home: SafeArea(
        top: true,
        child: Padding(
          padding: const EdgeInsets.only(top: 10.0),
          child: DefaultTabController(
            length: 3,
            child: Scaffold(
              appBar: AppBar(
                flexibleSpace: TabBar(
                  tabs: [
                    Tab(icon: Icon(Icons.1)),
                    Tab(icon: Icon(Icons.2)),
                    Tab(icon: Icon(Icons.3)),
                  ],
                ),
              ),
              body: TabBarView(
                children: [
                  Icon(Icons.1),
                  Icon(Icons.2),
                  Icon(Icons.3),
                ],
              ),
            ),
          ),
        ),
      )

【讨论】:

【参考方案6】:

只需在AppBar 小部件中使用toolbarHeight 属性和kMinInteractiveDimension 值,如下所示:

Scaffold(
  appBar: AppBar(
    toolbarHeight: kMinInteractiveDimension,
    bottom: TabBar(
      controller: _tabController,
      tabs: [], // your tab bars
    ),
  ),
  body: TabBarView(
    controller: _tabController,
    children: [], // your tab views
  ),
);

【讨论】:

【参考方案7】:

appBar属性中直接使用TabBar

@override
Widget build(BuildContext context) 
  return DefaultTabController(
    length: 2,
    child: Scaffold(
      appBar: TabBar(
        tabs: [
          Tab(icon: Icon(Icons.call)),
          Tab(icon: Icon(Icons.message)),
        ],
      ),
      body: TabBarView(
        children: [
          Center(child: Text('Call')),
          Center(child: Text('Message')),
        ],
      ),
    ),
  );

【讨论】:

非常感谢,这非常有效。而且很简单【参考方案8】:

很简单,您只需提供height 到您的TabView

这是一个简单的例子。您可以使用DefaultTabControllerTabController。为了完全可定制,我将使用TabController

_getTabBar函数返回选项卡列表。

  TabBar _getTabBar() 
    return TabBar(
      tabs: <Widget>[
        Tab(icon: Icon(Icons.home, color: Colors.redAccent)),
        Tab(icon: Icon(Icons.settings, color: Colors.redAccent)),
      ],
      controller: tabController,
    );
  

_getTabBarView 将小部件列表作为输入参数并创建一个选项卡视图。

  TabBarView _getTabBarView(tabs) 
    return TabBarView(
      children: tabs,
      controller: tabController,
    );
  

SafeArea 用于将内容移动到状态栏下方。 _getTabBarViewContainer 包裹,这样我们就可以分配一个高度。这是分配高度的一种方式,您也可以使用其他方式。

这里是完整的代码,包裹在MaterialApp 中,你就有了一个完全可定制的标签栏和视图。生命周期方法用于创建和处置TabController

import 'package:flutter/material.dart';

class TabControllerScreen extends StatefulWidget 

  @override
  _TabControllerScreenState createState() => _TabControllerScreenState();


class _TabControllerScreenState extends State<TabControllerScreen> with SingleTickerProviderStateMixin 
  TabController tabController;

  @override
  void initState() 
    super.initState();
    tabController = TabController(length: 2, vsync: this);
  

  @override
  void dispose() 
    tabController.dispose();
    super.dispose();
  

  TabBar _getTabBar() 
    return TabBar(
      tabs: <Widget>[
        Tab(icon: Icon(Icons.home, color: Colors.redAccent)),
        Tab(icon: Icon(Icons.settings, color: Colors.redAccent)),
      ],
      controller: tabController,
    );
  

  TabBarView _getTabBarView(tabs) 
    return TabBarView(
      children: tabs,
      controller: tabController,
    );
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: <Widget>[
            _getTabBar(),
            Container(
              height: 100,
              child: _getTabBarView(
                <Widget>[
                  Icon(Icons.home),
                  Icon(Icons.settings),
                ],
              ),
            )
          ],
        ),
      ),
    );
  


【讨论】:

【参考方案9】:

根据 CopsOnRoad 的回答,我创建了一个隐藏的 TabBar,如下所示:

Scaffold(
  body: Column(
    children: [
      TabBar( // <-- Hidden TabBar
        indicator: BoxDecoration(),
        tabs: [
          SizedBox.shrink(),
          SizedBox.shrink(),
        ],
      ),
      Expanded(
        child: TabBarView( // <-- Your TabBarView
          children: [
            Container(color: Colors.blue),
            Container(color: Colors.red),
          ],
        ),
      ),
    ],
  ),
)

【讨论】:

【参考方案10】:

尝试将TabBar 放入AppBarflexibleSpace 属性中。

【讨论】:

以上是关于没有 AppBar 的 Flutter TabBar的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:AppBar 背景图片

Flutter:如何仅在滚动后显示 AppBar?

appbar下如何滑动页面flutter

Flutter -- 基础组件AppBar 的使用

如果包含 endDrawer,Flutter 2.0 appbar 后退按钮会消失

Flutter appBar后退按钮不出现