likes
comments
collection
share

[译]Flutter Favorite之路由包beamer

作者站长头像
站长
· 阅读数 40

原文链接:beamer | Flutter Package (flutter-io.cn)

多有不足,不吝赐教


beamer

[译]Flutter Favorite之路由包beamerbeamer.dev 支持空安全

处理应用在所有平台上的路由,并同步浏览器 URL 地址栏和其它内容。 Beamer 基于 Router 并实现了所有的底层逻辑。

快速开始

快速开始

最简单的使用是用 RoutesLocationBuilder 实现,这种方式产出的代码最少。对于导航场景较少的应用或者页面栈浅的应用(即页面很少堆叠在一起)来说,是很棒的选择。

class MyApp extends StatelessWidget {
  final routerDelegate = BeamerDelegate(
    locationBuilder: RoutesLocationBuilder(
      routes: {
        // Return either Widgets or BeamPages if more customization is needed
        // 返回 Widgets 或 BeamPages(如果需要更多定制的话) 
        '/': (context, state, data) => HomeScreen(),
        '/books': (context, state, data) => BooksScreen(),
        '/books/:bookId': (context, state, data) {
          // Take the path parameter of interest from BeamState
          // 从 BeamState 获取路径参数
          final bookId = state.pathParameters['bookId']!;
          // Collect arbitrary data that persists throughout navigation
          // 收集在整个导航过程中持续存在的任意数据
          final info = (data as MyObject).info;
          // Use BeamPage to define custom behavior
          // 使用 BeamPage 来自定义行为
          return BeamPage(
            key: ValueKey('book-$bookId'),
            title: 'A Book #$bookId',
            popToNamed: '/',
            type: BeamPageType.scaleTransition,
            child: BookDetailsScreen(bookId, info),
          );
        }
      },
    ),
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routeInformationParser: BeamerParser(),
      routerDelegate: routerDelegate,
    );
  }
}

RoutesLocationBuilder 会根据路径对 routes 进行选择和排序。 例如,导航到 /books/1 会匹配 routes 里的全部3个实体,然后把它们堆叠在一起。导航到 /books 会匹配routes 的前两个实体。

对应的页面被放入到 Navigator.pages 中,BeamerDelegate (重新)构建 Navigator ,在屏幕上显示选中的页面栈。


为什么我们有一个 locationBuilder ? BeamLocation 是什么?它的输出是什么?

BeamLocation 是一个实体,它基于它的 state 来决定哪个页面要进入到 Navigator.pages 里。locationBuilder 选择适当的 BeamLocation 来进一步处理收到的 RouteInformation 。 这大多数是通过验证 BeamLocation.pathPatterns 来实现。

RoutesLocationBuilder 返回 BeamLocation 的一个特殊类型 - RoutesBeamLocation,它有用于绝大多数常用的导航场景的实现。 如果 RoutesLocationBuilder 没有提供所需的行为或者足够的定制,可以扩展 BeamLocation 为进入到 Navigator.pages 的任意数量的页面栈来定义和组织行为。

深入阅读: BeamLocation [中文]BeamState[中文]

导航

导航是用 "beam" 来完成的。可以认为是在应用中传送(beam)到其它地方。 类似于 Navigator.of(context).pushReplacementNamed('/my-route'),但是 Beamer 并不限于单个页面,或者是推入栈本身。 BeamLocation 创建页面的栈,当 beam 到某个页面时,页面会被构建。 Beaming 感觉像是同时使用了多个 Navigator 的 push/pop (入栈/出栈)方法。

// Basic beaming
Beamer.of(context).beamToNamed('/books/2');

// Beaming with an extension method on BuildContext
// 使用 BuildContext 的扩展方法 Beaming
context.beamToNamed('/books/2');

// Beaming with additional data that persist 
// throughout navigation withing the same BeamLocation
// 用同一个 BeamLocation 的导航过程中的数据 Beaming。
context.beamToNamed('/book/2', data: MyObject());

导航返回

这里有两种返回的类型,即 reverse navigation(反转导航); 向上 和 反转时序.

向上 (从栈中弹出页面)

向上导航是指导航到当前页面栈的前一个页面。就是大家熟知的弹出,通过 Navigator 的 pop/maybePop 方法来完成。如果不指定其它处理,默认的 AppBarBackButton 返回按钮会调用这个方法。

Navigator.of(context).maybePop();

反转时序 ( beam 到前一个状态)

反转时序导航会导航到前面访问过的任意地方。在深度链接的情况(例如:从 /authors/3 导航到 /books/2,而不是从 /books 导航到 /books/2)下,这和弹出是不一样的。 Beamer 在 beamingHistory 历史中保持着导航历史,所以它能够导航到 beamingHistory 中的前一个时间点的入口。这称作 "beam back" (回光返照?皮一下)。

Beamer.of(context).beamBack();

Android 返回按键

集成 beam 的 Android 返回按键通过在 MaterialApp.router 中设置 backButtonDispatcher 来实现。这个分发器需要指向同一个为routerDelegate 设置的 BeamerDelegate 的引用。

MaterialApp.router(
  ...
  routerDelegate: beamerDelegate,
  backButtonDispatcher: BeamerBackButtonDispatcher(delegate: beamerDelegate),
)

BeamerBackButtonDispatcher 会首先尝试 pop (弹出),如果弹出不可用,会改为 beamBack 。如果  beamBack 返回 false (没有地方可返回),Android 的返回按钮会关闭应用,也可能是返回前一个使用的应用(通过 deep-link (深度链接)打开当前应用)。 BeamerBackButtonDispatcher 可以配置为 alwaysBeamBack (意思是不会尝试 pop (弹出))或 fallbackToBeamBack (意思是不会尝试 beamBack)。

访问最近的 Beamer

要在组件中访问路由的属性(例如,用于构建 BookDetailsScreenbookId )可以使用:

@override
Widget build(BuildContext context) {
  final beamState = Beamer.of(context).currentBeamLocation.state as BeamState;
  final bookId = beamState.pathParameters['bookId'];
  ...
}

使用 "Navigator 1.0" 

注意 "Navigator 1.0"(命令式的 push/pop 和类似的函数)可以和 Beamer 一起使用。我们已经看到 Navigator.pop 用来向上导航。这告诉我们是在使用同样的 Navigator ,只是使用了不同的 API 。

Navigator.of(context).push (或任何类似的动作) 入栈不会反映到 BeamLocation 的状态,这意味着浏览器的 URL 不会改变。可以通过 Beamer.of(context).updateRouteInformation(...) 来只更新 URL 。当然在移动端使用 Beamer 时不会有这个问题,因为看不到 URL 。

通常,每个导航场景应该是可实现的声明式(定义页面栈),而不是命令式(入栈),但是做到这一点的难度会有所不同。


对于中级和高级的用法,现在介绍一些核心概念: BeamLocation 和 BeamState

核心概念

从最顶层来看,Beamer 是 Router 的包装,它使用 了自身的对 RouterDelegate 和 RouteInformationParser 的实现。Beamer 的目标是分离【用不同的状态为 Navigator.pages 的多个类来构建页面栈】的职责,代替所有页面栈使用一个全局状态。

例如,我们想要处理所有个人资料相关的页面栈如:

  • [ ProfilePage ] (个人资料页面),
  • [ ProfilePage, FriendsPage](个人资料页面,好友页面),
  • [ ProfilePage, FriendsPage, FriendDetailsPage ](个人资料页面,好友页面,好友详细页面),
  • [ ProfilePage, SettingsPage ](个人资料页面,设定页面),
  • ...

用一些 "ProfileHandler" 来知道哪个状态对应哪个页面栈。类似地,我们想要一个 "ShopHandler" 来处理所有商店关联的页面栈。这些页面如:

  • [ ShopPage ](商店页面),
  • [ ShopPage, CategoriesPage ](商店页面,品类页面),
  • [ ShopPage, CategoriesPage, ItemsPage ](商店页面,品类页面,商品页面),
  • [ ShopPage, CategoriesPage, ItemsPage, ItemDetailsPage ](商店页面,品类页面,商品页面,商品详细页面),
  • [ ShopPage, ItemsPage, ItemDetailsPage ](商店页面,商品页面,商品详细页面),
  • [ ShopPage, CartPage ](商店页面,购物车页面),
  • ...

这些 "Handlers" 被称为 BeamLocation

BeamLocation 自身无法工作。当 RouteInformation 作为 beaming 的初始状态或者结果通过深度链接进入应用时,需要决定哪个 BeamLocation 如何进一步处理 RouteInformation 和为 Navigator 构建页面。 这是 BeamerDelegate.locationBuilder 的工作,它会接收 RouteInformation , 然后根据其 pathPatterns (路径模式)传给正确的 BeamLocation

之后 BeamLocation 会从 RouteInformation 创建和保存属于它的状态,用于构建一个页面栈。

[译]Flutter Favorite之路由包beamer

BeamLocation

Beamer 中最重要的构成是 BeamLocation , 它呈现一个页面或多个页面的状态。

BeamLocation 有三个重要角色:

  • 知道它能处理哪些 URI :pathPatterns
  • 知道如何构建页面栈 :buildPages
  • 保持状态( state ),状态为上面两者之间提供链接。

BeamLocation 是一个抽象类,需要被实现。具有多个 BeamLocation 的目的是为了在应用中从架构上分离不相关的 “场所” 。例如,BooksLocation 可以处理所有和书相关的页面, ArticlesLocation 可以处理所有和文章相关的内容。

以下是 BeamLocation 的一个例子:

class BooksLocation extends BeamLocation<BeamState> {
  @override
  List<Pattern> get pathPatterns => ['/books/:bookId'];

  @override
  List<BeamPage> buildPages(BuildContext context, BeamState state) {
    final pages = [
      const BeamPage(
        key: ValueKey('home'),
        child: HomeScreen(),
      ),
      if (state.uri.pathSegments.contains('books'))
        const BeamPage(
          key: ValueKey('books'),
          child: BooksScreen(),
        ),
    ];
    final String? bookIdParameter = state.pathParameters['bookId'];
    if (bookIdParameter != null) {
      final bookId = int.tryParse(bookIdParameter);
      pages.add(
        BeamPage(
          key: ValueKey('book-$bookIdParameter'),
          title: 'Book #$bookIdParameter',
          child: BookDetailsScreen(bookId: bookId),
        ),
      );
    }
    return pages;
  }
}

BeamState

BeamState 是一个预制的状态,它可以用于自定义的 BeamLocation 。 它保持着各种 URI 的属性如 pathPatternSegments (选择路径模式的片段,一个 BeamLocation 能支持多个这样的片段)、pathParametersqueryParameters

自定义状态

任何类都可以用作 BeamLocation 的状态,例如 ChangeNotifier 。唯一的要求是 BeamLocation 的状态混合(mix) RouteInformationSerializable, 后者会强制实现 fromRouteInformation 和 toRouteInformation

完事的示例参考 这里

一个自定义的 BooksState :

class BooksState extends ChangeNotifier with RouteInformationSerializable {
  BooksState([
    bool isBooksListOn = false,
    int? selectedBookId,
  ])  : _isBooksListOn = isBooksListOn,
        _selectedBookId = selectedBookId;

  bool _isBooksListOn;
  bool get isBooksListOn => _isBooksListOn;
  set isBooksListOn(bool isOn) {
    _isBooksListOn = isOn;
    notifyListeners();
  }

  int? _selectedBookId;
  int? get selectedBookId => _selectedBookId;
  set selectedBookId(int? id) {
    _selectedBookId = id;
    notifyListeners();
  }

  void updateWith(bool isBooksListOn, int? selectedBookId) {
    _isBooksListOn = isBooksListOn;
    _selectedBookId = selectedBookId;
    notifyListeners();
  }

  @override
  BooksState fromRouteInformation(RouteInformation routeInformation) {
    final uri = Uri.parse(routeInformation.location ?? '/');
    if (uri.pathSegments.isNotEmpty) {
      _isBooksListOn = true;
      if (uri.pathSegments.length > 1) {
        _selectedBookId = int.parse(uri.pathSegments[1]);
      }
    }
    return this;
  }

  @override
  RouteInformation toRouteInformation() {
    String uriString = '';
    if (_isBooksListOn) {
      uriString += '/books';
    }
    if (_selectedBookId != null) {
      uriString += '/$_selectedBookId';
    }
    return RouteInformation(location: uriString.isEmpty ? '/' : uriString);
  }
}

然后使用上面的状态的 BeamLocation 会查找以下内容。注意如果自定义状态不是 ChangeNotifier的话,并不是所有这些都需要重写。

class BooksLocation extends BeamLocation<BooksState> {
  BooksLocation(RouteInformation routeInformation) : super(routeInformation);

  @override
  BooksState createState(RouteInformation routeInformation) =>
      BooksState().fromRouteInformation(routeInformation);

  @override
  void initState() {
    super.initState();
    state.addListener(notifyListeners);
  }

  @override
  void updateState(RouteInformation routeInformation) {
    final booksState = BooksState().fromRouteInformation(routeInformation);
    state.updateWith(booksState.isBooksListOn, booksState.selectedBookId);
  }

  @override
  void disposeState() {
    state.removeListener(notifyListeners);
    super.disposeState();
  }

  @override
  List<Pattern> get pathPatterns => ['/books/:bookId'];

  @override
  List<BeamPage> buildPages(BuildContext context, BooksState state) {
    final pages = [
      const BeamPage(
        key: ValueKey('home'),
        child: HomeScreen(),
      ),
      if (state.isBooksListOn)
        const BeamPage(
          key: ValueKey('books'),
          child: BooksScreen(),
        ),
    ];
    if (state.selectedBookId != null) {
      pages.add(
        BeamPage(
          key: ValueKey('book-${state.selectedBookId}'),
          title: 'Book #${state.selectedBookId}',
          child: BookDetailsScreen(bookId: state.selectedBookId),
        ),
      );
    }
    return pages;
  }
}

使用自定义 BooksState 时,可以通过下面的写法完全使用声明式:

onTap: () {
  final state = context.currentBeamLocation.state as BooksState;
  state.selectedBookId = 3;
},

注意 Beamer.of(context).beamToNamed('/books/3') 会生成同样的结果。

用法

使用 Beamer (或任何 Router ),必须构造带 .router 构造器(更多内容可查看Router documentation)的  *App 组件。和所有 *App 的常规属性一起,我们必须提供:

  • routeInformationParser 解析传入的 URI 。
  • routerDelegate 控制(重新)构建 Navigator

这里我们使用 BeamerParser 和 BeamerDelegate 在 Beamer 中的实现,给这两者传递所需的 LocationBuilder 。用最简单的形式,LocationBuilder 只是一个函数,它接收当前的 RouteInformation (和 BeamParameters (在这里并 不重要) ) ,返回基于 URI 或 其它状态属性的 BeamLocation

class MyApp extends StatelessWidget {
  final routerDelegate = BeamerDelegate(
    locationBuilder: (routeInformation, _) {
      if (routeInformation.location!.contains('books')) {
        return BooksLocation(routeInformation);
      }
      return HomeLocation(routeInformation);
    },
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerDelegate: routerDelegate,
      routeInformationParser: BeamerParser(),
      backButtonDispatcher:
          BeamerBackButtonDispatcher(delegate: routerDelegate),
    );
  }
}

如果我们不想自定义一个 locationBuilder 函数,这里还有两个可用的选项。

BeamLocation 列表

BeamerLocationBuilder 可以指定 BeamLocation 列表。该构建器将根据其pathPatterns (路径模式)自动选择正确的 BeamLocation

final routerDelegate = BeamerDelegate(
  locationBuilder: BeamerLocationBuilder(
    beamLocations: [
      HomeLocation(),
      BooksLocation(),
    ],
  ),
);

路由 Map

我们可以给 RoutesLocationBuilder 指定路由 Map ,如 快速开始[中文]中提到的。 这会完全移除自定义 BeamLocation 的需要,但是也会提供一个最少量的定制。尽管如此,通配符和路径参数会和其它所有选项一起被支持。

final routerDelegate = BeamerDelegate(
  locationBuilder: RoutesLocationBuilder(
    routes: {
      '/': (context, state, data) => HomeScreen(),
      '/books': (context, state, data) => BooksScreen(),
      '/books/:bookId': (context, state, data) =>
        BookDetailsScreen(
          bookId: state.pathParameters['bookId'],
        ),
    },
  ),
);

守护

要守护特定的路由,例如防止未授权的用户访问,全局的 BeamGuard 可通过 BeamerDelegate.guards 属性设置。 一个很常用的例子是如果用户未被授权的话, BeamGuard 来守护任意未 /login (登录) 的路由,然后重定向到 /login

BeamGuard(
  // on which path patterns (from incoming routes) to perform the check
  // 通过路径模式(传入的路由)来进行 检查
  pathPatterns: ['/login'],
  // perform the check on all patterns that **don't** have a match in pathPatterns
  // 对所有路径模式里未匹配的模式进行检查
  guardNonMatching: true,
  // return false to redirect
  // 返回 false 来重定向
  check: (context, location) => context.isUserAuthenticated(),
  // where to redirect on a false check
  // 返回 false 时重定向的位置
  beamToNamed: (origin, target) => '/login',
)

注意本例中 guardNonMatching 的使用。这很重要,因为守护(这里有很多守护,每一个守护不同的方面)会递归运行在前一个守护的输出上直到到达一个 “安全” 的路由。一个常见的错误是安装带 pathBlueprints:['*'] 的守护来守护所有,但是所有也包括 /login (这是一个 “安全” 的路由),这样就导致陷入了一个无限循环:

  • 检查 /login
  • 用户未授权
  • beam 到 /login
  • 检查 /login
  • 用户未授权
  • beam 到 /login
  • 。。。

当然,需要不使用 guardNonMatching 。有时我们只想守护少量明确指定的路由。这里有个和上面同样角色的守护,默认实现为 guardNonMatching: false :

BeamGuard(
  pathBlueprints: ['/profile/*', '/orders/*'],
  check: (context, location) => context.isUserAuthenticated(),
  beamToNamed: (origin, target) => '/login',
)

嵌套导航

需要嵌套导航时,可以把 Beamer 放在组件树中进行嵌套导航的任何位置。这并不会限制一个应用里有多少 Beamer 。 常用的使用场景是底部导航栏(查看示例),如下:

class MyApp extends StatelessWidget {
  final routerDelegate = BeamerDelegate(
    initialPath: '/books',
    locationBuilder: RoutesLocationBuilder(
      routes: {
        '/*': (context, state, data) {
          final beamerKey = GlobalKey<BeamerState>();

          return Scaffold(
            body: Beamer(
              key: beamerKey,
              routerDelegate: BeamerDelegate(
                locationBuilder: BeamerLocationBuilder(
                  beamLocations: [
                    BooksLocation(),
                    ArticlesLocation(),
                  ],
                ),
              ),
            ),
            bottomNavigationBar: BottomNavigationBarWidget(
              beamerKey: beamerKey,
            ),
          );
        }
      },
    ),
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerDelegate: routerDelegate,
      routeInformationParser: BeamerParser(),
    );
  }
}

一般注意事项

  • 当扩展 BeamLocation 时,需要实现两个方法:  pathPatterns 和 buildPages

    • buildPages 返回页面栈,当 beam 到这些页面时, Navigator 会构建页面。然后 BeamerpathPatterns 会决定哪个 BeamLocation 处理哪个 URI 。

    • BeamLocation 会将 URI 的查询和路径模式保持在其 BeamState 中。如果要从浏览器获取路径参数,那 pathPatterns 中的 : 是必须的。

  • BeamPage 的 child 是呈现应用屏幕/界面的任意组件。

    • key 对于 Navigatar 优化重新构建是重要的。这在 "页面状态" 中应该是唯一值。

    • BeamPage 默认创建 MaterialPageRoute , 但是也可以通过把 BeamPage.type 设置为可用的 BeamPageType 来选择其它的变换。

提示和常见问题

  • 可在 runApp() 之前调用 Beamer.setPathUrlStrategy() 移除 URL 中的 #
  • BeamPage.title 默认用来设置浏览器的标签页的标题,可以通过将 BeamerDelegate.setBrowserTabTitle 设置为 false 来选择移除。
  • 热加载中状态丢失

示例

这里查看所有示例(带 gif )

  • Location Builders: 基于这篇文章 重新创建示例应用,可以在这里学习很多关于 Navigator 2.0 的知识。该示例展示了 locationBuilder 的所有3个选项。

[译]Flutter Favorite之路由包beamer

  • Advanced Books: 作为进阶,添加了更多流程来展示 Beamer 的强大能力。

  • Deep Location: 在已经堆入栈的多个页面中,可以在应用中马上 beam 到一个位置,然后一个接一个地弹出它们或简单地 beamBack 到跳转来的地方。注意 beamToNamedbeamBackOnPop 参数有助于覆写使用 beamBack 进行 AppBar 的 pop 。

ElevatedButton(
    onPressed: () => context.beamToNamed('/a/b/c/d'),
    //onPressed: () => context.beamToNamed('/a/b/c/d', beamBackOnPop: true),
    child: Text('Beam deep'),
),
  • Provider: 可以覆写 BeamLocation.builder 来提供一些数据用于整个 location,即用于所有的 pages (页面)。
// 在你的 BeamLocation 实现中
@override
Widget builder(BuildContext context, Navigator navigator) {
  return MyProvider<MyObject>(
    create: (context) => MyObject(),
    child: navigator,
  );
}
  • Guards: 可以定义全局守护(例如,授权守护)或用于保持特定的状态安全的 BeamLocation.guards 。
// BeamerDelegate 中的全局守护
BeamerDelegate(
  guards: [
    // 如果用户没有授权,则通过 beam 到 /login 来守护 /books 和 /books/* :
    BeamGuard(
      pathBlueprints: ['/books', '/books/*'],
      check: (context, location) => context.isAuthenticated,
      beamToNamed: (origin, target) => '/login',
    ),
  ],
  ...
),
// BeamLocation 中的当前守护
@override
List<BeamGuard> get guards => [
  // 如果用户尝试进入 books/2 则显示禁止页面。
  BeamGuard(
    pathBlueprints: ['/books/2'],
    check: (context, location) => false,
    showPage: forbiddenPage,
  ),
];

注意:  在所有的嵌套 Beamer 中,定义 BeamLocation 时和 beam 时必须指定完整的路径。

迁移

从 0.14 迁移到 1.0.0

Medium 的这篇文章 说明了两个版本间的变化,并提供了一个迁移向导。最值得注意的破坏性变化:

  • 如果使用 SimpleLocationBuilder:

代替

locationBuilder: SimpleLocationBuilder(
  routes: {
    '/': (context, state) => MyWidget(),
    '/another': (context, state) => AnotherThatNeedsState(state)
  }
)

现在是

locationBuilder: RoutesLocationBuilder(
  routes: {
    '/': (context, state, data) => MyWidget(),
    '/another': (context, state, data) => AnotherThatNeedsState(state)
  }
)
  • 如果使用一个自定义的 BeamLocation:

代替

class BooksLocation extends BeamLocation {
  @override
  List<Pattern> get pathBlueprints => ['/books/:bookId'];

  ...
}

现在是

class BooksLocation extends BeamLocation<BeamState> {
  @override
  List<Pattern> get pathPatterns => ['/books/:bookId'];

  ...
}

从 0.13 迁移到 0.14

代替

locationBuilder: SimpleLocationBuilder(
  routes: {
    '/': (context) => MyWidget(),
    '/another': (context) {
      final state = context.currentBeamLocation.state;
      return AnotherThatNeedsState(state);
    }
  }
)

现在是

locationBuilder: SimpleLocationBuilder(
  routes: {
    '/': (context, state) => MyWidget(),
    '/another': (context, state) => AnotherThatNeedsState(state)
  }
)

从 0.12 迁移到 0.13

  • BeamerRouterDelegate 重命名为 BeamerDelegate
  • BeamerRouteInformationParser 重命名为 BeamerParser
  • pagesBuilder 重命名为 buildPages
  • Beamer.of(context).currentLocation 重命名为 Beamer.of(context).currentBeamLocation

从 0.11 迁移到 0.12

  • 不再有 RootRouterDelegate ,只是重命名为 BeamerDelegate 。如果你在使用它的 homeBuilder ,使用 SimpleLocationBuilder 然后改为 routes: {'/': (context) => HomeScreen()}.
  • beamBack 的行为改为跳转到前一个 BeamState ,而不是 BeamLocation 。如果这不是你想要的,使用 popBeamLocation() 和原来的 beamback 的行为是一样的。

从 0.10 迁移到 0.11 

  • BeamerDelegate.beamLocations 现在是 locationBuilder 。看下 BeamerLocationBuilder 用于最简单的迁移。
  • Beamer 现在接收 BeamerDelegate ,而不是直接接收 BeamLocations 。
  • buildPages 现在也可以携带 state

从 0.9 迁移到 0.10

  • BeamLocation 构造器现在只接收 BeamState state 。(这里没有必要定义特殊的构造器,如果使用 beamToNamed,可以调用 super )。

  • 原来 BeamLocation 的大多数属性现在在 BeamLocation.state 里。通过 BeamLocation 来访问它们:

    • pathParameters 现在是 state.pathParameters
    • queryParameters 现在是 state.queryParameters
    • data 现在是 state.data
    • pathSegments 现在是 state.pathBlueprintSegments
    • uri 现在是 state.uri

从 0.7 迁移到 0.8

  • pages 重命名为 BeamLocation 里的 buildPages
  • 传递 beamLocations 给 BeamerDelegate 代替 BeamerParser 。 查看 用法

从 0.4 迁移到 0.5

  • 代替使用 Beamer 包装 MaterialApp ,使用 *App.router()
  • String BeamLocation.pathBlueprint 现在是 List<String> BeamLocation.pathBlueprints
  • 移除 BeamLocation.withParameters 构造器, 所有的参数用一个构造器处理。如果需要 super,请查看示例。
  • BeamPage.page 现在称作 BeamPage.child

帮助和联络

有任何问题、疑问、建议、好玩的想法。。。,在 Discord 上加入我们。

贡献

如果您注意到任何 BUG , 但是没有在 issues 中,请创建一个新 issue 。 如果您想自己进行修复或加强,非常欢迎您提出 PR ,提出 PR 之前:

  • 如果想要解决一个存在的 issue ,请首先在 issue 的评论里告诉我们。
  • 如果是有其它的加强的想法,先创建一个 issue ,这样我们可以讨论您的想法。

期待在我们尊敬的贡献者列表里看到您!


任何一种技艺达到完美~ 都会令人无法抗拒~

转载自:https://juejin.cn/post/7043058554869137439
评论
请登录