Flutter嵌套导航管理插件nested_navigators的使用

Flutter 中的 nested_navigators 插件可以帮助我们实现多个嵌套导航器及其各自的路由栈。以下是该插件的基本用法和完整示例。


使用方法

要使用 nested_navigators,至少需要指定两个参数:itemsgenerateRoute

示例代码

class RootPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _RootPageState();
}

class _RootPageState extends State<RootPage> {
  @override
  Widget build(BuildContext context) {
    return NestedNavigators(
      items: {
        NestedNavItemKey.blue: NestedNavigatorItem(
          initialRoute: Routes.blue,
          icon: Icons.access_time,
          text: "Blue",
        ),
        NestedNavItemKey.red: NestedNavigatorItem(
          initialRoute: Routes.red,
          icon: Icons.send,
          text: "Red",
        ),
        NestedNavItemKey.green: NestedNavigatorItem(
          initialRoute: Routes.green,
          icon: Icons.perm_identity,
          text: "Green",
        ),
      },
      generateRoute: Routes.generateRoute,
    );
  }
}

关键点解释

  1. items 参数
    定义了导航器的选项卡集合,每个选项卡都有一个唯一的键(如 NestedNavItemKey.blue)和对应的初始路由(如 Routes.blue)。

  2. generateRoute 参数
    定义了如何生成路由,通常通过一个路由生成函数来实现。


配置底部导航栏

默认情况下,NestedNavigators 会显示底部导航栏(BottomNavigationBar),但你可以隐藏它或自定义其外观。

隐藏底部导航栏

clearStackAfterTapOnCurrentTab: false

自定义底部导航栏项

方法一:配置 BottomNavigationBarItem

buildBottomNavigationItem: (key, item, selected) =>
  BottomNavigationBarItem(
    icon: Icon(
      item.icon,
      color: Colors.blue,
    ),
    title: Text(
      item.text,
      style: TextStyle(fontSize: 20),
    ),
  )

方法二:定义自己的自定义组件

buildCustomBottomNavigationItem: (key, item, selected) =>
  Container(
    height: 60,
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      mainAxisSize: MainAxisSize.min,
      children: [
        Icon(
          item.icon,
          size: 24,
          color: selected ? Colors.blue : null,
        ),
        Text(
          item.text,
          style: TextStyle(fontSize: 20, color: selected ? Colors.blue : null),
        ),
      ],
    ),
  )

初始选中导航器

  • 如果导航器数量为偶数,则默认选择第一个导航器。
  • 如果导航器数量为奇数,则默认选择中间的导航器。
  • 也可以手动指定初始选中的导航器:
initialNavigatorKey: NestedNavItemKey.green

主题配置

可以通过主题配置底部导航栏的颜色,例如设置水波纹效果的颜色:

bottomNavigationBarTheme: Theme.of(context).copyWith(
  splashColor: Colors.blue[100],
)

路由级别的隐藏导航栏

在某些特定路由中可以隐藏底部导航栏,只需在导航时添加以下参数:

Navigator.of(context).pushNamed(
  Routes.red,
  arguments: {
    hideNavTabBar: true,
  },
);

在路由中切换导航器

可以从当前路由中切换到其他导航器:

NestedNavigatorsBlocProvider.of(context).select(NestedNavItemKey.green)

或者同时切换导航器并进行导航:

NestedNavigatorsBlocProvider.of(context).selectAndNavigate(
  NestedNavItemKey.green,
  (navigator) => navigator.pushNamed(
    Routes.green,
    arguments: {
      ArgumentKeys.value: 100,
    },
  ),
);

NestedNavigatorsBlocProvider 添加到根 Widget

如果需要在全局范围内访问根导航器并操作嵌套导航器,可以在 MaterialApp 上方添加 NestedNavigatorsBlocProvider

class App extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _AppState();
}

class _AppState extends State<App> {
  final NestedNavigatorsBloc _bloc = NestedNavigatorsBloc<NestedNavItemKey>();

  @override
  Widget build(BuildContext context) {
    return NestedNavigatorsBlocProvider(
      bloc: _bloc,
      child: MaterialApp(
        title: 'Flutter Demo',
        home: RootPage(),
        onGenerateRoute: (routeSettings) => Routes.generateRoute(routeSettings),
      ),
    );
  }
}

替换为抽屉菜单

可以将底部导航栏替换为抽屉菜单(Drawer),并通过回调函数动态渲染菜单项:

drawer: (items, selectedItemKey, selectNavigator) =>
  Drawer(
    child: ListView(
      children: items.entries
          .map((entry) =>
            ListTile(
              title: Text(
                entry.value.text,
                style: TextStyle(
                  color: entry.key == selectedItemKey ? Colors.blue : null,
                ),
              ),
              trailing: Icon(
                entry.value.icon,
                color: entry.key == selectedItemKey ? Colors.blue : null,
              ),
              onTap: () => selectNavigator(entry.key),
            )
          )
          .toList(),
    ),
  ),

从页面中打开抽屉菜单:

NestedNavigatorsBlocProvider.of(context).actionWithScaffold(
  (scaffoldState) => scaffoldState.openDrawer(),
);

示例代码

完整示例代码位于 GitHubexample 文件夹中。以下是入口文件的完整代码:

import 'package:flutter/material.dart';

import 'app.dart';

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

更多关于Flutter嵌套导航管理插件nested_navigators的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter嵌套导航管理插件nested_navigators的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


nested_navigators 是一个用于 Flutter 的插件,它允许你在应用中嵌套多个 Navigator,从而实现更复杂的导航结构。这在需要管理多个独立导航栈的场景中非常有用,例如在选项卡式应用中,每个选项卡可以有自己的导航栈。

安装

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

dependencies:
  flutter:
    sdk: flutter
  nested_navigators: ^1.0.0  # 请检查最新版本

然后运行 flutter pub get 来安装依赖。

基本使用

1. 创建嵌套导航器

nested_navigators 提供了一个 NestedNavigators 组件,你可以在 MaterialApp 中使用它来创建嵌套导航器。

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: NestedNavigators(
        initialRoute: '/home',
        routes: {
          '/home': (context) => HomeScreen(),
          '/details': (context) => DetailsScreen(),
        },
      ),
    );
  }
}

2. 在嵌套导航器中进行导航

你可以在嵌套导航器中使用 Navigator.of(context) 来进行导航操作。例如,在 HomeScreen 中导航到 DetailsScreen

class HomeScreen extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).pushNamed('/details');
          },
          child: Text('Go to Details'),
        ),
      ),
    );
  }
}

3. 处理返回按钮

在嵌套导航器中,返回按钮的行为与普通的 Navigator 类似。你可以使用 Navigator.of(context).pop() 来返回上一页。

class DetailsScreen extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Details')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).pop();
          },
          child: Text('Go Back'),
        ),
      ),
    );
  }
}
回到顶部