0
点赞
收藏
分享

微信扫一扫

[译]Flutter Favorite之路由包beamer

快速开始

最简单的使用是用 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 ,在屏幕上显示选中的页面栈。


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

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 感觉像是同时使用了多个 Navigatorpush/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(反转导航); 向上反转时序.

向上 (从栈中弹出页面)

向上导航是指导航到当前页面栈的前一个页面。就是大家熟知的弹出,通过 Navigatorpop/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 。

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


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

核心概念

从最顶层来看,BeamerRouter 的包装,它使用 了自身的对 RouterDelegateRouteInformationParser 的实现。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 创建和保存属于它的状态,用于构建一个页面栈。

image.png

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, 后者会强制实现 fromRouteInformationtoRouteInformation

完事的示例参考 这里。

一个自定义的 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

这里我们使用 BeamerParserBeamerDelegate 在 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 时,需要实现两个方法: pathPatternsbuildPages

    • 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个选项。

image.png

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

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

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,
  ),
]; 
  • Authentication Bloc: 该示例展示了如何使用 BeamGuard 和 flutter_bloc 为授权流程进行状态管理。
  • Bottom Navigation: 该示例是使用底部导航栏时把 Beamer 放到组件树里。
  • Bottom Navigation With Multiple Beamers: 每个标签页有一个 Beamer
  • Nested Navigation: 嵌套路由的侧边抽屉栏。

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

  • Animated Rail: 使用 animated_rail 包的示例。

迁移

从 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
  • 传递 beamLocationsBeamerDelegate 代替 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 ,这样我们可以讨论您的想法。

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

  • devj3ns
  • ggirotto
  • youssefali424
  • schultek
  • hatem-u
  • matuella
  • jeduden
  • omacranger
  • spicybackend
  • ened
  • AdamBuchweitz
  • nikitadol
  • gabriel-mocioaca
  • piyushchauhan
  • britannio
  • satyajitghana
  • Zambrella
  • luketg8
  • timshadel
  • definev

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

get=https%3A%2F%2Fgithub.com%2Fpiyushchauhan “https://github.com/piyushchauhan”)

  • britannio
  • satyajitghana
  • Zambrella
  • luketg8
  • timshadel
  • definev

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

最后

按照国际惯例,给大家分享一套十分好用的Android进阶资料:《全网最全Android开发笔记》。

整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。

图片

图片

因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。

如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。

(一)架构师必备Java基础

1、深入理解Java泛型

2、注解深入浅出

3、并发编程

4、数据传输与序列化

5、Java虚拟机原理

6、高效IO

……

图片

(二)设计思想解读开源框架

1、热修复设计

2、插件化框架设计

3、组件化框架设计

4、图片加载框架

5、网络访问框架设计

6、RXJava响应式编程框架设计

……

图片

(三)360°全方位性能优化

1、设计思想与代码质量优化

2、程序性能优化

  • 启动速度与执行效率优化
  • 布局检测与优化
  • 内存优化
  • 耗电优化
  • 网络传输与数据储存优化
  • APK大小优化

3、开发效率优化

  • 分布式版本控制系统Git
  • 自动化构建系统Gradle

……

图片

(四)Android框架体系架构

1、高级UI晋升

2、Android内核组件

3、大型项目必备IPC

4、数据持久与序列化

5、Framework内核解析

……

图片

(五)NDK模块开发

1、NDK开发之C/C++入门

2、JNI模块开发

3、Linux编程

4、底层图片处理

5、音视频开发

6、机器学习

……

图片

(六)Flutter学习进阶

1、Flutter跨平台开发概述

2、Windows中Flutter开发环境搭建

3、编写你的第一个Flutter APP

4、Flutter Dart语言系统入门

……

图片

(七)微信小程序开发

1、小程序概述及入门

2、小程序UI开发

3、API操作

4、购物商场项目实战

……

图片

(八)kotlin从入门到精通

1、准备开始

2、基础

3、类和对象

4、函数和lambda表达式

5、其他

……

图片

图片

举报

相关推荐

0 条评论