Flutter底部导航栏插件advn_bottom_nav_bar的使用

Flutter底部导航栏插件advn_bottom_nav_bar的使用

advn_bottom_nav_bar 是一个高度可定制的 Flutter 底部导航栏插件。它提供了17种预设样式供选择,并且可以自定义任何样式,而不会丢失任何功能。

概览

预览

目录

样式 #

预设样式

Style1 Style2 Style3
[style1] [style2] [style3]
Style4 Style5 Style6
[style4] [style5] [style6]
Style7 Style8 Style9
[style7] [style8] [style9]
Style10 Style11 Style12
[style10] [style11] [style12]
Style13 Style14 Style15
[style13] [style14] [style15]
Style16 Neumorphic
[style16] [neumorphic]

注意:这些样式不包含所有可能的变化。

特性 #

  • 可以在显示或不显示导航栏的情况下推送新页面。
  • 提供了17种预设的底部导航栏样式。
  • 每个样式都可以完全自定义。
  • 支持自定义导航栏。
  • 持久标签页 - 切换标签页时不会丢弃导航栈。
  • 支持透明度和模糊效果。
  • 处理硬件/软件Android返回按钮。
  • 支持 go_router 以利用 Flutter 的路由API。

开始使用 #

1. 安装包 #

安装 advn_bottom_nav_bar 包:

dependencies:
  advn_bottom_nav_bar: ^x.y.z

2. 导入包 #

import 'package:advn_bottom_nav_bar/advn_bottom_nav_bar.dart';

3. 使用 PersistentTabView #

PersistentTabView 是你的顶级容器,它将包含你的导航栏和所有的页面(就像一个 Scaffold)。因此,不建议将 PersistentTabView 包裹在 Scaffold.body 中,因为它会为你完成这一切。所以只需创建每个标签页的配置并插入 PersistentTabView 即可:

import 'package:flutter/material.dart';
import 'package:advn_bottom_nav_bar/advn_bottom_nav_bar.dart';

void main() => runApp(PersistenBottomNavBarDemo());

class PersistenBottomNavBarDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Persistent Bottom Navigation Bar Demo',
      home: PersistentTabView(
        tabs: [
          PersistentTabConfig(
            screen: YourFirstScreen(),
            item: ItemConfig(
              icon: Icon(Icons.home),
              title: "Home",
            ),
          ),
          PersistentTabConfig(
            screen: YourSecondScreen(),
            item: ItemConfig(
              icon: Icon(Icons.message),
              title: "Messages",
            ),
          ),
          PersistentTabConfig(
            screen: YourThirdScreen(),
            item: ItemConfig(
              icon: Icon(Icons.settings),
              title: "Settings",
            ),
          ),
        ],
        navBarBuilder: (navBarConfig) => Style1BottomNavBar(
          navBarConfig: navBarConfig,
        ),
      ),
    );
  }
}

样式化 #

你可以通过传递一个 NavBarDecoration 实例来定制导航栏。这个实例继承自 BoxDecoration,因此它提供了 BoxDecoration 所有的功能。例如,你可以通过传递 BorderRadius.circular(8) 来设置不同的圆角半径。

PersistentTabView(
  tabs: [
    PersistentTabConfig(
      screen: YourFirstScreen(),
      item: ItemConfig(
        icon: Icon(Icons.home),
        title: "Home",
      ),
    ),
  ],
  navBarBuilder: (navBarConfig) => Style1BottomNavBar(
    navBarConfig: navBarConfig,
    decoration: NavBarDecoration(
      borderRadius: BorderRadius.circular(8),
    ),
  ),
);

使用自定义导航栏 #

你可以替换 Style1BottomNavBar 小部件为自己的自定义小部件。navBarBuilder 给你一个 navBarConfig,这是构建自定义导航栏所需的一切。以下是一个自定义导航栏小部件的例子:

class CustomNavBar extends StatelessWidget {
  final NavBarConfig navBarConfig;
  final NavBarDecoration navBarDecoration;

  const CustomNavBar({
    super.key,
    required this.navBarConfig,
    this.navBarDecoration = const NavBarDecoration(),
  });

  Widget _buildItem(ItemConfig item, bool isSelected) {
    final title = item.title;
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Expanded(
          child: IconTheme(
            data: IconThemeData(
              size: item.iconSize,
              color: isSelected
                      ? item.activeForegroundColor
                      : item.inactiveForegroundColor,
            ),
            child: isSelected ? item.icon : item.inactiveIcon,
          ),
        ),
        if (title != null)
          Padding(
            padding: const EdgeInsets.only(top: 15.0),
            child: Material(
              type: MaterialType.transparency,
              child: FittedBox(
                child: Text(
                  title,
                  style: item.textStyle.apply(
                    color: isSelected
                            ? item.activeForegroundColor
                            : item.inactiveForegroundColor,
                  ),
                ),
              ),
            ),
          ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return DecoratedNavBar(
      decoration: navBarDecoration,
      height: navBarConfig.navBarHeight,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          for (final (index, item) in navBarConfig.items.indexed)
            Expanded(
              child: InkWell(
                onTap: () => navBarConfig.onItemSelected(index),
                child: _buildItem(item, navBarConfig.selectedIndex == index),
              ),
            ),
        ],
      ),
    );
  }
}

在你的 PersistentTabView 中,你可以像使用预定义样式一样使用它:

PersistentTabView(
  tabs: ...,
  navBarBuilder: (navBarConfig) => CustomNavBar(
    navBarConfig: navBarConfig,
  ),
),

编程控制导航栏 #

内部,PersistentTabView 使用了一个 PersistentTabController。因此,你可以传递一个控制器给 PersistentTabView,以便稍后通过编程更改标签页:

PersistentTabController _controller = PersistentTabController(initialIndex: 0);

PersistentTabView(
  controller: _controller,
  ...
);

_controller.jumpToTab(2);

// 跳转到上一个选中的标签页
_controller.jumpToPreviousTab();

页面切换时自定义过渡动画 #

当从一个标签页切换到另一个标签页时,默认行为是一个滑动过渡,当前页面向左或向右滑动,目标页面滑入屏幕。你可以通过构建自己的动画来自定义这种行为,例如,淡出当前页面并淡入新页面。要控制动画,你可以传递一个函数给 PersistentTabView.animatedTabBuilder。这个函数是一个构建器,同时构建旧页面和新页面。这就是为什么它得到了 BuildContext 作为参数,当前构建的标签页的索引,动画进度,新索引,旧索引和实际页面内容作为子组件。

默认动画构建器如下所示:

final double yOffset = newIndex > index
    ? -animationValue
    : (newIndex < index
        ? animationValue
        : (index < oldIndex ? animationValue - 1 : 1 - animationValue));
return FractionalTranslation(
  translation: Offset(yOffset, 0),
  child: child,
);

导航 #

每个标签页都会获得自己的 Navigator,这样它们就不会相互干扰。这意味着调用 Navigator.of(context).push()(这将在当前标签页内推送一个新的屏幕)和 Navigator.of(context, rootNavigator: true).push()(这将在整个 PersistentTabView 上方推送一个新的屏幕,最终隐藏导航栏)之间会有区别。

该包包括以下实用导航函数:

pushScreen(
  context,
  screen: MainScreen(),
  withNavBar: true/false,
);
pushWithNavBar(
  context,
  MaterialPageRoute(builder: (context) => ...)
);
pushWithoutNavBar(
  context,
  MaterialPageRoute(builder: (context) => ...)
);

默认情况下,每个标签页的 Navigator 将继承根 Navigator 的所有设置。因此,对根 Navigator 的命名路由等配置所做的任何更改,在每个标签页中都会生效。如果你需要每个 Navigator 的特定设置(如额外的路由、NavigatorObservers 等),可以通过传递一个 NavigatorConfig 到相应的 PersistentTabConfig 来实现。

PersistentTabView 可以记住每个标签页的导航栈,所以当你切换回它时,你会看到离开时的内容。这种行为可以通过 PersistentTabView.stateManagement 参数来切换。

路由API #

要结合此插件使用 Flutter 的路由 API,必须使用 go_router。按照 go_router 文档中的设置开始使用声明式路由。要集成持久导航栏,你必须设置一个 StatefulShellRoute.indexedStack 作为你的一个路由,它将包含 PersistentTabView。以下是完整的代码示例或代码片段:

StatefulShellRoute.indexedStack(
  builder: (context, state, navigationShell) =>
      PersistentTabView.router(
    tabs: [
      PersistentRouterTabConfig(
        item: ItemConfig(
          icon: const Icon(Icons.home),
          title: "Home",
        ),
      ),
      PersistentRouterTabConfig(
        item: ItemConfig(
          icon: const Icon(Icons.message),
          title: "Messages",
        ),
      ),
      PersistentRouterTabConfig(
        item: ItemConfig(
          icon: const Icon(Icons.settings),
          title: "Settings",
        ),
      ),
    ],
    navBarBuilder: (navBarConfig) => Style1BottomNavBar(
      navBarConfig: navBarConfig,
    ),
    navigationShell: navigationShell,
  ),
  branches: [
    // 第一个标签页的路由分支
    StatefulShellBranch(
      routes: <RouteBase>[
        GoRoute(
          path: "home",
          builder: (context, state) => const MainScreen(
            useRouter: true,
          ),
          routes: [
            GoRoute(
              path: "detail",
              builder: (context, state) => const MainScreen2(
                useRouter: true,
              ),
            ),
          ],
        ),
      ],
    ),

    // 第二个标签页的路由分支
    StatefulShellBranch(
      routes: <RouteBase>[
        GoRoute(
          path: "messages",
          builder: (context, state) => const MainScreen(
            useRouter: true,
          ),
        ),
      ],
    ),

    // 第三个标签页的路由分支
    StatefulShellBranch(
      routes: <RouteBase>[
        GoRoute(
          path: "settings",
          builder: (context, state) => const MainScreen(
            useRouter: true,
          ),
        ),
      ],
    ),
  ],
),

有用的技巧 #

  • 尝试官方 git 仓库中的交互示例项目以更好地了解该插件:互动示例项目

  • 弹出到指定标签页的任意屏幕:

    Navigator.of(context).popUntil((route) {
        return route.settings.name == "ScreenToPopBackTo";
    });
    
  • 弹出到指定标签页的第一个屏幕:

    Navigator.of(context).popUntil(ModalRoute.withName("/"));
    

    为了使这起作用,你需要让你的 PersistentNavBarItem 被命名为 '/'

    PersistentBottomNavBarItem(
        title: ("Home"),
        routeAndNavigatorSettings:
            RouteAndNavigatorSettings(initialRoute: '/'),
    )
    

    或者,你可以使用非命名路由来执行此操作:

    Navigator.of(context).pushAndRemoveUntil(
        CupertinoPageRoute(
        builder: (BuildContext context) {
            return FirstScreen();
        },
        ),
        (_) => false,
    );
    
  • 若要在导航栏上方显示底部弹出窗口,请使用 showModalBottomScreen 并将其属性 useRootNavigator 设置为 true。参见示例项目以了解示例。

  • 如果你需要在导航栏中的图标上添加通知计数器,可以使用 badges 包,例如:

    PersistentTabConfig(
      screen: ...,
      item: ItemConfig(
          icon: Badge(
              animationType: BadgeAnimationType.scale,
              badgeContent: UnreadIndicator(),
              child: const Icon(
                  Icons.chat_rounded,
              ),
          ),
          title: "Chat",
      ),
    ),
    

更多关于Flutter底部导航栏插件advn_bottom_nav_bar的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter底部导航栏插件advn_bottom_nav_bar的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用 advn_bottom_nav_bar 插件的 Flutter 代码示例。这个插件允许你创建一个高度可定制的底部导航栏。

首先,你需要在你的 pubspec.yaml 文件中添加 advn_bottom_nav_bar 依赖:

dependencies:
  flutter:
    sdk: flutter
  advn_bottom_nav_bar: ^0.6.0  # 请确保版本号是最新的

然后运行 flutter pub get 来获取依赖。

接下来,你可以在你的 Flutter 应用中使用 AdvnBottomNavBar。以下是一个完整的示例代码,展示如何设置和使用这个插件:

import 'package:flutter/material.dart';
import 'package:advn_bottom_nav_bar/advn_bottom_nav_bar.dart';

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

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

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

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  int _selectedIndex = 0;
  final List<Widget> _widgetOptions = <Widget>[
    Text('Home Screen'),
    Text('Search Screen'),
    Text('Profile Screen'),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AdvnBottomNavBar Demo'),
      ),
      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),
      bottomNavigationBar: AdvnBottomNavBar(
        items: [
          AdvnBottomNavBarItem(
            icon: Icons.home,
            title: 'Home',
          ),
          AdvnBottomNavBarItem(
            icon: Icons.search,
            title: 'Search',
          ),
          AdvnBottomNavBarItem(
            icon: Icons.person,
            title: 'Profile',
          ),
        ],
        currentIndex: _selectedIndex,
        onTabSelectedListener: _onItemTapped,
        backgroundColor: Colors.white,
        activeColor: Colors.blue,
        inactiveColor: Colors.grey,
        borderRadius: 25.0,
      ),
    );
  }
}

代码解释

  1. 依赖添加:在 pubspec.yaml 中添加 advn_bottom_nav_bar 依赖。

  2. 主应用结构MyApp 是根组件,使用 MaterialApp 包裹,设置主题和首页。

  3. 首页组件MyHomePage 是一个有状态的组件,包含 _selectedIndex 来跟踪当前选中的导航项,以及 _widgetOptions 列表来显示不同的屏幕内容。

  4. 导航项点击事件_onItemTapped 方法更新 _selectedIndex 的值。

  5. 底部导航栏AdvnBottomNavBar 组件配置导航项(图标和标题),当前选中索引,点击事件监听器,背景颜色,激活和非激活颜色,以及边框圆角。

自定义选项

AdvnBottomNavBar 提供了许多自定义选项,你可以根据需要调整 icon, title, backgroundColor, activeColor, inactiveColor, borderRadius, animationDuration, shadow 等属性。

希望这个示例能帮助你理解如何在 Flutter 应用中使用 advn_bottom_nav_bar 插件。如果你有其他问题,欢迎继续提问!

回到顶部