[Flutter翻译]深入了解新的 "骨架 "应用模板

本文由 简悦SimpRead 转码,原文地址 blog.gskinner.com

多年来,Flutter在您创建一个新项目时,都会提供基本的 "计数器应用程序"。Whil......

许多年来,Flutter在你每次创建新项目时都会提供基本的 "计数器应用程序"。虽然这个例子对 "StatefulWidget "的基础概念和基本的字体/资产管理提供了一个很好的介绍,但它并不像一个 "完整的应用程序 "的结构。这使得开发者在基本的单页应用之外没有任何指导。

为了解决这个问题,Flutter团队一直在努力开发一个新的模板,其目标是。

  • 让开发者更容易在Counter应用之外继续他们的学习之旅。
  • 与原来的Counter应用程序模板并存。它不是一个替代物。

2021年6月,新模板被合并到master,你可以用这个命令创建它。

   flutter create -t skeleton new_flutter_template   

复制代码

提示:如果您想在不创建新项目的情况下查看代码,您可以检查Flutter资源库中的模板定义文件.

这里面有什么?

该模板试图提供一些最佳实践,同时避免对其他方面进行评判。让我们深入了解一些具体的主题。

包结构

这个 "骨架 "模板比Counter应用程序大得多,有9个文件,分布在3个包中。

该模板按照功能而不是类型来组织代码,所以所有设置的视图、控制器和服务都被归入同一个包。

这是一种常见的方法,但也是编程中一个古老的辩论。一般的共识似乎是,在文件数量较少的情况下,按类型组织是有效的(例如,每个类型<10),但随着事情的发展,按功能组织往往更有效。

状态管理

在某种程度上,Flutter团队避免用这个模板对状态管理进行评论。没有使用状态管理库。没有真正的反应式数据绑定或数据注入。相反,一个简单的 "ChangeNotifier "被用来创建一个 "SettingsController",该控制器通过构造参数被手动传递给 "SettingsView"。

粗略的说,这个应用程序被配置成一个简单的MVC应用程序,并带有一个服务层(MVC+S)。SettingsController是一个组合的模型/控制器对象,它的构造函数中包含一个SettingsService的实例。

// Set up the SettingsController, which will glue user settings to multiple
// Flutter Widgets.
final settingsController = SettingsController(SettingsService());
复制代码

为了在设置发生变化时重建视图,模板简单地将ChangeNotifier绑定到应用程序树的顶部,使用有点混乱的名字 AnimatedBuilder

return AnimatedBuilder(
  animation: settingsController,
  builder: (BuildContext context, Widget? child) {
  return MaterialApp(...);
});
复制代码

不要被这里的 "AnimatedBuilder "的名字所迷惑,这里没有动画发生,这个小部件被称为 "ListenableBuilder "更为正确。它只是在每次settingsController调用notifyListeners时运行一次构建器。这和settingsController.addListener(() => setState((){})的想法是一样的,但不需要使用StatefulWidget

这不是很优化,因为它在任何变化时都会构建整个树,但是它确实展示了如何轻松地将ChangeNotifier绑定到树上并以非常简单的方式触发重建的基本原理。

导航

许多开发者可能在等待新的Router API(又称Nav 2)的方向,但与状态管理类似,Flutter团队对此也是 "不予置评"。

该模板使用经典的路由风格,有简单的命名路由和onGenerateRoute处理程序。

return MaterialApp(
...
// Define a function to handle named routes in order to support
// Flutter web url navigation and deep linking.
onGenerateRoute: (RouteSettings routeSettings) {
  return MaterialPageRoute<void>(
    settings: routeSettings,
    builder: (BuildContext context) {
      switch (routeSettings.name) {
        case SettingsView.routeName:
          return SettingsView(controller: settingsController);
        case SampleItemDetailsView.routeName:
          return const SampleItemDetailsView();
        case SampleItemListView.routeName:
        default:
          return const SampleItemListView();
      }
    });});
复制代码

正如评论中所指出的,这种结构将使你在网络和应用程序中获得基本的深度链接支持。然而,它不会让你在网络上获得适当的返回/前进支持,因为它没有使用新的Routerapi。

这是可以理解的,为什么团队决定在这里坚持使用经典导航,否则这个例子的大小和复杂性就会大大增加。这势必会让一些希望在这里找到一些方向的开发者失望。

本地化

该模板很好地演示了如何使用l10n.yaml文件和flutter_localizations包进行本地化。

// Provide the generated AppLocalizations to the MaterialApp. This
// allows descendant Widgets to display the correct translations
// depending on the user's locale.
localizationsDelegates: const [
  AppLocalizations.delegate,
  GlobalMaterialLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
  GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
  Locale('en', ''), // English, no country code
],

// Use AppLocalizations to configure the correct application title
// depending on the user's locale.
// The appTitle is defined in .arb files found in the localization directory.
onGenerateTitle: (BuildContext context) =>
    AppLocalizations.of(context)!.appTitle,
复制代码

这是一个很好的例子,因为它展示了如何创建自定义字符串,以及为内置的flutter部件启用本地化。根据Flutter文档,添加上述代码后。

MaterialCupertino包现在应该被正确地本地化为78种支持的语言之一。小工具应该适应本地化的信息,以及正确的从左到右和从右到左的布局。_

你可以查看Flutter的文档,了解关于这个主题的更多信息

设置

在设置方面,模板做了一些有趣的事情。

首先,在渲染 "MaterialApp "之前,它执行了一次加载。这在实际应用中是很常见的,因为你经常想在显示任何样式的页面之前获取主题,所以很高兴看到这个功能的加入。

  // Set up the SettingsController, which will glue user settings to multiple
  // Flutter Widgets.
  final settingsController = SettingsController(SettingsService());

  // Load the user's preferred theme while the splash screen is displayed.
  // This prevents a sudden theme change when the app is first displayed.
  await settingsController.loadSettings();

  // Run the app and pass in the SettingsController. The app listens to the
  // SettingsController for changes, then passes it further down to the
  // SettingsView.
  runApp(MyApp(settingsController: settingsController));
复制代码

然后,它展示了你如何使用一个保存的枚举值,在明暗模式之间切换。

// Define a light and dark color theme. Then, read the user's
// preferred ThemeMode (light, dark, or system default) from the
// SettingsController to display the correct theme.
          theme: ThemeData(),
          darkTheme: ThemeData.dark(),
          themeMode: settingsController.themeMode,
复制代码

修复

该模板提供了一个初步的例子,说明如何在你的MaterialApp中使用新的Restoration API

// Providing a restorationScopeId allows the Navigator built by the
// MaterialApp to restore the navigation stack when a user leaves and
// returns to the app after it has been killed while running in the
// background.
restorationScopeId: 'app',
复制代码

除了这一条评论之外,没有太多的关于恢复的解释,但是看到它的存在是很好的,至少可以让新的开发者注意到这个问题。

结束语

总的来说,新的模板对于来自 "CounterApp "的开发者来说是一个很好的下一步,但对于那些寻求现实世界指导的有经验的开发者来说,它可能还不够。

例如,这里展示的状态管理方法在一个完整的应用程序中并不具有可扩展性或可操作性。如果能看到某种数据注入就更好了,即使是作为一个简单的 "InheritedWidget "或新的 "SharedAppData "API来构建,并注意在特定事物变化时优化重建区域。

导航是这里另一个令人失望的缺失。不能制作新的Router API的简单演示这一事实说明了该API的学习曲线。话虽如此,在我自己对新的 "路由器 "API进行了广泛的实验后,我完全理解这里的两难处境,也不能责怪他们的决定。事情仍在发展中,所以虽然开发者需要指导,但现在可能不是提供指导的时候或地点。

虽然这对新的开发者来说是Counter应用程序的一个明确的垫脚石,并为你的应用程序如何组合提供了一个坚实的基础结构,但我觉得在不远的将来,还有一个 "骨架2 "的空间。它将展示一个更强大的状态管理方法,以及一个在网络上完全可行的路由的清晰例子。

如果你对新模板有任何问题或意见,请告诉我们。


www.deepl.com 翻译

Guess you like

Origin juejin.im/post/7031801933690208269