Flutter中的透明底部导航栏

43

我是Flutter的新手。我正在尝试实现这个UI:

screenshot

我还没有找到在Flutter中创建透明底部导航栏的有用解决方案。

我已经尝试使用

BottomNavigationBarItem(
        backgroundColor: Colors.transparent,
        icon: e,
        activeIcon: _activeIcons[_index],
        title: Text(
          title[_index],
          style: AppStyle.tabBarItem,
        ),
      )

但这似乎不起作用。请帮忙。


1
我认为这可能是你需要的:https://dev59.com/81UM5IYBdhLWcg3wF9TZ - magicleon94
1
我刚刚尝试了一下,但它没有起作用。你能否请给这个问题点赞?我是 Stack Overflow 的新手,人们正在对它进行负面评价。 - DaIOSGuy
1
深入思考后,我意识到这样做不能得到期望的结果,因为两个女孩的图像会在导航栏上方。我建议使用一个Stack,将两个女孩的图像作为底层(堆栈的底部),并使用全屏的Column,将MainAxisSize设置为MainAxisSize.max,将MainAxisAlignment设置为MainAxisAlignment.end。我可以写一个答案,但现在无法测试,所以我更喜欢写一个评论。希望能有所帮助。 - magicleon94
@SnakeyHips 这是带有透明背景的答案输出 https://imgur.com/a/0DzIfXb。可能是因为 bottomNavigationbar 下面没有任何东西,所以我们看不到任何内容。 - DaIOSGuy
@magicleon94 很有趣,所以你的意思是我应该将堆栈作为列的子元素? - DaIOSGuy
显示剩余6条评论
14个回答

86

对我来说,所给出的答案都没有用,但我发现了非常重要的一点:您必须添加属性extendBody: true

如果为true,并且指定了bottomNavigationBar或persistentFooterButtons,则body会延伸到Scaffold的底部,而不仅仅是延伸到bottomNavigationBar或persistentFooterButtons的顶部。

当bottomNavigationBar具有非矩形形状(如CircularNotchedRectangle)时,此属性通常很有用,该形状在栏的顶边缘添加了大小为FloatingActionButton的缺口。在这种情况下,指定extendBody:true可确保Scaffold的主体通过底部导航栏的缺口可见

连同 backgroundColor: Color(0x00ffffff),

NB:以0x开头的颜色值是十六进制的ARGB值(0xAARRGGBB),因此ffffff前面的00表示最大透明度,您可以通过将00增加到ff(十六进制中的255)来增加不透明度。

enter image description here

完整的代码示例:

import 'package:flutter/material.dart';

class NavigationBar extends StatefulWidget {
  static int _selectedIndex = 0;

  @override
  NavigationBarState createState() => NavigationBarState();
}

class NavigationBarState extends State<NavigationBar> {
  void _onItemTapped(int index) {
    setState(() {
      NavigationBar._selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {

    return BottomNavigationBar(
        elevation: 0, // to get rid of the shadow
        currentIndex: NavigationBar._selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
        backgroundColor: Color(0x00ffffff), // transparent, you could use 0x44aaaaff to make it slightly less transparent with a blue hue.
        type: BottomNavigationBarType.fixed,
        unselectedItemColor: Colors.blue,
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.grade),
            label: 'Level',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.notifications),
            label: 'Notification',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            label: 'Achievements',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.settings),
            label: 'Settings',
          ),
        ]
    );
  }

  @override
  Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

那么你在 MaterialApp 中的返回位置:

return MaterialApp(
                home: Scaffold(
                    extendBody: true, // very important as noted
                    bottomNavigationBar: NavigationBar(), // here you make use of the transparent bar.
                    body: Container(
                        decoration: BoxDecoration(
                            image: DecorationImage(
                                image: ExactAssetImage("assets/background.png"), // because if you want a transparent navigation bar I assume that you have either a background image or a background color. You need to add the image you want and also authorize it in pubspec.yaml
                                fit: BoxFit.fill
                            ),
                        ),
                        child: Container(
                              // the body of your app
                        ),
                    ),
                ),
            );
        }
    }

我希望这会有所帮助。


3
这应该成为官方被接受的答案!它很简单。不要使用双层或三层脚手架,哈哈 - Peter Poliwoda
3
在我看来,这是最佳答案。另外,如果你的小部件树中有SafeArea小部件,你可能需要将bottom设置为false。(SafeArea( bottom:false, child:...) - goldensoju
1
最佳答案,我可以将其与另一个导航栏插件一起使用。 - M. Syamsul Arifin
1
在代码上方的简短信息中添加 type: BottomNavigationBarType.fixed,,谢谢。 - Mamrezo
1
这太棒了,只需要添加一个属性,一切都顺理成章了,肯定是正确的答案,没有任何巧妙的手段,也没有绕过的方法,干得好! - undefined

22

使用评论中讨论的堆栈方法尝试:

  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Stack(
          children: <Widget>[
            Container(
              decoration: BoxDecoration(
                image: DecorationImage(
                    image: AssetImage('assets/background.jpg'),
                    fit: BoxFit.cover),
              ),
            ),
            Align(
                alignment: Alignment.bottomCenter,
                child: Theme(
                    data: Theme.of(context)
                        .copyWith(canvasColor: Colors.transparent),
                    child: BottomNavigationBar(
                      currentIndex: 0,
                      items: [
                        BottomNavigationBarItem(
                            icon: Icon(Icons.home), title: Text('Home')),
                        BottomNavigationBarItem(
                            icon: Icon(Icons.home), title: Text('Home')),
                        BottomNavigationBarItem(
                            icon: Icon(Icons.home), title: Text('Home'))
                      ],
                    ))),
          ],
        ),
      ),
    );
  }

底部透明导航栏

编辑: BottomNavigationBar 内置了一个不可更改的高度为 8.0 的阴影效果,导致出现奇怪的阴影。如果您想要去除这个阴影效果,您可以像下面这样实现自己的底部导航栏:

Align(
                alignment: Alignment.bottomCenter,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                  IconButton(icon: Icon(Icons.home, color: Theme.of(context).accentColor,), onPressed: () {},),
                  IconButton(icon: Icon(Icons.home, color: Theme.of(context).accentColor,), onPressed: () {},),
                  IconButton(icon: Icon(Icons.home, color: Theme.of(context).accentColor,), onPressed: () {},),
                ],)),

transparent nav demo 2


非常感谢,救了我的一天。你能帮忙回答另一个问题吗?我想让ListItem占用全部可用的高度和宽度,但是使用Expanded似乎不起作用。 - DaIOSGuy
让我为此创建另一个问题。 - DaIOSGuy
没问题,但我想你可能想使用其他东西而不是ListTile,因为据我所知它们有固定的高度。 - soupjake
哪种其他的集合结构提供了这样的功能? - DaIOSGuy
一个 ListView 不一定要由 ListTile 组成,子元素可以是任何类型的 Widget,因此我想你可以使用 Row 或创建自己版本的 ListTile - soupjake

10

这是我实现它的方式

    return Scaffold(
      body: Builder(
        builder: (context) => Container(
          decoration: bgAuthenticationDecoration(),
          child: _HomeBodyWidget(_currentIndex),
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(items: <BottomNavigationBarItem>[
        BottomNavigationBarItem(icon: Icon(Icons.home,),title: Container()),
        BottomNavigationBarItem(icon: Icon(Icons.message),title: Container()),
        BottomNavigationBarItem(icon: Icon(Icons.list),title: Container()),
        BottomNavigationBarItem(icon: Icon(Icons.favorite),title: Container()),
        BottomNavigationBarItem(icon: Icon(Icons.supervised_user_circle),title: Container()),
      ],
      backgroundColor:Colors.black.withOpacity(0.1),),
      extendBodyBehindAppBar: true,
      extendBody: true,
    );

接下来您需要在应用程序主题中将画布颜色设置为透明。

canvasColor: Colors.transparent

希望这能帮到您。 愉快编码!

5

在新版Flutter(1.2.1)中,有一个与海拔高度相关的参数,您可以将其设置为 elevation: 0.0


3
这是我的方法:
Stack(
      children: <Widget>[
        Container(
          decoration: BoxDecoration(
            image: DecorationImage(
              fit: BoxFit.fill,
              image: NetworkImage("https://cdn.pixabay.com/photo/2018/09/17/16/24/cat-3684184_960_720.jpg")
            )
          ),
        ),
        Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.end,
          children: <Widget>[
            Theme(
              data: Theme.of(context).copyWith(canvasColor: Colors.transparent),
              child: BottomNavigationBar(
                items: [
                  BottomNavigationBarItem(
                      icon: Icon(Icons.photo_camera), title: Text("Test")),
                  BottomNavigationBarItem(
                      icon: Icon(Icons.photo_camera), title: Text("Test")),
                ],
              ),
            )
          ],
        )
      ],
    );

这将使用背景图像(底层)和在其内容对齐方式为end的列内的底部导航栏来填充整个屏幕(图像纯粹是琐碎的,但您会得到这个意思)。
为了完成目的,我将在下面粘贴我在原问题的评论中给出的解释。 深入思考后,我意识到这将无法实现所需的相同效果,因为两个女孩的图像将位于NavigationBar上方。建议使用一个Stack,其中两个女孩的图像是底层(堆栈的底部),并且设置MainAxisSize.max和MainAxisAlignment.end的全屏幕Column。我可以把它写在答案中,但我现在无法测试它,所以我更愿意写一个评论。希望有所帮助
更新: 之前的解决方案仍然具有navbar阴影。由于我使用了Row自己实现了BottomNavigationBar,因此此屏幕(小部件)的构建方法不会产生阴影。
@override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Container(
          decoration: BoxDecoration(
              image: DecorationImage(
                  fit: BoxFit.fill,
                  image: NetworkImage(
                      "https://media.idownloadblog.com/wp-content/uploads/2016/04/macinmac-portrat-splash.jpg"))),
        ),
        Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.end,
          children: <Widget>[
            Row(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                GestureDetector(
                    onTap: () {
                      print("Tap!");
                    },
                    child: Icon(
                      Icons.photo_camera,
                      size: 50,
                    )),
                GestureDetector(
                    onTap: () {
                      print("Tap!");
                    },
                    child: Icon(
                      Icons.photo_camera,
                      size: 50,
                    )),
                GestureDetector(
                    onTap: () {
                      print("Tap!");
                    },
                    child: Icon(
                      Icons.photo_camera,
                      size: 50,
                    )),
                GestureDetector(
                    onTap: () {
                      print("Tap!");
                    },
                    child: Icon(
                      Icons.photo_camera,
                      size: 50,
                    )),
              ],
            )
          ],
        )
      ],
    );

这是我手机上的截图:

Example

奖励

您可以通过调用以下方法实现全屏:

SystemChrome.setEnabledSystemUIOverlays([]);

source: here


哦,我不这么认为。我比较慢,没关系,我很乐意以一张赞的奖励作为安慰奖:) - magicleon94
让我为此创建另一个问题。 - DaIOSGuy
1
哈哈,抱歉 @magicleon94!你的方法是否仍然在导航栏上有高程阴影? - soupjake
哦,对不起,是的,它有,因为我使用了一张暗色图像,所以我没有注意到! - magicleon94
完成,我正在更新答案。 - magicleon94
显示剩余4条评论

1

使用BottomNavigationBar非常简单。

只需在BottomNavigationBar中添加这些属性即可。

backgroundColor: Color(0x00ffffff),
elevation: 0,
type: BottomNavigationBarType.fixed,

添加这些后,我的代码看起来像这样。

BottomNavigationBar(
selectedItemColor: MyTheme.primary_color,
unselectedItemColor: Color.fromRGBO(153, 153, 153, 1),
backgroundColor: Color(0x00ffffff),
elevation: 0,
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (index) {
_currentIndex = index;                  
},
items: [

1
我的高层次解决方案:
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          SingleChildScrollView(
            child: Center(
              child: Column(
                children: <Widget>[
                  child(),
                  child(),
                  child(),
                  child(),
                  child(),
                ],
              ),
            ),
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: Opacity(opacity: showBottomBar ? 1 : 0, child: bottomBar()),
          )
        ],
      ),
    );

这个想法是在下层具有可滚动视图的堆栈,以及在其顶部对齐的自定义底部栏。

1

好消息是,我找到了解决方案,而且非常简单!

 Widget build(BuildContext context) {
// Set Android Bar Transparent
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(
  const SystemUiOverlayStyle(
    //NavigationBar
    systemNavigationBarColor: Colors.transparent,
    systemNavigationBarContrastEnforced: false,
    systemNavigationBarIconBrightness: Brightness.dark,
    //StatusBar
    // systemStatusBarContrastEnforced: false,
    statusBarColor: Colors.transparent,
    statusBarIconBrightness: Brightness.dark,
  ),
);

0

2022解决方案 非常非常简单

1-透明颜色 2-零高程

BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          backgroundColor: Colors.transparent,
          elevation: 0,

0
我通过将透明度分配给背景颜色来解决了这个问题: backgroundColor: const Color(0x00FFFFFF) 0x00 = 0%不透明度

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