在这里插入图片描述

引言

路由管理是应用架构的核心组成部分。Flutter 的 Navigator 2.0 提供了全新的路由管理方式,支持声明式路由、URL 同步、路由堆栈控制等高级特性。相比传统的 Navigator 1.0,Navigator 2.0 更适合复杂的路由场景,特别是需要深度链接、Web 支持、复杂导航逻辑的应用。本文将深入探讨 Navigator 2.0 的实现原理和最佳实践,并结合 OpenHarmony PC 端的特性,展示如何在不同平台上实现强大的路由管理。

Navigator 2.0 的核心思想是声明式路由:路由状态由应用状态决定,而不是通过命令式的方法调用。这种设计使得路由与应用状态完全同步,支持时间旅行调试、状态持久化等高级功能。在 OpenHarmony PC 端,Navigator 2.0 的优势更加明显,因为 PC 端通常需要更复杂的导航逻辑和更好的 URL 支持。

一、Navigator 2.0 架构基础

Navigator 2.0 基于三个核心组件:RouterRouteInformationParserRouterDelegate。理解这三个组件的作用是掌握 Navigator 2.0 的关键。

Router 组件

MaterialApp.router(
  routeInformationParser: MyRouteInformationParser(),
  routerDelegate: MyRouterDelegate(),
)

代码解释: MaterialApp.router 是使用 Navigator 2.0 的入口。它需要两个参数:routeInformationParser 负责解析 URL 为路由配置,routerDelegate 负责根据路由配置构建页面。这种设计将 URL 解析和页面构建分离,使得路由系统更加灵活和可测试。

RouteInformationParser

RouteInformationParser 负责将 RouteInformation(包含 URL 和状态)解析为应用的路由配置对象。它实现了双向转换:URL 到路由配置,路由配置到 URL。这使得应用状态和 URL 可以完全同步。

RouterDelegate

RouterDelegate 负责根据路由配置构建页面。它继承自 ChangeNotifier,当路由配置变化时通知 Navigator 重建页面。RouterDelegate 是路由系统的核心,所有的导航逻辑都在这里实现。

二、路由堆栈管理

Navigator 2.0 的核心优势是可以完全控制路由堆栈。传统的 Navigator 1.0 使用命令式的方法(如 pushpop),而 Navigator 2.0 使用声明式的方式,通过更新路由配置来改变路由堆栈。

路由堆栈状态

final List<String> _pages = [];

void _pushPage(String page) {
  setState(() {
    _pages.add(page);
  });
}

void _popPage() {
  if (_pages.isNotEmpty) {
    setState(() {
      _pages.removeLast();
    });
  }
}

代码解释: 这里使用列表来管理路由堆栈。_pushPage 添加新页面到堆栈,_popPage 移除顶部页面。通过 setState 更新状态,触发路由重建。这种设计使得路由堆栈完全由应用状态控制,可以轻松实现复杂的路由逻辑,如批量导航、路由替换等。

路由堆栈可视化

if (_pages.isEmpty)
  const Text('(空)', style: TextStyle(color: Colors.grey))
else
  ..._pages.asMap().entries.map((entry) {
    return Padding(
      padding: const EdgeInsets.only(left: 16, top: 4),
      child: Text('${entry.key + 1}. ${entry.value}'),
    );
  })

代码解释: 显示路由堆栈可以帮助开发者理解当前的导航状态,也便于调试。在开发过程中,可以显示路由堆栈,帮助理解导航流程。在生产环境中,可以隐藏或简化显示。

路由堆栈操作

void _clearStack() {
  setState(() {
    _pages.clear();
  });
}

void _replaceStack(List<String> newPages) {
  setState(() {
    _pages.clear();
    _pages.addAll(newPages);
  });
}

代码解释: 除了基本的 push 和 pop,还可以实现更复杂的操作,如清空堆栈、替换整个堆栈等。这些操作在需要重置导航状态或实现特殊导航逻辑时非常有用。

三、路由配置管理

路由配置是 Navigator 2.0 的核心概念。它定义了路由的结构和参数,使得路由系统更加类型安全和可维护。

路由配置定义

class AppRouteConfig {
  final String path;
  final Map<String, String>? parameters;
  
  AppRouteConfig(this.path, {this.parameters});
  
  static AppRouteConfig fromPath(String path) {
    final uri = Uri.parse(path);
    return AppRouteConfig(
      uri.path,
      parameters: uri.queryParameters,
    );
  }
  
  String toPath() {
    final uri = Uri(path: path, queryParameters: parameters);
    return uri.toString();
  }
}

代码解释: AppRouteConfig 封装了路由配置,包括路径和参数。fromPath 从 URL 路径创建配置,toPath 将配置转换为 URL 路径。这种设计使得路由配置和 URL 之间的转换变得简单和统一。

路由配置表

final Map<String, WidgetBuilder> _routes = {
  '/home': (context) => HomePage(),
  '/products': (context) => ProductsPage(),
  '/products/:id': (context) {
    final id = ModalRoute.of(context)!.settings.arguments as String;
    return ProductDetailPage(id: id);
  },
  '/profile': (context) => ProfilePage(),
};

代码解释: 路由配置表将路径映射到页面构建器。对于带参数的路径(如 /products/:id),需要在构建器中提取参数。在实际应用中,应该使用更强大的路由库,它们提供了自动参数提取、类型安全等功能。

路由匹配

WidgetBuilder? _matchRoute(String path) {
  for (var route in _routes.keys) {
    if (_matchPath(route, path)) {
      return _routes[route];
    }
  }
  return null;
}

bool _matchPath(String pattern, String path) {
  // 实现路径匹配逻辑,支持参数占位符
  final patternParts = pattern.split('/');
  final pathParts = path.split('/');
  
  if (patternParts.length != pathParts.length) {
    return false;
  }
  
  for (int i = 0; i < patternParts.length; i++) {
    if (patternParts[i].startsWith(':')) {
      continue; // 参数占位符,匹配任何值
    }
    if (patternParts[i] != pathParts[i]) {
      return false;
    }
  }
  
  return true;
}

代码解释: 路径匹配是路由系统的核心功能。这里实现了简单的匹配逻辑,支持参数占位符(如 :id)。在实际应用中,应该使用正则表达式或专门的路由库,它们提供了更强大和灵活的匹配功能。

四、URL 同步

Navigator 2.0 的一个重要特性是 URL 同步。应用的路由状态和浏览器的 URL 可以完全同步,这对于 Web 应用和深度链接非常重要。

URL 更新

class MyRouterDelegate extends RouterDelegate<AppRouteConfig>
    with ChangeNotifier {
  AppRouteConfig? _currentConfig;
  
  
  AppRouteConfig? get currentConfiguration => _currentConfig;
  
  void _updateRoute(AppRouteConfig config) {
    _currentConfig = config;
    notifyListeners();
    // 更新浏览器 URL(Web 平台)
    Router.neglect(context, () {
      Router.of(context).routeInformationProvider!.value = 
        RouteInformation(uri: Uri.parse(config.toPath()));
    });
  }
}

代码解释: RouterDelegate 需要实现 currentConfiguration getter,返回当前的路由配置。当路由配置变化时,调用 notifyListeners() 通知 Navigator 重建。对于 Web 平台,还需要更新浏览器的 URL,使用 Router.neglect 避免触发路由解析循环。

URL 解析

class MyRouteInformationParser extends RouteInformationParser<AppRouteConfig> {
  
  Future<AppRouteConfig> parseRouteInformation(
    RouteInformation routeInformation,
  ) async {
    return AppRouteConfig.fromPath(routeInformation.uri.toString());
  }
  
  
  RouteInformation? restoreRouteInformation(AppRouteConfig configuration) {
    return RouteInformation(uri: Uri.parse(configuration.toPath()));
  }
}

代码解释: RouteInformationParser 实现 URL 和路由配置之间的双向转换。parseRouteInformation 将 URL 解析为路由配置,restoreRouteInformation 将路由配置转换为 URL。这种设计确保了 URL 和路由状态的完全同步。

五、路由守卫实现

路由守卫可以在路由切换时执行检查,如身份验证、权限检查等,保证路由的安全性。

路由守卫接口

abstract class RouteGuard {
  Future<bool> canActivate(AppRouteConfig config);
  Future<void> onActivate(AppRouteConfig config);
}

代码解释: RouteGuard 定义了路由守卫的接口。canActivate 检查是否可以激活路由,返回 false 时阻止导航。onActivate 在路由激活时调用,可以执行额外的操作,如记录日志、更新状态等。

身份验证守卫

class AuthGuard extends RouteGuard {
  final bool isAuthenticated;
  
  AuthGuard(this.isAuthenticated);
  
  
  Future<bool> canActivate(AppRouteConfig config) async {
    final protectedRoutes = ['/profile', '/settings'];
    if (protectedRoutes.contains(config.path)) {
      return isAuthenticated;
    }
    return true;
  }
  
  
  Future<void> onActivate(AppRouteConfig config) async {
    if (!isAuthenticated && protectedRoutes.contains(config.path)) {
      // 重定向到登录页面
    }
  }
}

代码解释: AuthGuard 实现了身份验证检查。对于需要登录的页面,检查用户是否已登录。如果未登录,可以重定向到登录页面。这种模式在需要权限控制的应用中非常常见。

路由守卫链

class RouteGuardChain {
  final List<RouteGuard> _guards = [];
  
  void addGuard(RouteGuard guard) {
    _guards.add(guard);
  }
  
  Future<bool> canActivate(AppRouteConfig config) async {
    for (var guard in _guards) {
      if (!await guard.canActivate(config)) {
        return false;
      }
    }
    return true;
  }
}

代码解释: 路由守卫链允许多个守卫按顺序执行。所有守卫都通过时,路由才能激活。这种设计支持复杂的权限控制逻辑,如角色检查、功能权限检查等。

六、OpenHarmony PC 端适配要点

在 OpenHarmony PC 端适配 Navigator 2.0 时,需要注意几个关键点:

多窗口支持

PC 端可能支持多窗口,不同窗口可能需要独立的路由状态。应该为每个窗口维护独立的路由堆栈,避免窗口之间的路由冲突。

浏览器集成

PC 端应用可能运行在浏览器中,需要正确处理浏览器的前进、后退按钮。Navigator 2.0 的 URL 同步功能可以自动处理这些情况,但需要正确实现 RouteInformationParser

键盘导航

PC 端用户习惯使用键盘快捷键进行导航。应该为常用的导航操作设置快捷键,如 Alt+Left 返回、Alt+Right 前进等。

响应式路由

PC 端屏幕尺寸变化范围很大,路由应该能够适应不同的屏幕尺寸。可以使用 LayoutBuilder 根据屏幕尺寸选择不同的路由配置。

七、最佳实践

使用路由库

对于复杂的应用,应该使用专门的路由库,如 go_routerauto_route 等。它们提供了更强大的功能,如自动代码生成、类型安全、嵌套路由等。

路由配置集中管理

应该将路由配置集中管理,而不是分散在各个文件中。这样可以更容易维护和更新路由,也便于实现路由守卫、路由分析等功能。

路由测试

应该为路由系统编写测试,确保各种路由场景都能正常工作。特别是路由守卫、参数解析、URL 同步等复杂功能,需要充分的测试覆盖。

性能优化

对于包含大量路由的应用,应该优化路由匹配和页面构建的性能。可以使用路由缓存、懒加载等技术提升性能。

八、Flutter 桥接 OpenHarmony 原理与 EntryAbility.ets 实现

Navigator 2.0 在 OpenHarmony 平台上的实现需要与系统的导航机制进行桥接。OpenHarmony 系统支持深度链接、应用间导航等功能,这些功能需要通过 Platform Channel 与 Flutter 的路由系统集成。

Flutter 桥接 OpenHarmony 的架构原理

Flutter 与 OpenHarmony 的桥接基于 Platform Channel 机制,这是一个异步、类型安全的通信系统。对于路由管理,OpenHarmony 系统可以处理 URL、应用间导航等。EntryAbility 接收系统的导航意图,通过 Platform Channel 传递给 Flutter 的 Navigator 2.0 系统。Flutter 解析路由配置并导航到对应页面。

路由桥接流程: 当系统或其他应用触发导航时(如深度链接、应用间跳转),OpenHarmony 系统会启动或激活应用,并将导航信息传递给 EntryAbility。EntryAbility 通过 Platform Channel 将路由信息传递给 Flutter。Flutter 的 Navigator 2.0 系统解析路由配置,更新路由堆栈,并构建对应的页面。这种桥接机制使得 Flutter 应用可以无缝处理来自系统的导航请求。

EntryAbility.ets 中的路由桥接配置

import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import { MethodChannel } from '@ohos/flutter_ohos';
import { Want } from '@kit.AbilityKit';

export default class EntryAbility extends FlutterAbility {
  private _routerChannel: MethodChannel | null = null;
  
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    this._setupRouterBridge(flutterEngine)
  }
  
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    super.onCreate(want, launchParam);
    this._handleNavigation(want);
  }
  
  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    super.onNewWant(want, launchParam);
    this._handleNavigation(want);
  }
  
  private _setupRouterBridge(flutterEngine: FlutterEngine) {
    this._routerChannel = new MethodChannel(
      flutterEngine.dartExecutor,
      'com.example.app/router'
    );
    
    // 监听 Flutter 端的路由变化
    this._routerChannel.setMethodCallHandler((call, result) => {
      if (call.method === 'updateSystemUrl') {
        // Flutter 端路由变化,更新系统 URL(Web 平台)
        const url = call.arguments['url'] as string;
        // 更新浏览器 URL(如果运行在 Web 平台)
        result.success(true);
      } else {
        result.notImplemented();
      }
    });
  }
  
  private _handleNavigation(want: Want) {
    const uri = want.uri;
    if (uri && this._routerChannel) {
      // 将路由信息传递给 Flutter
      this._routerChannel.invokeMethod('onSystemNavigation', {
        uri: uri.toString(),
        scheme: uri.scheme,
        host: uri.hostInfo,
        path: uri.path,
        parameters: uri.queryParameters
      });
    }
  }
}

代码解释: _setupRouterBridge 方法设置路由桥接。创建 MethodChannel 用于 Flutter 与 OpenHarmony 之间的路由信息传递。setMethodCallHandler 监听 Flutter 端的路由变化,当 Flutter 路由变化时,可以更新系统 URL(对于 Web 平台)。_handleNavigation 方法处理来自系统的导航请求,提取 URI 信息并通过 Platform Channel 传递给 Flutter。onCreateonNewWant 方法都会调用 _handleNavigation,确保无论是应用启动还是运行时都能正确处理导航。

Flutter 端路由桥接实现

在 Flutter 端,需要设置方法调用处理器来接收系统导航:

class RouterBridge {
  static const _routerChannel = MethodChannel('com.example.app/router');
  static Function(String)? onSystemNavigation;
  
  static void initialize() {
    _routerChannel.setMethodCallHandler((call) async {
      if (call.method == 'onSystemNavigation') {
        final args = call.arguments as Map;
        final uri = args['uri'] as String;
        onSystemNavigation?.call(uri);
      }
    });
  }
  
  static Future<void> notifyRouteChange(String route) async {
    try {
      await _routerChannel.invokeMethod('updateSystemUrl', {'url': route});
    } catch (e) {
      print('更新系统 URL 失败: $e');
    }
  }
}

代码解释: Flutter 端在应用启动时调用 initialize 方法,设置方法调用处理器。当 OpenHarmony 系统传递导航信息时,onSystemNavigation 方法会被调用。onSystemNavigation 是一个回调函数,应用可以设置这个回调来处理系统导航。notifyRouteChange 方法允许 Flutter 主动通知原生端路由变化,这对于同步 URL 很有用。

Navigator 2.0 与系统导航集成

在 Navigator 2.0 的实现中,集成系统导航:

class AppRouterDelegate extends RouterDelegate<AppRouteConfig>
    with ChangeNotifier {
  AppRouteConfig? _currentConfig;
  
  AppRouterDelegate() {
    RouterBridge.initialize();
    RouterBridge.onSystemNavigation = _handleSystemNavigation;
  }
  
  void _handleSystemNavigation(String uri) {
    final config = AppRouteConfig.fromPath(uri);
    setNewRoutePath(config);
  }
  
  
  Future<void> setNewRoutePath(AppRouteConfig configuration) async {
    _currentConfig = configuration;
    notifyListeners();
    // 通知原生端路由变化
    RouterBridge.notifyRouteChange(configuration.toPath());
  }
  
  
  AppRouteConfig? get currentConfiguration => _currentConfig;
  
  
  Widget build(BuildContext context) {
    // 根据路由配置构建页面
    return _buildPage(_currentConfig);
  }
}

代码解释: AppRouterDelegate 在构造函数中初始化路由桥接,设置系统导航回调。_handleSystemNavigation 方法处理来自系统的导航请求,解析 URI 并更新路由配置。setNewRoutePath 方法在路由变化时调用,更新当前路由配置并通知原生端。这种设计使得 Navigator 2.0 可以无缝处理来自系统的导航请求,同时保持路由状态与系统 URL 的同步。

应用间导航桥接

OpenHarmony 支持应用间导航,可以通过桥接实现:

this._routerChannel.setMethodCallHandler((call, result) => {
  if (call.method === 'navigateToApp') {
    const targetApp = call.arguments['app'] as string;
    const route = call.arguments['route'] as string;
    
    // 启动目标应用并传递路由信息
    const want: Want = {
      bundleName: targetApp,
      abilityName: 'EntryAbility',
      uri: route
    };
    
    this.context.startAbility(want).then(() => {
      result.success(true);
    }).catch((error) => {
      result.error('NAVIGATION_ERROR', error.message, null);
    });
  } else {
    result.notImplemented();
  }
});

代码解释: navigateToApp 方法实现应用间导航。接收目标应用名称和路由信息,使用 OpenHarmony 的 startAbility API 启动目标应用。如果目标应用支持深度链接,可以传递路由信息,目标应用启动后会自动导航到指定页面。这种桥接机制使得 Flutter 应用可以与其他应用进行导航交互。

路由历史管理桥接

对于需要管理路由历史的应用,可以桥接到系统的返回栈:

this._routerChannel.setMethodCallHandler((call, result) => {
  if (call.method === 'canPop') {
    // 检查是否可以返回
    result.success(true);
  } else if (call.method === 'pop') {
    // 执行返回操作
    // 可以调用系统返回或通知 Flutter 返回
    result.success(true);
  } else {
    result.notImplemented();
  }
});

代码解释: canPop 方法检查路由堆栈是否可以返回,这对于禁用系统返回键很有用。pop 方法执行返回操作,可以通知 Flutter 执行返回,或者调用系统的返回功能。这种桥接机制使得路由系统可以更好地与系统的导航机制集成。

总结

Navigator 2.0 提供了强大而灵活的路由管理方案。通过掌握 Navigator 2.0 的架构和最佳实践,我们可以创建高效、可靠的路由系统。在 OpenHarmony PC 端,充分利用 Navigator 2.0 的优势,可以创建更好的导航体验。同时,要注意多窗口支持、浏览器集成、键盘导航等问题,确保在不同场景下都能提供良好的用户体验。

路由管理是应用架构的核心,选择合适的方案对项目的成功至关重要。Navigator 2.0 虽然学习曲线较陡,但它提供了更好的控制和灵活性,特别适合复杂的应用场景。通过不断学习和实践,我们可以掌握更多路由管理技术,创建出更加优秀的应用。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐