Flutter路由钩子插件nav_hooks的使用

Flutter路由钩子插件nav_hooks的使用

提供一种简单的导航方式。包含了许多路由。适用于Android和iOS平台。

安装

pubspec.yaml文件中添加nav依赖:

dependencies:
  nav: ^{latest version}

使用

步骤1:在你的AppState中添加Nav混入

import 'package:nav/nav.dart';

class _MyAppState extends State<MyApp> with Nav

步骤2:重写navigatorKey方法并提供一个全局键,该键将在MaterialApp中使用

class MyApp extends StatefulWidget {
  static GlobalKey<NavigatorState> navigatorKey = GlobalKey();

  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with Nav {
  [@override](/user/override)
  GlobalKey<NavigatorState> get navigatorKey => MyApp.navigatorKey;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Material(
      textStyle: const TextStyle(color: Colors.black),
      child: MaterialApp(
        navigatorKey: navigatorKey,
        title: 'Nav Demo',
        theme: ThemeData(
          fontFamily: 'DMSans',
          scaffoldBackgroundColor: Colors.white,
          iconTheme: const IconThemeData(color: Colors.blue),
          textTheme: const TextTheme(),
          bottomSheetTheme: const BottomSheetThemeData(backgroundColor: Colors.transparent),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

步骤3:使用各种推送方法

// 动态方式
Nav.push(Widget, navAni: NavAni.Blink)

// 或者
Nav.pushFromRight(Widget)
Nav.pushFromLeft(Widget)
Nav.pushFromTop(Widget)
Nav.pushFromBottom(Widget)
Nav.pushReplacement(Widget)
Nav.pushWithRippleEffect(Widget, centerAlignment : Alignment.bottomRight, centerOffset : Offset(10, 10))
Nav.clearAllAndPush(Widget)

enum NavAni { Left, Right, Top, Bottom, Fade, Ripple, Blink }

步骤4:所有方法都可以返回值

// 从底部屏幕返回结果
final result = await Nav.pushFromRight(TopScreen); // 你可以从TopWidget获取结果

// 从顶部屏幕返回结果
Nav.pop(context, result: {"key": "value", "key2": 2})

示例代码

以下是完整的示例代码:

import 'dart:io';

import 'package:after_layout/after_layout.dart';
import 'package:example/enum_direction.dart';
import 'package:example/util/u_color_generator.dart';
import 'package:flutter/material.dart';
import 'package:nav_hooks/nav.dart';

import 'dialog/message_dialog.dart';

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

class MyApp extends StatefulWidget {
  static GlobalKey<NavigatorState> navigatorKey = GlobalKey();

  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with Nav {
  [@override](/user/override)
  GlobalKey<NavigatorState> get navigatorKey => MyApp.navigatorKey;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Material(
      textStyle: const TextStyle(color: Colors.black),
      child: MaterialApp(
        navigatorKey: navigatorKey,
        title: 'Nav Demo',
        theme: ThemeData(
          fontFamily: 'DMSans',
          scaffoldBackgroundColor: Colors.white,
          iconTheme: const IconThemeData(color: Colors.blue),
          textTheme: const TextTheme(),
          bottomSheetTheme: const BottomSheetThemeData(backgroundColor: Colors.transparent),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.navType}) : super(key: key);

  final NavType? navType;

  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with AfterLayoutMixin {
  bool showBackButton = false;
  Color? bgColor;

  bool get isHome => !showBackButton;

  [@override](/user/override)
  void initState() {
    bgColor = getRandomColor();
    super.initState();
  }

  [@override](/user/override)
  void afterFirstLayout(BuildContext context) {
    checkCanPop();
  }

  void checkCanPop() async {
    if (await Nav.canPop()) {
      setState(() {
        this.showBackButton = true;
      });
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: bgColor,
      floatingActionButton: Builder(
        builder: (context) => FloatingActionButton(
          onPressed: () async {
            await MessageDialog().show();
          },
          tooltip: 'Ripple',
          child: Icon(Icons.open_in_new),
        ),
      ),
      body: Builder(
        builder: (context) => SafeArea(
          child: Stack(
            children: [
              Column(
                children: [
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      IconButton(
                        onPressed: () async => onResult(
                            context,
                            await Nav.pushFromTop(MyHomePage(navType: NavType.Top))),
                        icon: icon(Icons.vertical_align_bottom),
                      )
                    ],
                  ),
                  Expanded(
                    child: Container(
                      child: Center(
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            IconButton(
                              onPressed: () async {
                                onResult(
                                    context,
                                    await Nav.pushFromLeft(MyHomePage(navType: NavType.Left)));
                              },
                              icon: icon(Icons.keyboard_arrow_right),
                            ),
                            Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                InkWell(
                                  child: icon(iconData),
                                  onTap: () {
                                    if (isHome) {
                                      Nav.clearAllAndPush(MyHomePage(navType: NavType.ClearAll));
                                    } else {
                                      Nav.popResultSuccess(context);
                                    }
                                  },
                                ),
                                isHome
                                    ? Text("Click an Arrow",
                                        style: TextStyle(
                                            color: Colors.white,
                                            fontSize: 20,
                                            fontWeight: FontWeight.w700))
                                    : Container(),
                              ],
                            ),
                            IconButton(
                              onPressed: () async => onResult(
                                  context,
                                  await Nav.pushFromRight(MyHomePage(navType: NavType.Right))),
                              icon: icon(Icons.keyboard_arrow_left),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      IconButton(
                        onPressed: () async => onResult(
                            context,
                            await Nav.pushFromBottom(MyHomePage(navType: NavType.Bottom))),
                        icon: icon(Icons.vertical_align_top),
                      )
                    ],
                  ),
                ],
              ),
              !showBackButton
                  ? Container()
                  : IconButton(
                      onPressed: () => Nav.pop(context),
                      icon: icon(Platform.isIOS
                          ? Icons.arrow_back_ios
                          : Icons.arrow_back),
                    )
            ],
          ),
        ),
      ),
    );
  }

  Icon icon(IconData data) {
    return Icon(
      data,
      size: 30,
      color: Colors.white,
    );
  }

  IconData get iconData {
    switch (widget.navType) {
      case NavType.Top:
        return Icons.vertical_align_top;
      case NavType.Bottom:
        return Icons.vertical_align_bottom;
      case NavType.Left:
        return Icons.keyboard_arrow_left;
      case NavType.Right:
        return Icons.keyboard_arrow_right;
      case NavType.Ripple:
        return Icons.archive;
      case NavType.ClearAll:
        return Icons.border_clear;
      default:
        return Icons.home;
    }
  }

  SnackBar createSnackBar(BuildContext context, String message) {
    return SnackBar(
        elevation: 0,
        behavior: SnackBarBehavior.fixed,
        backgroundColor: Colors.transparent,
        content: GestureDetector(
          onTap: () {},
          child: Container(
            color: Colors.transparent,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                Center(
                  child: Container(
                      padding: EdgeInsets.symmetric(horizontal: 20, vertical: 11),
                      child: Center(
                        child: Text(message,
                            style: TextStyle(
                              color: Colors.white,
                              fontSize: 14,
                              fontStyle: FontStyle.normal,
                            )),
                      ),
                      decoration: new BoxDecoration(
                          color: Color(0xff5a8fee),
                          borderRadius: BorderRadius.circular(5))),
                ),
              ],
            ),
          ),
        ));
  }

  void onResult(BuildContext context, dynamic result) {
    if (Nav.isSuccess(result)) {
      final snackbar =
          createSnackBar(context, "Result is Success: ${result.toString()}");
      ScaffoldMessenger.of(context).showSnackBar(snackbar);
    }
  }
}

更多关于Flutter路由钩子插件nav_hooks的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter路由钩子插件nav_hooks的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


nav_hooks 是一个用于 Flutter 的路由钩子插件,它允许你在导航过程中执行一些自定义逻辑。通过使用 nav_hooks,你可以在路由跳转前后执行特定的操作,例如权限验证、日志记录、数据预处理等。

安装

首先,你需要将 nav_hooks 添加到你的 pubspec.yaml 文件中:

dependencies:
  flutter:
    sdk: flutter
  nav_hooks: ^1.0.0  # 请查看最新版本

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

基本用法

1. 创建路由钩子

你可以通过继承 RouteHook 类来创建自定义的路由钩子。RouteHook 提供了以下几个生命周期方法:

  • beforeNavigate: 在导航之前调用。
  • afterNavigate: 在导航之后调用。
  • beforePop: 在返回之前调用。
  • afterPop: 在返回之后调用。
import 'package:nav_hooks/nav_hooks.dart';

class AuthRouteHook extends RouteHook {
  @override
  FutureOr<bool> beforeNavigate(BuildContext context, RouteSettings settings) async {
    // 在导航之前执行的逻辑,例如权限检查
    bool isAuthenticated = await checkAuth();
    if (!isAuthenticated) {
      // 如果用户未认证,重定向到登录页面
      Navigator.pushNamed(context, '/login');
      return false; // 阻止原路由的跳转
    }
    return true; // 允许继续导航
  }

  @override
  void afterNavigate(BuildContext context, RouteSettings settings) {
    // 在导航之后执行的逻辑,例如日志记录
    print('Navigated to: ${settings.name}');
  }

  @override
  FutureOr<bool> beforePop(BuildContext context, RouteSettings settings) async {
    // 在返回之前执行的逻辑
    bool shouldPop = await showConfirmationDialog();
    return shouldPop; // 允许或阻止返回
  }

  @override
  void afterPop(BuildContext context, RouteSettings settings) {
    // 在返回之后执行的逻辑
    print('Popped from: ${settings.name}');
  }
}

2. 使用路由钩子

你可以在 MaterialAppCupertinoApp 中使用 NavigatorHooks 来应用路由钩子。

import 'package:flutter/material.dart';
import 'package:nav_hooks/nav_hooks.dart';
import 'auth_route_hook.dart'; // 导入自定义的路由钩子

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Nav Hooks Demo',
      navigatorKey: NavigatorHooks.navigatorKey,
      navigatorObservers: [
        NavigatorHooks(
          hooks: [AuthRouteHook()], // 添加路由钩子
        ),
      ],
      initialRoute: '/',
      routes: {
        '/': (context) => HomePage(),
        '/login': (context) => LoginPage(),
        '/dashboard': (context) => DashboardPage(),
      },
    );
  }
}

高级用法

你还可以在特定的路由上应用不同的路由钩子,或者根据条件动态地应用钩子。

NavigatorHooks(
  hooks: [
    AuthRouteHook(), // 全局钩子
    RouteHookBuilder(
      shouldApply: (settings) => settings.name == '/dashboard',
      builder: () => DashboardRouteHook(), // 仅在 '/dashboard' 路由上应用的钩子
    ),
  ],
);
回到顶部