Flutter通用导航插件universal_navigation的使用

简介

universal_navigation 是一个用于 Flutter 应用程序的核心导航库。它允许使用底部导航栏,并在不同块(Bloc)和页面之间传递数据。


平台兼容性

该插件已在以下平台进行了测试:

  • Android;
  • iOS;
  • Windows 10 64 位;
  • Linux(Ubuntu 20.04.1 LTS);
  • Web(Chrome)。

注意:MacOS 的支持未被保证,但可以尝试构建。


使用方法

要使用此插件,请在 pubspec.yaml 文件中添加 universal_navigation 作为依赖项。

dependencies:
  universal_navigation:
  injectable:

dev_dependencies:
  injectable_generator:
  build_runner:

开始使用

安装步骤

1. 创建全局页面模块

首先,创建一个文件来表示全局页面(或屏幕、Bloc)。

// 文件名: global_flows_module.dart
@module
abstract class GlobalFlowsModule {
  @singleton
  GlobalFlows get getGlobalFlows => GlobalFlows({
    StartPage.routeName: (ctx) => getIt<StartPage>(),
    LoginPage.routeName: (ctx) => getIt<LoginPage>(),
    BottomNavigationPage.routeName: (ctx) => getIt<BottomNavigationPage>(),
    NestedTabPage.routeName: (ctx) => getIt<NestedTabPage>(),
  });
}

说明

  • GlobalFlows 类已定义在库中。
  • 使用了 @module@singleton 注解,这些来自 injectable 包。

2. 创建标签页模块

接下来,创建一个文件来表示标签页流页面(或屏幕、Bloc)。

// 文件名: tab_flows_module.dart
@module
abstract class TabFlowsModule {
  @singleton
  List<TabFlow> get getTabFlows => [
        TabFlow(
            page: getIt<FirstTabPage>(),
            navigatorKey: GlobalKey<NavigatorState>(),
            iconData: Icons.mail,
            title: '1'),
        TabFlow(
            page: getIt<SecondTabPage>(),
            navigatorKey: GlobalKey<NavigatorState>(),
            iconData: Icons.announcement,
            title: '2'),
        TabFlow(
            page: getIt<ThirdTabPage>(),
            navigatorKey: GlobalKey<NavigatorState>(),
            iconData: Icons.person,
            title: '3'),
      ];
}

说明

  • TabFlow 已定义在库中,包含以下属性:
    • page: 页面或 Bloc。
    • navigatorKey: 用于嵌套导航的键。
    • iconData: 底部导航栏的图标。
    • title: 底部导航栏的文字。

3. 创建底部导航桥接类

创建一个类实现 BottomNavigationBuilderTabChanger 接口。

// 文件名: default_bottom_nav_bridge.dart
class DefaultBottomNavBridge implements BottomNavigationBuilder, TabChanger {
  final BottomNavKey bottomNavKey;
  final List<TabFlow> tabFlows;
  final Color backgroundColor;
  final Color selectedItemColor;
  final Color unselectedItemColor;
  BottomNavigationBar _bottomNavigationBar;

  DefaultBottomNavBridge({
    @required this.bottomNavKey,
    @required this.tabFlows,
    @required this.backgroundColor,
    @required this.selectedItemColor,
    @required this.unselectedItemColor,
  });

  @override
  Widget build(int currentIndexTab, Function(int) onTabChanged) {
    _bottomNavigationBar = BottomNavigationBar(
      key: bottomNavKey.key,
      backgroundColor: backgroundColor,
      selectedItemColor: selectedItemColor,
      unselectedItemColor: unselectedItemColor,
      currentIndex: currentIndexTab,
      items: tabFlows
          .map(
            (e) => BottomNavigationBarItem(
              icon: Icon(e.iconData),
              label: e.title,
            ),
          )
          .toList(),
      onTap: onTabChanged,
    );
    return _bottomNavigationBar;
  }

  @override
  List<TabFlow> getTabFlows() {
    return tabFlows;
  }

  @override
  void onTap(int index) {
    _bottomNavigationBar.onTap(index);
  }
}

4. 注册默认底部导航桥接类

创建一个文件来注册 DefaultBottomNavBridge 类作为依赖项。

// 文件名: bottom_nav_bridge_module.dart
@module
abstract class BottomNavBridgeModule {
  @lazySingleton
  DefaultBottomNavBridge get getDefaultBottomNavBridge =>
      DefaultBottomNavBridge(
        bottomNavKey: getIt<BottomNavKey>(),
        tabFlows: getIt<List<TabFlow>>(),
        backgroundColor: Colors.white,
        selectedItemColor: Colors.redAccent,
        unselectedItemColor: Colors.grey,
      );

  BottomNavigationBuilder get getBottomNavigationBuilder =>
      getIt<DefaultBottomNavBridge>();

  TabChanger get getTabChanger => getIt<DefaultBottomNavBridge>();
}

5. 合并依赖项

由于 injectable 无法生成库中的依赖项,因此我们使用 get_it 来初始化依赖项。

// 文件名: injection.dart
@injectableInit
void configureInjection(String env) {
  initUNavInjection(env);
  $initGetIt(environment: env);
  initUNavAppNavigatorInjection(env);
  _initBottomNavigationInjection(env);
}

void _initBottomNavigationInjection(String env) {
  getIt.registerSingleton<BottomNavigationPage>(
      BottomNavigationPage(getIt<TabChangeListener>(), getIt<BottomNavigationBuilder>()));
}

6. 修改 main.dart

main.dart 中配置依赖注入。

// 文件名: main.dart
void main() {
  configureInjection(Environment.prod);
  runApp(Application());
}

class Application extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Universal Navigation Test App',
      initialRoute: StartPage.routeName,
      navigatorKey: getIt<GlobalNavKey>().key,
      debugShowCheckedModeBanner: false,
      routes: getIt<GlobalFlows>().flows,
    );
  }
}

7. 运行代码生成

运行代码生成命令:

flutter packages pub run build_runner watch

如果只想运行一次,可以使用:

flutter packages pub run build_runner build

导航与页面间数据传递

默认情况下,您可以使用 NavigationController 进行导航。

// 文件名: start_page.dart
@injectable
class StartPage extends StatefulWidget {
  static const routeName = '/start_page';

  final NavigationController<EventData> _navigationController;

  const StartPage(this._navigationController);

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

class _StartPageState extends State<StartPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: Column(
            children: [
              Text('Start Page'),
              Padding(
                padding: const EdgeInsets.only(top: 16),
                child: TextButton(
                  onPressed: () {
                    widget._navigationController.pushGlobalPage(LoginPage.routeName, eventData: EventData(event: Event.Login, data: 'Data From Start Page'));
                  },
                  child: Text('NEXT'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

如果您想自定义传递的数据类型,可以使用 freezed

// 文件名: event_union.freezed.dart
part 'event_union.freezed.dart';

[@freezed](/user/freezed)
abstract class EventUnion with _$EventUnion {
  const factory EventUnion() = Empty;
  const factory EventUnion.login({String data}) = Login;
}

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

1 回复

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


universal_navigation 是一个用于 Flutter 的通用导航插件,它提供了一种简单且灵活的方式来处理应用程序中的导航逻辑。这个插件可以帮助你在不同的平台(如 Android、iOS、Web 等)上实现一致的导航体验。

安装

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

dependencies:
  flutter:
    sdk: flutter
  universal_navigation: ^1.0.0  # 请使用最新版本

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

基本用法

1. 初始化导航

在你的 main.dart 文件中,初始化 UniversalNavigation

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Universal Navigation Demo',
      navigatorKey: UniversalNavigation.navigatorKey,
      onGenerateRoute: UniversalNavigation.onGenerateRoute,
      initialRoute: '/',
      routes: {
        '/': (context) => HomeScreen(),
        '/details': (context) => DetailsScreen(),
      },
    );
  }
}

2. 导航到新页面

你可以使用 UniversalNavigation 提供的 push 方法来导航到新页面:

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

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

3. 返回上一页

你可以使用 pop 方法返回到上一页:

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

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

高级用法

1. 传递参数

你可以在导航时传递参数:

UniversalNavigation.push(context, '/details', arguments: {'id': 123});

在目标页面中,你可以通过 ModalRoute.of(context) 获取传递的参数:

class DetailsScreen extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    final Map<String, dynamic> args = ModalRoute.of(context).settings.arguments;

    return Scaffold(
      appBar: AppBar(
        title: Text('Details'),
      ),
      body: Center(
        child: Text('ID: ${args['id']}'),
      ),
    );
  }
}

2. 自定义路由

你可以通过 onGenerateRoute 来自定义路由逻辑:

Route<dynamic> _onGenerateRoute(RouteSettings settings) {
  switch (settings.name) {
    case '/':
      return MaterialPageRoute(builder: (_) => HomeScreen());
    case '/details':
      return MaterialPageRoute(builder: (_) => DetailsScreen());
    default:
      return MaterialPageRoute(builder: (_) => NotFoundScreen());
  }
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Universal Navigation Demo',
      navigatorKey: UniversalNavigation.navigatorKey,
      onGenerateRoute: _onGenerateRoute,
      initialRoute: '/',
    );
  }
}
回到顶部