Go Router Plus

CI codecov GitHub license

Go Router 是一个非常棒的、易于使用的、生产就绪的包,用于创建路由、处理导航、深度链接和动态链接。但在大型应用中,有很多屏幕、状态和认证逻辑,管理路由、重定向和刷新路由逻辑非常困难。

这个包试图通过添加屏幕模式、通用重定向逻辑和链式重定向/刷新监听器来解决上述问题。

安装

运行以下命令来安装此包

flutter pub add go_router_plus

安装 pub 包后,您现在可以导入它了

import 'package:go_router_plus/go_router_plus.dart';

创建屏幕

屏幕代表您应用程序的路由,其目的是将 GoRoute 工厂逻辑与 GoRouter 工厂分离,以便易于阅读和维护。

/// lib/screens/my_first_screen.dart

import 'package:go_router_plus/go_router_plus.dart';
import 'package:go_router/go_router.dart'

class MyFirstScreen extends Screen {
  @override
  Widget builder(BuildContext context, GoRouterState state) {
    return Text('Hello world');
  }

  @override
  String get routeName => 'my_first_screen';

  @override
  String get routePath => '/my-first-screen';
}

您所有的屏幕都必须继承此包提供的抽象类 Screen。以下方法和 getter 需要实现

  • 方法 builder(BuildContext context, GoRouterState state) 必须重新声明返回类型为 WidgetPage<void>,通常情况下您应该使用 Widget,但如果您想 控制屏幕的过渡,您应该使用 Page<void>
  • Getter routeName 声明屏幕的路由名称,必须是唯一的。
  • Getter routePath 声明屏幕的路由路径。

将屏幕标记为初始屏幕

要设置初始路径(用户将看到的第一个屏幕),传统方式是设置 GoRouter 工厂的 initialPath 参数,但使用此包时,您需要创建一个实现 InitialScreen 接口的屏幕。

class LoginScreen extends Screen implements InitialScreen {
  ///......
}

将屏幕标记为错误屏幕

如您所知,GoRouter 工厂提供了一个 errorBuilder 参数来处理错误(例如找不到路由),但使用此包时,您需要创建一个实现 ErrorScreen 接口的屏幕。

class MyErrorScreen extends Screen implements ErrorScreen {
  @override
  Widget builder(BuildContext context, GoRouterState state) {
    return Text(state.error);
  }
  ///......
}

使用屏幕创建路由器

现在您已经掌握了屏幕模式的概念,让我们来创建 Go Router

final router = createGoRouter(
  screens: [
    LoginScreen(),
    MyFirstScreen(),
    MyErrorScreen(),
  ],
);

createGoRouter 是此包提供的工厂函数。

重定向器

Redirector 将处理所有路由的 重定向逻辑

您可以通过 createGoRouter 工厂函数的 redirectors 参数来设置一个或多个重定向器。

final router = createGoRouter(
  screens: [
    LoginScreen(),
    MyFirstScreen(),
    MyErrorScreen(),
  ],
  redirectors: [
    AuthRedirector(
     state: LoggedInStateProvider(),
     guestRedirectPath: '/login',
     userRedirectPath: '/home-page',
    ),
    ScreenRedirector(),
  ],
);

认证重定向

认证重定向是整个应用中最常见的逻辑,因此此包通过 AuthRedirector 开箱即用地提供了它。

要使用此内置功能,您需要创建一个实现 LoggedInState 接口的类。

class LoggedInStateProvider implements LoggedInState {
  bool _loggedIn = false;
  
  @override
  bool get loggedIn => _loggedIn;
  
  set loggedIn(bool value) {
    _loggedIn = value;
  }
}

这是一个简单的接口,需要您实现 loggedIn getter,当用户已登录时,此 getter 返回 true,否则用户为访客(未登录)。

您还需要使用 UserScreenGuestScreen 接口来标记您应用中的访客和用户屏幕。

class LoginScreen extends Screen implements GuestScreen {
 @override
 String get routePath => '/login';
 ///...
}

class HomeScreen extends Screen implements UserScreen {
 @override
 String get routePath => '/home';
  ///...
}

在上面的示例中,我们有两个屏幕,一个用于访客(登录屏幕),一个用于用户(主屏幕)。当用户 **未** 登录时,他们将被重定向到登录屏幕,否则他们将被重定向到主屏幕。为了处理这种情况,我们需要添加具有以下设置的 AuthRedirector

final router = createGoRouter(
  screens: [
    LoginScreen(),
    HomeScreen(),
  ],
  redirectors: [
    AuthRedirector(
     state: LoggedInStateProvider(),
     guestRedirectPath: '/login',
     userRedirectPath: '/home',
    ),
  ],
);

现在,每当用户访问实现 UserScreen 接口的屏幕但 **未** 登录时,他们将被重定向到登录屏幕;如果他们访问实现 GuestScreen 接口的屏幕但已登录,他们将被重定向到主屏幕。

屏幕重定向

这是一项内置功能,用于支持具有自定义 redirect 逻辑的屏幕(例如:通过应用状态、用户角色进行访问控制)。

希望构建自定义重定向逻辑的屏幕应实现 RedirectAware

class VipScreen extends Screen implements UserScreen, RedirectAware {
 @override
 String? redirect(GoRouterState state) {
   /// final currentUser = ....
   return !currentUser.isVip ? '/home' : null; 
 }
}

原始重定向 一样,redirect 方法在当前用户是 VIP 类型时返回 null,这样用户就可以停留在该屏幕上,另一方面,普通用户将被重定向到主屏幕。

并通过将 ScreenRedirector 实例传递给工厂函数的 redirectors 参数来激活此功能。

final router = createGoRouter(
  screens: [
    LoginScreen(),
    HomeScreen(),
    VipScreen(),
  ],
  redirectors: [
    ScreenRedirector(),
    AuthRedirector(
     state: LoggedInStateProvider(),
     guestRedirectPath: '/login',
     userRedirectPath: '/home',
    ),
  ],
);

创建自定义重定向器

在某些情况下,您可能希望控制常见屏幕的重定向逻辑(如上所示的 AuthRedirectorScreenRedirector)(例如:添加一个接口来标记某些屏幕只有管理员用户可以访问)。

abstract class AdminScreen {}

class ManageUserScreen extends Screen implements AdminScreen {
  ///...
}

class AdminRedirector implements RestrictRedirector {
 @override
 bool shouldRedirect(Screen screen) {
   return screen is AdminScreen;
 }

 @override
 String? redirect(GoRouterState state) {
  /// final currentUser = ....
  return !currentUser.isAdmin ? '/home' : null;
 }
}

在上面的示例中,我们添加了 AdminScreen 接口,用于标记仅供管理员用户使用的屏幕;如果用户不允许,将被重定向到主屏幕。

AdminRedirector 实现 RestrictRedirector 接口,仅在 shouldRedirect 方法返回 true 时执行 redirect 方法,否则将跳过。在这种情况下,shouldRedirect 方法仅在当前屏幕实现 AdminScreen 时返回 true

最后一步是将自定义重定向器添加到工厂函数的 redirectors 参数中。

final router = createGoRouter(
 screens: [
  ManageUserScreen(),
 ],
 redirectors: [
  AdminRedirector(),
  ///...
 ],
);

刷新通知器

刷新监听器是 GoRouter内置功能,用于重新调用重定向逻辑,但它只接受一个监听器,我们需要在一个地方实现所有的重调用逻辑。

使用此包,您可以添加一个或多个监听器来重新调用重定向逻辑,这样您就可以分离逻辑,使其易于控制、可读且相互独立。

将一个或多个监听器实例传递给工厂函数的 refreshNotifiers 参数。

class AuthService with ChangeNotifier implements LoggedInState {
  bool _loggedIn = true;
  
  @override
  bool loggedIn() => _loggedIn;
  
  void logout() {
    _loggedIn = false;
    notifyListeners();
  }
}

class PromotionService with ChangeNotifer {
 void activate() {
  ///....
  notifyListeners();
 }
}

final authService = AuthService();
final router = createGoRouter(
 screens: [
  ManageUserScreen(),
 ],
 redirectors: [
  AuthRedirector(
   state: authService,
   guestRedirectPath: '/login',
   userRedirectPath: '/home',
  ),
  ///...
 ],
 refreshNotifiers: [
  authService,
  PromotionService(),
  /// GoRouterRefreshStream(...),
 ]
);

在上面的示例中,AuthService 类是一个刷新通知器,并实现了 LoggedInState 接口,以提供当前用户是已登录还是访客的状态。当用户注销时,AuthRedirector 将被调用,并将他们重定向到登录屏幕。另一方面,当激活促销时(例如,将用户移动到促销屏幕),促销服务将调用重定向逻辑。

GitHub

查看 Github