一文理解flutter中的路由工作原理

25 阅读4分钟

要理解 Flutter 的 Navigator 1.0 工作原理,我们需要深入研究其背后的实现逻辑,并结合源代码进行详细解释。Navigator 1.0 的设计目标是管理应用中的页面导航,这通过维护一个路由栈(stack of routes)来实现。每个页面(route)都可以推入栈顶,或者从栈顶弹出。

1. Navigator 1.0 的核心概念

  • Navigator:负责管理路由栈的核心类,它提供了推送(push)、弹出(pop)页面等功能。
  • Route:表示导航栈中的一个页面,它是一个抽象类,有多个实现如 MaterialPageRoute
  • RouteStack:Navigator 内部维护的一个栈,用于管理路由的顺序和状态。

2. Navigator 1.0 的工作流程

Navigator 1.0 的核心是对路由栈的管理。当你调用 Navigator.push 时,一个新的路由被创建并推入栈顶;当你调用 Navigator.pop 时,栈顶的路由被移除。

2.1 Navigator.push 的实现

让我们从 Navigator.push 的实现开始看起:

dart
复制代码
static Future<T?> push<T extends Object?>(BuildContext context, Route<T> route) {
  return Navigator.of(context).push(route);
}

Navigator.push 调用了 Navigator.of(context).push(route),让我们看看内部 NavigatorStatepush 方法:

dart
复制代码
@override
Future<T?> push<T extends Object?>(Route<T> route) {
  assert(!_debugLocked);
  assert(() {
    _debugLocked = true;
    return true;
  }());
  _history.add(route);
  route._navigator = this;
  route.install();
  if (route.isFirst) {
    _first?.markNeedsBuild();
  } else {
    final ModalRoute<dynamic>? previousRoute = _history[_history.length - 2];
    previousRoute?.didChangeNext(route);
  }
  route.didPush();
  _cancelActivePointers();
  route.didChange();
  return route.popped;
}

这里发生了一系列操作:

  1. _history.add(route) :将新路由添加到路由栈中。
  2. route.install() :安装路由,通常包括将页面挂载到 widget 树上。
  3. route.didPush() :通知路由已经被推入栈顶。
  4. _cancelActivePointers() :取消所有正在进行的触摸事件,以防止推送过程中发生意外事件。
  5. route.didChange() :通知路由状态发生变化。

最终,push 方法返回一个 Future,该 Future 会在路由完成推入时完成。

2.2 Navigator.pop 的实现

类似地,Navigator.pop 的实现如下:

dart
复制代码
static bool pop<T extends Object?>(BuildContext context, [ T? result ]) {
  return Navigator.of(context).maybePop(result);
}

Navigator.pop 实际上调用了 maybePop,这允许我们在弹出之前进行一些检查:

dart
复制代码
bool maybePop<T extends Object?>([ T? result ]) {
  if (_history.isNotEmpty) {
    final Route<T> route = _history.last as Route<T>;
    final bool returnValue = route.willPop();
    if (returnValue) {
      pop<T>(result);
      return true;
    }
    return false;
  }
  return false;
}

如果栈不为空,maybePop 会调用 route.willPop() 以确定是否应该弹出。如果确定要弹出,则会调用 pop

dart
复制代码
void pop<T extends Object?>([ T? result ]) {
  assert(!_debugLocked);
  assert(() {
    _debugLocked = true;
    return true;
  }());
  final Route<dynamic>? route = _history.isNotEmpty ? _history.removeLast() : null;
  if (route != null) {
    route.didComplete(result);
    route.dispose();
  }
  _cancelActivePointers();
  route?.didChange();
}

这里的关键步骤是:

  1. _history.removeLast() :从路由栈中移除栈顶路由。
  2. route.didComplete(result) :完成路由,并通过 Future 返回结果。
  3. route.dispose() :释放与路由相关的资源。
  4. _cancelActivePointers() :与推送时类似,取消正在进行的触摸事件。
  5. route?.didChange() :通知剩余路由栈的栈顶路由状态已更改。

3. Route 的内部工作原理

Route 是 Navigator 管理的最基本的单位,Flutter 提供了多种类型的 Route,比如 MaterialPageRouteCupertinoPageRoute

3.1 Route 生命周期

Route 的生命周期包括几个关键阶段:

  • install() :当 Route 被推入栈时调用,用于初始化与构建页面的相关工作。
  • didPush() :当 Route 被 Navigator 推入栈时调用,用于通知 Route 它现在在栈顶。
  • willPop() :在 Route 被弹出之前调用,以允许阻止弹出操作。
  • didComplete() :当 Route 被弹出并且导航完成时调用,用于清理资源并通知结果。
  • dispose() :当 Route 不再需要时调用,用于释放资源。

4. NavigatorState 和 InheritedWidget

Navigator 是一个 StatefulWidget,它的状态由 NavigatorState 来管理。Navigator.of(context) 实际上是在 widget 树中查找 NavigatorInheritedWidget,然后获取其状态。

dart
复制代码
static NavigatorState of(BuildContext context, { bool rootNavigator = false }) {
  final NavigatorState? navigator = rootNavigator
    ? context.findRootAncestorStateOfType<NavigatorState>()
    : context.findAncestorStateOfType<NavigatorState>();
  assert(() {
    if (navigator == null) {
      throw FlutterError(
        'Navigator operation requested with a context that does not include a Navigator.\n'
        'The context used to push or pop routes from the Navigator must be that of a '
        'widget that is a descendant of a Navigator widget.'
      );
    }
    return true;
  }());
  return navigator!;
}

这里的 findAncestorStateOfTypefindRootAncestorStateOfType 是 Flutter 用于在 widget 树中查找状态的方法,这允许 Navigator 访问到与当前上下文相关联的 NavigatorState

5. Navigator 1.0 的优缺点

优点:

  • 简单易用:Navigator 1.0 提供了简单直接的 API,使得页面导航非常直观。
  • 适合简单应用:对于简单的页面切换和导航需求,Navigator 1.0 足够高效。

缺点:

  • 灵活性不足:Navigator 1.0 的命令式导航方式在处理复杂导航逻辑、深度链接等需求时显得力不从心。
  • 维护困难:随着应用复杂度的增加,手动管理路由栈可能变得难以维护。