CupertinoTabBar

Flutter cupertino库中提供的一个iOS风格的底部导航栏组件

CupertinoTabBar是Flutter cupertino库中提供的一个iOS风格的底部导航栏组件。它遵循Apple的HIG(Human Interface Guidelines)设计规范,通常用于在应用程序的不同主要视图之间进行切换。CupertinoTabBar通 常与CupertinoTabScaffold配合使用,作为其tabBar属性的值,共同实现一个完整的iOS风格的多页面切换体验。

主要用途和逻辑

  • 导航切换: 提供一种直观的方式,让用户在多个顶级功能模块或页面之间快速导航。
  • 状态管理: 每个标签页的视图状态可以独立维护。
  • 视觉反馈: 当用户选择一个标签时,会显示选中状态,且图标和文本通常有颜色变化。

使用场景

  • 应用程序的主导航结构,如微信、QQ、设置等iOS应用程序底部的主导航栏。
  • 含有多个独立功能模块的工具类应用。
  • 任何需要提供iOS风格底部导航体验的Flutter应用。

示例

基本使用

import 'package:flutter/cupertino.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return const CupertinoApp(
      title: 'Cupertino TabBar Demo',
      home: MyTabbedApp(),
    );
  }
}

class MyTabbedApp extends StatelessWidget {
  const MyTabbedApp({super.key});

  
  Widget build(BuildContext context) {
    return CupertinoTabScaffold(
      // 创建 CupertinoTabBar
      tabBar: CupertinoTabBar(
        // 配置标签项
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.home),
            label: '首页',
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.search),
            label: '搜索',
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.profile_circled),
            label: '我的',
          ),
        ],
        // 可选:设置选中项的颜色
        activeColor: CupertinoColors.activeBlue,
        // 可选:设置未选中项的颜色
        inactiveColor: CupertinoColors.inactiveGray,
        backgroundColor: CupertinoColors.white, // 背景色
      ),
      // 页面构建器,根据选中的索引返回对应的页面
      tabBuilder: (BuildContext context, int index) {
        return CupertinoTabView(
          builder: (BuildContext context) {
            switch (index) {
              case 0:
                return const CupertinoPageScaffold(
                  navigationBar: CupertinoNavigationBar(middle: Text('首页')),
                  child: Center(child: Text('这是首页内容')),
                );
              case 1:
                return const CupertinoPageScaffold(
                  navigationBar: CupertinoNavigationBar(middle: Text('搜索')),
                  child: Center(child: Text('这是搜索内容')),
                );
              case 2:
                return const CupertinoPageScaffold(
                  navigationBar: CupertinoNavigationBar(middle: Text('我的')),
                  child: Center(child: Text('这是我的内容')),
                );
              default:
                return const Center(child: Text('未知页面'));
            }
          },
        );
      },
    );
  }
}

动态badge(红点提示)和自定义图标

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; // 用于 Text 上的 badge

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return const CupertinoApp(
      title: 'Cupertino TabBar with Badge Demo',
      home: MyTabbedAppWithBadge(),
    );
  }
}

class MyTabbedAppWithBadge extends StatefulWidget {
  const MyTabbedAppWithBadge({super.key});

  
  State<MyTabbedAppWithBadge> createState() => _MyTabbedAppWithBadgeState();
}

class _MyTabbedAppWithBadgeState extends State<MyTabbedAppWithBadge> {
  int _messageCount = 3; // 假设有一个消息计数

  
  Widget build(BuildContext context) {
    return CupertinoTabScaffold(
      tabBar: CupertinoTabBar(
        items: <BottomNavigationBarItem>[
          const BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.home),
            label: '首页',
          ),
          BottomNavigationBarItem(
            icon: Stack(
              children: [
                const Icon(CupertinoIcons.bell_fill), // 使用带填充的图标
                if (_messageCount > 0)
                  Positioned(
                    right: 0,
                    top: 0,
                    child: Container(
                      padding: const EdgeInsets.all(2),
                      decoration: BoxDecoration(
                        color: CupertinoColors.destructiveRed,
                        borderRadius: BorderRadius.circular(6),
                      ),
                      constraints: const BoxConstraints(
                        minWidth: 14,
                        minHeight: 14,
                      ),
                      child: Text(
                        '$_messageCount',
                        style: const TextStyle(
                          color: CupertinoColors.white,
                          fontSize: 9,
                        ),
                        textAlign: TextAlign.center,
                      ),
                    ),
                  )
              ],
            ),
            label: '消息',
          ),
          const BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.settings),
            label: '设置',
          ),
        ],
        activeColor: CupertinoColors.activeOrange,
        inactiveColor: CupertinoColors.systemGrey,
        backgroundColor: CupertinoColors.systemGrey5,
      ),
      tabBuilder: (BuildContext context, int index) {
        return CupertinoTabView(
          builder: (BuildContext context) {
            switch (index) {
              case 0:
                return CupertinoPageScaffold(
                  navigationBar: const CupertinoNavigationBar(middle: Text('首页')),
                  child: Center(
                    child: CupertinoButton(
                      child: const Text('模拟首页操作'),
                      onPressed: () {},
                    ),
                  ),
                );
              case 1:
                return CupertinoPageScaffold(
                  navigationBar: const CupertinoNavigationBar(middle: Text('消息')),
                  child: Center(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text('当前消息数: $_messageCount'),
                        CupertinoButton(
                          child: const Text('清空消息'),
                          onPressed: () {
                            setState(() {
                              _messageCount = 0;
                            });
                          },
                        ),
                      ],
                    ),
                  ),
                );
              case 2:
                return CupertinoPageScaffold(
                  navigationBar: const CupertinoNavigationBar(middle: Text('设置')),
                  child: Center(
                    child: CupertinoButton(
                      child: const Text('进入设置'),
                      onPressed: () {},
                    ),
                  ),
                );
              default:
                return const Center(child: Text('未知页面'));
            }
          },
        );
      },
    );
  }
}

自定义背景色、边框及不透明度

import 'package:flutter/cupertino.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return const CupertinoApp(
      title: 'Custom Cupertino TabBar Demo',
      home: CustomTabBarApp(),
    );
  }
}

class CustomTabBarApp extends StatelessWidget {
  const CustomTabBarApp({super.key});

  
  Widget build(BuildContext context) {
    return CupertinoTabScaffold(
      tabBar: CupertinoTabBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.photo),
            label: '相册',
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.video_camera),
            label: '视频',
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.folder),
            label: '文件',
          ),
        ],
        // 自定义背景色(带透明度)
        backgroundColor: CupertinoColors.darkBackgroundGray.withOpacity(0.8),
        // 自定义选中项颜色
        activeColor: CupertinoColors.systemYellow,
        // 自定义未选中项颜色
        inactiveColor: CupertinoColors.white,
        // 自定义顶部边框
        border: const Border(
          top: BorderSide(
            color: CupertinoColors.systemGrey4,
            width: 0.5,
            style: BorderStyle.solid,
          ),
        ),
      ),
      tabBuilder: (BuildContext context, int index) {
        return CupertinoTabView(
          builder: (BuildContext context) {
            String pageName;
            IconData icon;
            switch (index) {
              case 0:
                pageName = '相册';
                icon = CupertinoIcons.photo;
                break;
              case 1:
                pageName = '视频';
                icon = CupertinoIcons.video_camera;
                break;
              case 2:
                pageName = '文件';
                icon = CupertinoIcons.folder;
                break;
              default:
                pageName = '未知';
                icon = CupertinoIcons.question_circle;
            }
            return CupertinoPageScaffold(
              navigationBar: CupertinoNavigationBar(middle: Text(pageName)),
              child: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Icon(icon, size: 50, color: CupertinoColors.systemGrey),
                    const SizedBox(height: 20),
                    Text('$pageName 页面内容', style: const TextStyle(fontSize: 18)),
                  ],
                ),
              ),
            );
          },
        );
      },
    );
  }
}

注意点

  • 通常与CupertinoTabScaffold配合使用: CupertinoTabBar本身只负责显示底部导航栏,不包含页面切换逻辑。它几乎总是作为CupertinoTabScaffoldtabBar属性来使用的,由CupertinoTabScaffold负责管理多个CupertinoTabView之间的切换。
  • BottomNavigationBarItem: CupertinoTabBaritems属性接受的是List<BottomNavigationBarItem>,而不是CupertinoTabBarItem。虽然名字上没有Cupertino前缀,但这些Item会被CupertinoTabBar按照iOS的风格渲染。
  • 图标和文本颜色: activeColorinactiveColor属性用于设置选中和未选中状态下图标和文本的颜色。
  • 背景透明度: iOS底部导航栏通常是半透明的,可以设置backgroundColor为带有透明度的Color,例如Color.fromRGBO(249, 249, 249, 0.9)CupertinoColors.systemGrey6.withOpacity(0.9),并确保CupertinoTabScaffoldresizeToAvoidBottomInset属性根据需要设置。
  • 性能考虑: CupertinoTabScaffold在切换标签页时,默认会保持所有CupertinoTabView的状态。如果每个标签页都加载大量数据且CupertinoTabView数量较多,可能会导致内存占用较高。对于性能敏感的应用,可能需要考虑使用其他状态管理方式(如AutomaticKeepAliveClientMixin)或延迟加载部分页面内容。
  • 自定义高度: 默认高度为50.0。iOS 的Human Interface Guidelines(HIG)建议标准Tab Bar高度为 49pt。不建议随意修改其高度,以保持iOS风格的一致性。
  • Accessibility: 注意为BottomNavigationBarItemlabel提供有意义的文本,以支持无障碍功能。

构造函数

const CupertinoTabBar({
  super.key,
  required this.items,        // 底部导航栏的项列表
  this.backgroundColor,       // 背景颜色
  this.activeColor,           // 选中项的图标和文本颜色
  this.inactiveColor,         // 未选中项的图标和文本颜色
  this.border = _kTabBarBorder, // 导航栏顶部的边框
  this.iconSize = 30.0,       // 图标大小
  this.currentIndex = 0,      // 当前选中项的索引
  this.onTap,                 // 点击回调
  this.height = _kTabBarHeight, // 导航栏高度
});

属性

属性名类型说明
itemsList<BottomNavigationBarItem>必填项,定义了Tab Bar中显示的导航项。每个项包含图标和可选的文本标签。
backgroundColorColor?Tab Bar的背景颜色。如果为 null,将使用 CupertinoThemebarBackgroundColor
activeColorColor?选中项的图标和文本颜色。如果为 null,将使用 CupertinoThemeprimaryColor
inactiveColorColor?未选中项的图标和文本颜色。如果为 null,为黑色(深色模式下为白色)。
borderBorder?Tab Bar顶部的边框。默认为一道细灰色边框。如果设置为 null,将没有边框。
iconSizedoubleTab Bar中图标的尺寸。默认为 30.0
currentIndexint当前选中的 BottomNavigationBarItem 的索引。默认为 0。通常由 CupertinoTabScaffold 内部维护。
onTapValueChanged<int>?当用户点击某个 Tab 项时的回调函数。参数为被点击项的索引。通常由 CupertinoTabScaffold 内部处理。
heightdoubleTab Bar的高度。默认为 50.0。不建议随意更改以保持 iOS 视觉一致性。

关键属性解释

  • items: 这是CupertinoTabBar的核心,它是一个BottomNavigationBarItem列表,定义了Tab Bar上的所有导航项。每个BottomNavigationBarItem至少需要一个icon,通常也会有label(文本标签)。
  • backgroundColor: 允许您自定义 Tab Bar 的背景颜色。在 iOS 风格的应用中,常使用带一些透明度的颜色来创建磨砂玻璃效果,例如Colors.white.withOpacity(0.9)
  • activeColorinactiveColor: 这两个属性对于控制Tab Bar的视觉主题至关重要。activeColor定义了用户选中某个Tab时图标和文本的颜色,而inactiveColor则定义了未选中Tab的颜色。合理设置这两者可以增强用户体验,明确指示当前所在的页面。
  • border: 默认情况下,CupertinoTabBar会在顶部显示一个细细的分割线,模拟iOS的设计。你可以通过这个属性自定义边框,甚至设置为null来移除边框。
  • onTap: 这是一个回调函数,当用户点击Tab Bar中的某个项时会被调用。然而,当CupertinoTabBar作为CupertinoTabScaffold的一部分时,通常由CupertinoTabScaffold内部处理 onTap 事件来切换CupertinoTabView。如果你想在点击Tab时执行额外逻辑(而不只是切换页面),可以自定义onTap,但要确保它不会干扰CupertinoTabScaffold默认的页面切换行为。