在 Flutter 中使用服务定位器管理路由

问题

在 Flutter 中进行页面跳转需要访问到当前的 BuildContext 及目标页面的 Widget,不利于代码的组织管理。

onPressed: () {
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => SecondRoute()),
  );
}

服务定位器(Service Locator)

The service locator pattern is a design pattern used in software development to encapsulate the processes involved in obtaining a service with a strong abstraction layer. This pattern uses a central registry known as the “service locator”, which on request returns the information necessary to perform a certain task. Proponents of the pattern say the approach simplifies component-based applications where all dependencies are cleanly listed at the beginning of the whole application design, consequently making traditional dependency injection a more complex way of connecting objects. Critics of the pattern argue that it is an anti-pattern which obscures dependencies and makes software harder to test.

Wikipedia - Service locator pattern

简单来说服务定位器模式可以将获得服务的过程封装在服务定位器中,应用程序的代码不需要管理维护服务的大量依赖,使应用程序的结构得到良好的分离。

应用

创建 NavigationService

首先创建 NavigationService 处理页面的跳转,它包含两个方法:navigateTogoBack 分别负责页面的跳转和返回。

navigatorKey 是用来创建 NavigatorKey,通过它可以直接得到 Navigator 而不需要使用 Navigator.ofBuildContext 中获取 Navigator

class NavigationService {
  final GlobalKey<NavigatorState> navigatorKey =
      new GlobalKey<NavigatorState>();

  Future<dynamic> navigateTo(String routeName, {dynamic arguments}) {
    return navigatorKey.currentState!
        .pushNamed(routeName, arguments: arguments);
  }

  void goBack() {
    navigatorKey.currentState!.pop();
  }
}

注册 locator

get_it 的 setup 中注册 NavigationService

GetIt locator = GetIt.instance;

void setupLocator() {
  locator.registerLazySingleton(() => NavigationService());
}

routes 生成

router.dart 中创建路由生成函数。页面参数的可以通过 RouteSettings 来传递。

const String HomePageRoute = '/';

const String LoginPageRoute = '/login';

const String WebViewPageRoute = '/web_view';

Route<dynamic> generateRoute(RouteSettings settings) {
  switch (settings.name) {
    case HomePageRoute:
      return MaterialPageRoute(builder: (context) => HomePage(title: 'Home'));
    case LoginPageRoute:
      return MaterialPageRoute(builder: (context) => LoginPage(title: 'Login'));
    case WebViewPageRoute:
      var args = settings.arguments! as Map<String, dynamic>;
      var url = args['url'] as String;
      var title = args['title'] as String;
      return MaterialPageRoute(
          builder: (context) => WebViewPage(
                url: url,
                title: title,
              ));
    default:
      return MaterialPageRoute(builder: (context) => HomePage(title: 'Home'));
  }
}

在 MaterialApp 上定义路由

import 'package:flutter/material.dart';
import 'services/navigation_service.dart';
import 'router.dart' as router;
import 'get_it.dart';

void main() {
  setupLocator();

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: locator<NavigationService>().navigatorKey,
      onGenerateRoute: router.generateRoute,
      initialRoute: router.HomePageRoute
    );
  }
}

应用中调用

完成上述操作后,我们便可以在应用中的任意位置通过 locator 访问 NavigationService 来进行页面跳转:

class _HomePageState extends State<HomePage> {
  final NavigationService _navigationService = locator<NavigationService>();

  _onPressed () {
    _navigationService.navigateTo(router.LoginPageRoute);
  }
}

此时页面跳转仅需要访问到 locatorNavigationService 和目标页路由的 name

参考