本章中的食谱帮助你设置你的本地开发环境,为构建Flutter应用做好准备。根据机器的操作系统,设置步骤可能会有所不同。你只需要按照你自己的要求使用食谱。在使用了本章中的方法之后,你应该能够在模拟器或者物理设备上运行第一个Flutter应用。
你有一台Windows机器,你想在这台机器上开始Flutter开发。
在Windows机器上安装FlutterSDK,设置Android平台。
FlutterSDK支持Windows平台。在Windows上安装Flutter并不像你想象的那样困难。首先,您需要确保您的本地开发环境满足最低要求。您需要64位Windows7SP1或更高版本,以及至少400MB的可用磁盘空间供FlutterSDK使用。FlutterSDK还要求WindowsPowerShell5.0或更高版本以及GitforWindows在机器上可用。
图1-1
GitforWindows安装程序
为了能够在任何Windows控制台中运行FlutterSDK命令,我们需要将FlutterSDK添加到PATH环境变量中。安装目录的bin的完整路径应该添加到PATH中。要在Windows10上修改路径
现在,您可以打开一个新的PowerShell窗口并键入命令flutter--version来验证安装;见图1-2。
图1-2
在Windows上成功安装FlutterSDK
Windows上仅支持Android平台。按照配方1-7继续设置。
你有一台Linux机器,你想在这台机器上开始Flutter开发。
在Linux机器上安装FlutterSDK,设置Android平台。
FlutterSDK支持Linux平台。然而,鉴于有许多不同的Linux发行版可用,安装FlutterSDK的实际步骤可能会略有不同。这个方法是基于在LTS的Ubuntu18.04上安装FlutterSDK。
FlutterSDK需要几个命令行工具在本地环境中可用,包括bash、mkdir、rm、git、curl、unzip和which。对于大多数Linux发行版,默认情况下应该已经包含了命令bash、mkdir、rm、unzip和which。验证这一点最简单的方法是打开一个终端窗口,键入这些命令来查看输出。如果没有安装命令,您会看到“找不到命令”错误。git和curl不太可能默认包含。大多数Linux发行版都提供了内置的包管理器来安装这些工具。对于Ubuntu,可以使用apt-get;请参见以下命令。
$sudoapt-getupdate$sudoapt-getinstall-ycurlgit安装成功完成后,您可以键入命令curl和git进行验证。
$bin/flutter--version建议将FlutterSDK的bin目录添加到PATH环境变量中,这样flutter命令可以在任何终端会话中运行。对于Ubuntu,可以编辑文件~/.profile。
$nano~/.profile将下面一行添加到该文件并保存。
Linux上只支持Android平台。按照配方1-7继续设置。
你有一台macOS机器,你想在这台机器上开始Flutter开发。
安装FlutterSDK,在macOS机器上设置Android和iOS平台。
要在任何终端会话中运行flutter命令,应该更新PATH环境变量以包含FlutterSDK的bin目录。这通常通过更新壳的轮廓来完成。对于默认的bash,这个文件是~/.bash_profile。对于zsh来说,这个文件就是~/.zshrc。修改该文件以包含以下行。
在任一终端窗口中运行flutter--version来验证安装。您将看到与图1-2相同的输出。
macOS上同时支持Android和iOS平台。按照配方1-4和1-7继续设置。
你想为iOS平台开发Flutter应用。
在Mac上为FlutterSDK设置iOS平台。
要为iOS开发Flutter应用,你需要有一台至少安装了Xcode9.0的Mac。要设置iOS平台,您需要完成以下步骤:
图1-3
Flutter医生的输出
你需要一个快速的方法来测试iOS平台上的Flutter应用。
设置iOS模拟器。
Xcode为不同的iOS版本提供模拟器。您可以使用Xcode偏好设置中的标签组件下载其他模拟器。要打开模拟器,请运行以下命令。
$open-aSimulator当模拟器打开时,您可以使用菜单硬件设备切换不同设备和iOS版本的组合。
模拟器启动后,运行flutterdevices应该会显示模拟器。
你已经在iOS模拟器上完成了你的Flutter应用的测试,你想在真实的iOS设备上测试它们。
将Flutter应用部署到iOS设备。
在将Flutter应用部署到iOS设备之前,您需要运行flutterdoctor来验证iOS工具链是否设置正确。要在设备上开发和测试Flutter应用,你需要有一个AppleID。如果您想将应用分发到AppStore,您还需要注册Apple开发者计划。
第一次连接物理设备进行iOS开发时,您需要信任Mac来连接您的设备。Flutter应用需要在部署到设备之前进行签名。在Xcode中打开Flutterapp的ios/Runner.xcworkspace文件。在常规选项卡中,在签约部分选择正确的团队。如果您选择连接的设备作为运行目标,Xcode将完成代码签名的必要配置。捆绑标识符必须是唯一的。
图1-4
Xcode中的应用签名
Flutter应用可以使用Xcode或命令flutterrun部署到设备上。首次部署应用时,您可能需要信任iOS设备上设置应用的通用设备管理中的开发证书。
你想为Android平台开发Flutter应用。
安装AndroidStudio,在本地机器上设置Android平台。
要开发Android平台的Flutterapps,首先需要设置Android平台。FlutterSDK由于其Android平台依赖性,需要完整安装AndroidStudio,所以我们必须安装AndroidStudio。
图1-5
AndroidStudio的下载选项
在AndroidStudio中首选项的AndroidSDK页面,还可以安装额外的AndroidSDK平台和工具;参见图1-6。AndroidStudio还会提示已安装的AndroidSDK平台和工具的可用更新。
图1-6
在AndroidStudio中管理AndroidSDK
你需要一种快速的方法来测试Android平台的Flutter应用。
设置Android模拟器。
开发Flutter应用时,可以在Android模拟器上运行,看看应用运行的结果。要设置Android模拟器,您可以完成以下步骤。
在AndroidStudio中打开一个Android项目,选择工具AndroidAVD管理器打开AVD管理器,点击“创建虚拟设备…”;见图1-7。
图1-7
Android虚拟设备管理器
选择一个设备定义,比如Nexus6P,点击下一步;参见图1-8。
图1-8
选择硬件
为您想要模拟的Android版本选择一个系统映像,然后单击“下一步”;参见图1-9。
图1-9
选择一个系统映像
为“仿真性能”选择“硬件-GLE2.0”以启用硬件加速,然后单击“完成”;参见图1-10。
图1-10
选择模拟性能
您已经在模拟器上完成了对Flutter应用的测试,并且您想要在真实的Android设备上测试它们。
设置您的Android设备运行Flutter应用。
要设置您的Android设备,您可以完成以下步骤:
可以使用AndroidStudio或命令flutterrun将Flutter应用部署到设备上。
您已经设置了本地环境来开发Flutter应用。即使使用AndroidStudio或VS代码是一个很好的开发选择,您可能仍然想知道如何从命令行完成这项工作。
使用FlutterSDK中的命令创建和构建Flutter应用。
使用像AndroidStudio和VSCode这样的工具可以让Flutter开发变得容易得多。然而,知道如何使用命令行工具构建Flutter应用仍然很有价值。这对持续集成很重要。它还允许您使用任何其他编辑器来开发Flutter应用。
命令fluttercreate可以用来创建一个新的Flutterapp。实际上,AndroidStudio和VSCode都使用这个命令来创建新的Flutter应用。下面的命令在目录flutter_app中创建新的Flutter应用。
$fluttercreateflutter_app该命令在指定目录中创建各种文件,作为新应用的框架代码。导航到目录flutter_app并使用flutterrun运行该应用。
在开发Flutter应用时,您希望拥有一个强大的IDE来满足大多数需求。
使用AndroidStudio创建Flutter应用。
既然我们已经安装了AndroidStudio来为FlutterSDK搭建Android平台,那么使用AndroidStudio作为开发Flutter应用的IDE是一个很自然的选择。AndroidStudio本身就是一个基于IntelliJ平台的强大IDE。如果你使用过JetBrains的其他产品,如IntelliJIDEA或WebStorm,你可能会发现使用AndroidStudio非常容易。
要使用AndroidStudio进行Flutter开发,需要Flutter和Dart插件。要安装这两个插件,在AndroidStudio的首选项对话框中打开插件页面,点击“浏览存储库…”按钮。在打开的对话框中,输入“Flutter”搜索要安装的Flutter插件;见图1-11。单击绿色的安装按钮进行安装。这也将提示您安装Dart插件。单击“是”也安装它。重启AndroidStudio。
图1-11
在AndroidStudio中安装Flutter插件
重启AndroidStudio后,你应该会看到一个新的选项来启动一个新的Flutter项目。Flutter项目向导有不同的页面来配置新项目。
第一页允许你选择新的Flutter项目的类型。页面中的描述显示了这四种不同项目类型的区别。大多数时候,我们会创建一个Flutter应用。
图1-12
选择Flutter项目的类型
第二页允许您定制新的Flutter项目的基本配置,包括项目名称、位置和描述。
图1-13
基本项目配置
最后一页允许您定制一些高级项目配置。公司域用于创建项目的唯一标识符。
图1-14
高级项目配置
完成向导后,一个新项目被创建并在AndroidStudio中打开。
你想用一个轻量级的编辑器来开发Flutter应用。
使用VS代码创建Flutter应用。
图1-15
在VS代码中安装Flutter扩展
要在VS代码中创建新的Flutter,打开命令面板并运行Flutter:NewProject命令。在打开的对话框中输入新项目的名称。选择项目的目录。VS代码为新创建的项目打开一个新窗口。
你想在模拟器或设备上运行Flutter应用。
使用flutterrun命令或ide运行Flutter应用。
根据您开发Flutter应用的首选方法,有不同的方法来运行Flutter应用。在运行Flutter应用之前,您必须至少有一个正在运行的仿真器或连接的设备:
图1-16
在AndroidStudio中选择设备
你想知道Flutter应用的典型结构。
浏览FlutterSDK生成的样例app,了解文件。
在深入开发Flutter应用的细节之前,您应该了解Flutter应用的代码结构,这样您就知道在哪里添加新文件。Flutter应用为应用中的各种文件提供了预定义的目录结构。当一个新的应用创建时,你可以看看生成的文件,并对它们有一个基本的了解。表1-1显示了创建的app的目录和文件。
表1-1
一个Flutterapp的目录和文件
名字
|
描述
||---|---||lib|app源代码主目录。文件main.dart通常是app的入口点。||test|包含测试文件的目录。||android|Android平台的文件。||ios|iOS平台的文件。||pubspec.yaml|Dart发布工具的包描述。||pubspec.lock|Dart发布工具的锁定文件。||.metadata|FlutterSDK使用的Flutter项目描述。|
您希望确保本地开发环境的配置对于Flutter开发是正确的。
使用命令flutterdoctor。
本章的食谱提供了如何让你的本地机器为Flutter应用开发做好准备的指导。flutterdoctor是一个有用的设置工具。通过遵循此命令提供的说明,您应该能够修复大多数配置问题。在下一章,我们将看到使用DartSDK、FlutterSDK和IDEs提供的工具的方法。
没有各种工具的帮助,构建Flutter应用是不可能成功的。在开发过程中,我们可能需要使用DartSDK、FlutterSDK和IDEs中的工具。善用这些工具可以提高你的生产力。本章涵盖了DartSDK、FlutterSDK、AndroidStudio和VSCode工具的使用。
你想知道一个正在运行的Flutterapp的内部情况。
使用DartSDK提供的Dart天文台。
DartObservatory是DartSDK提供的工具,用于分析和调试Dart应用。由于Flutter应用也是Dart应用,所以Observatory也可以用于Flutter应用。Observatory是调试、跟踪和分析Flutter应用的重要工具。天文台允许你
当使用flutterrun启动Flutterapp时,Observatory也会启动并等待连接。您可以指定Observatory监听的端口,或者让它默认监听一个随机端口。您可以在命令输出中看到访问天文台的URL。在浏览器中导航到网址,就可以看到天文台的UI。
为了获得最佳效果,建议在使用天文台时使用谷歌浏览器。其他浏览器可能无法正常工作。
观察站用户界面的顶部显示Dart虚拟机信息;见图2-1。点击刷新按钮更新信息。
图2-1
灾难援助反应队天文台的虚拟机信息
底部显示了分离株列表;参见图2-2。每个Flutter应用的入口点文件都有一个初始隔离。对于每个隔离,饼图显示虚拟机活动的明细。在饼图的右侧,有一个链接列表指向其他天文台功能的不同屏幕。
图2-2
在Dart天文台隔离信息
当开发Flutter应用时,在您进行了一些代码更改后,您希望快速看到结果。
使用FlutterSDK提供的热重装和热重启。
FlutterSDK提供的热重载是一个杀手级特性,可以显著提高开发人员的工作效率。使用热重新加载,应用更新之间的状态是反常的,因此您可以立即看到UI更新,并从您进行更改的最后一个执行点继续开发和测试。
根据Flutter应用的启动方式,有不同的方式来触发热重装。只有调试模式下的Flutter应用可以热重装:
如果应用热重新加载成功,您可以在控制台中看到热重新加载的详细信息。图2-3显示了在AndroidStudio中保存文件触发热重装时的控制台输出。
图2-3
热重装输出
热重新加载非常有用,您可能希望它对您所做的所有代码更改都可用。不幸的是,仍有一些情况下热重装可能不起作用:
如果热重新加载不起作用,您仍然可以使用热重启,这将从头重新启动应用。您可以确保热重启将反映您所做的所有更改。根据Flutter应用的启动方式,触发热重启有不同的方式:
您希望让FlutterSDK保持最新,以获得最新的特性、错误修复和性能改进。
跟踪不同的FlutterSDK通道并升级SDK。
有时,我们可能需要升级FlutterSDK以获得新功能、错误修复和性能改进。FlutterSDK有不同的渠道获取更新。每个通道实际上是FlutterSDK的存储库中的一个Git分支。执行命令flutterchannel显示所有可用通道;见图2-4。标有星形符号的频道是当前频道。在图2-4中,电流通道为stable。
图2-4
命令的输出flutterchannel
表2-1显示了FlutterSDK的四个通道。
表2-1
FlutterSDK通道
引导
||---|---||stable|稳定构建的渠道。这是产品开发的推荐渠道。||beta|上个月最佳构建频道。||dev|最新全面测试版本的通道。在此通道中运行的测试比master多。||master|积极开发最新变化的渠道。如果您想尝试最新的功能,这是要跟踪的频道。这个通道中的代码通常工作正常,但有时可能会意外中断。使用本频道风险自担。|
$flutterchannelmaster$flutterupgrade2.4在AndroidStudio中调试Flutter应用问题您正在使用AndroidStudio开发Flutter应用,并希望找出代码无法按您预期的方式运行的原因。
使用AndroidStudio内置的Flutter调试支持。
调试是开发人员日常工作的重要组成部分。调试时,我们可以在运行时看到实际的代码执行路径,并检查变量值。如果您有使用其他编程语言的经验,您应该已经具备了基本的调试技能。
在AndroidStudio中,你可以点击编辑器中某一行的左边来添加断点。点击调试图标或者使用菜单运行调试在调试模式下启动app见图2-5。
图2-5
单击调试图标开始调试
图2-6中的帧视图显示了当前执行的帧。
图2-6
AndroidStudio中的框架视图
图2-7中的变量视图显示变量和对象的值。在这个视图中,我们还可以添加表达式来监视值。
图2-7
AndroidStudio中的变量视图
图2-8中的控制台视图显示了显示在控制台上的信息。
图2-8
AndroidStudio中的控制台视图
你希望看到Flutter应用的轮廓,以便清楚地了解小部件是如何组织的。
在AndroidStudio中使用FlutterOutline视图。
在AndroidStudio中,可以从菜单视图工具窗口Flutter轮廓打开Flutter轮廓视图。此视图显示当前打开文件的树状层次结构;见图2-9。Flutter轮廓视图与文件编辑器相链接。在FlutterOutline视图中选择一个元素会使编辑器滚动并突出显示这个元素的源代码。此链接是双向的;编辑器中的选择也会导致在Flutter轮廓视图中选择相应的元素。
图2-9
AndroidStudio中的Flutter轮廓视图
FlutterOutline视图中的工具栏有不同的操作来管理小部件。例如,中心小部件按钮用一个Center小部件包装当前小部件。
您正在使用VS代码开发Flutter应用,并且想要找出为什么代码没有按照您预期的方式工作。
使用VS代码中内置的Flutter调试支持。
在VS代码中,你可以点击编辑器中某一行的左边来添加断点。使用菜单调试开始调试在调试模式下启动应用。
图2-10显示了调试模式下的VS代码视图。此视图中有不同的面板:
顶部的操作栏包含的操作包括继续、单步执行、单步执行、单步执行、重新启动和停止。
图2-10
在VS代码中调试
您想要创建不同类型的Flutter项目。
使用带有不同参数的命令fluttercreate。
fluttercreate是FlutterSDK提供的创建Flutter项目的命令。在菜谱1-10中,我们使用这个命令来创建一个简单的Flutter应用。在配方1-11中,我们还看到了Android提供的向导来创建新的Flutter项目,它允许对已创建的项目进行定制。在引擎盖下,AndroidStudio也使用了fluttercreate命令。该命令支持不同场景的不同参数。以下代码是fluttercreate的基本用法。输出目录将包含新项目的文件。
表2-2
Flutter项目类型
项目类型
||---|---||app|Flutter应用。这是默认类型。||package|一个包含模块化Dart代码的可共享的Flutter项目。||plugin|一个可共享的Flutter项目,包含Android和iOS平台特定的代码。|
下面的命令显示了如何创建一个Flutter包和插件。
$fluttercreate-tpackagemy_package$fluttercreate-tpluginmy_plugin在创建插件的时候,我们也可以使用参数-i或者--ios-language来指定iOS代码的编程语言。Objective-C的可能值为objc,Swift的可能值为swift。默认值为objc。对于Android代码,我们可以用自变量-a或者--android-language来指定Android代码的编程语言。可能的值是Java的java和Kotlin的kotlin。默认值为java。以下命令显示了如何使用SwiftforiOS和KotlinforAndroid创建一个Flutter插件。
创建项目时有一些常规配置可用;参见表2-3。
表2-3
Flutter项目配置
争吵
缺省值
||---|---|---||--project-name|这个新的Flutter项目的名称。该名称必须是有效的dart包名称。|从输出目录名派生||--org|这个新的Flutter项目的组织名称。该值应该采用反向域表示法,例如,com.example。该值用作Android代码的Java包名和iOS包标识符中的前缀。|com.example||--description|这个新Flutter项目的描述。|新的Flutter项目|
以下命令使用表2-3中的项目配置。
$fluttercreate--org=com.mycompany--description="E-commerceapp"my_ecommerce_app启用或禁用功能有额外的标志来启用或禁用某些功能;见表2-4。一次只能指定每对中的一个参数。前缀为--no的参数名表示禁用一个特性,另一个表示启用一个特性。例如,--overwrite表示启用覆盖,--no-overwrite表示禁用覆盖。默认值“开”或“关”分别表示默认情况下该功能是启用还是禁用。例如,--overwrite和--no-overwrite对的默认值Off表示默认使用--no-overwrite。
表2-4
fluttercreate的特点
争论
||---|---|---||--overwrite/--no-overwrite|是否覆盖现有文件。|离开||--pub/--no-pub|项目创建后是否运行flutterpackagesget。|在||--offline/--no-offline|是否在离线模式下运行flutterpackagesget。仅在--pub开启时适用。|离开||--with-driver-test/--no-with-driver-test|是否添加flutter_driver依赖,生成样本Flutter驱动测试。|离开|
你想运行Flutter应用。
使用带有不同参数的命令flutterrun。
flutterrun是FlutterSDK提供的启动Flutterapps的命令。flutterrun针对不同的使用场景有很多说法。
默认情况下,flutterrun会构建应用的调试版本。调试版本适用于支持热重装的开发和测试。对于不同的场景,您可以使用其他的构建风格;见表2-5。
表2-5
构建Flutter运行的风味
||---|---||--debug|调试版本。这是默认的构建风格。||--profile|专门用于性能分析的版本。此选项目前不支持模拟器目标。||--release|准备发布到appstore的发布版本。||--flavor|由特定于平台的构建设置定义的自定义应用风格。这需要在AndroidGradle脚本和自定义Xcode方案中使用产品风格。|
参数-t或--target指定应用的主入口点文件。它必须是一个包含main()方法的Dart文件。默认值为lib/main.dart。下面的命令使用lib/app.dart作为入口点文件。
$flutterrun-tlib/app.dart如果您的应用有不同的路线,请使用参数--route来指定运行应用时要加载的路线。
如果你想记录正在运行的Flutterapp的进程id,使用参数--pid-file指定文件来写进程id。有了进程id,您可以发送信号SIGUSR1来触发热重装,发送信号SIGUSR2来触发热重启。在下面的命令中,进程id被写入文件~/app.pid。
$flutterrun--pid-file~/app.pid现在我们可以使用kill向正在运行的Flutterapp发送信号。
表2-6
flutterrun的额外参数
||---|---|---||--hot/--not-hot|是否应启用热重装。|在||--build/--no-build|在运行应用之前,是否应该构建它。|在||--pub/--no-pub|是否先运行flutterpackagesget再运行。|在||--target-platform|为Android设备构建应用时,指定目标平台。可能的值有default、android-arm和android-arm64。|default||--observatory-port|指定观察站调试器连接的端口。|0(随机自由港)||--start-paused|让应用以暂停模式启动,并等待调试器连接。|||--trace-startup|开始追踪。|||--enable-software-rendering|使用Skia启用渲染。|||--skia-deterministic-rendering|与--enable-software-rendering一起使用时,提供100%确定性Skia渲染。|||--trace-skia|启用Skia代码跟踪。||
图2-11显示运行命令flutterrun的输出。从输出中,我们可以看到正在运行的app的天文台端口,这对于其他FlutterSDK命令与正在运行的app协同工作非常重要。我们可以通过按不同的键与控制台进行交互。例如,按“r”触发热重装。按下“h”后,flutterrun会显示一条关于它可以接受的所有命令的帮助消息。
图2-11
Flutter运行命令的输出
你想为Android和iOS平台构建应用二进制文件。
使用命令flutterbuild。
为了将Flutter应用部署到设备上并发布到应用商店,我们需要为Android和iOS平台构建二进制文件。命令flutterbuild支持构建这些二进制文件。
命令flutterbuildapk为你的应用构建APK文件。表2-7显示了该命令支持的参数。
表2-7
Flutter生成参数apk
||---|---||--debug|构建调试版本。||--profile|构建一个专门用于性能分析的版本。||--release|构建发布版本,准备发布到appstore。||--flavor|构建由特定于平台的构建设置定义的自定义应用风格。这需要在AndroidGradle脚本和自定义Xcode方案中使用产品风格。||--pub/--no-pub|构建app前是否运行flutterpackagesget。||--build-number=|一个整数,用于指定递增的内部版本号。对于每个版本,该值必须是唯一的。该值被用作“versionCode”。||--build-name=|格式为x.y.z的字符串版本号。该值被用作“versionName”。||--build-shared-library|编译成a∫。所以归档吧。||--target-platform|目标平台。可能的值是android-arm和android-arm64。|
建立APK文件时,--release是默认模式。下面的命令构建了一个发布版本,版本号为5,版本名为0.1.0。
$flutterbuildapk--build-number=5--build-name=0.1.0为iOS构建命令flutterbuildios构建iOS应用捆绑包。该命令的参数--debug、--profile、--release、--flavor、--pub、--no-pub、--build-number和--build-version与flutterbuildapk相同。--build-number的值作为CFBundleVersion,而--build-name的值作为CFBundleShortVersionString。
它也有其他的论点;参见表2-8。
表2-8
颤动构建ios的额外参数
||---|---||--simulator|为iOS模拟器创建一个版本。||--no-simulator|为iOS设备构建一个版本。||--codesign/--no-codesign|是否对应用包进行签名。默认值为--codesign。|
默认情况下,flutterbuildios为设备构建app,即使用--no-simulator。以下命令为模拟器构建了一个调试版本,但没有对应用包进行签名。
$flutterbuildios--debug--no-codesign--simulator2.10安装Flutter应用问题你想把Flutter应用安装到模拟器或者设备上。
使用命令flutterinstall。
命令flutterinstall将当前的Flutter应用安装到仿真器或设备上。要安装该应用,您需要至少启动一个模拟器或连接一个设备。在安装应用之前,目标仿真器或设备应该有一个可用的二进制文件。首先使用flutterbuild构建二进制文件。
以下命令安装构建的二进制文件。
$flutterinstall2.11管理包问题你想要管理Flutter应用的依赖关系。
使用命令flutterpackages。
使用包是管理项目依赖关系的捷径。Flutter继承了相同的依赖性管理方式。你可能在其他编程平台上看到过类似的概念。为了让依赖关系管理工作,我们需要有一种方法来描述可共享的组件及其依赖关系。我们还需要一个工具来获取依赖关系。表2-9显示了不同平台的包管理工具。FlutterSDK使用命令flutterpackages来管理依赖关系,它使用了底层的Dartpub工具。
表2-9
包管理工具
平台
描述文件
工具
||---|---|---||Node.js|package.json|新公共管理故事||镖摆动|pubspec.yaml|pub``flutterpackages||爪哇|pom.xml``build.gradle|专家格拉德尔||红宝石|Gemfile|大错|
命令flutterpackagesget下载Flutter项目中的依赖包。命令flutterpackagesupgrade升级一个Flutter项目中的包。这两个命令简单地围绕Dart的底层pub工具。我们也可以使用flutterpackagespub直接调用Dartpub工具。命令flutterpackages不能做太多,因为它提供的功能有限。您可以随时使用flutterpackagespub将任务委派给Dartpub工具。
你应该使用flutterpackagesget和flutterpackagesupgrade来管理Flutter应用的依赖关系。不应使用Dartpub工具中的命令pubget和pubupgrade。如果您需要Dartpub工具的更多功能,请使用flutterpackagespub。
命令flutterpackagestest与pubruntest相同,但与fluttertest不同。由flutterpackagestest运行的测试托管在一个纯Dart环境中,所以像dart:ui这样的库是不可用的。这使得测试运行得更快。如果您正在构建不依赖于FlutterSDK中任何包的库,您应该使用这个命令来运行测试。
您已经为Flutter应用编写了测试,并且您想要确保这些测试通过。
使用命令fluttertest。
测试是可维护软件项目的重要组成部分。你应该对Flutter应用进行测试。命令fluttertest运行Flutter应用的测试。运行该命令时,您可以提供一个以空格分隔的相对文件路径列表,以指定要运行的测试文件。如果没有提供文件,则包含test目录中文件名以_test.dart结尾的所有文件。下面的命令运行测试文件test/mytest.dart。
$fluttertesttest/mytest.dart筛选要运行的测试参数--name指定正则表达式来匹配要运行的测试的名称。一个测试文件可以包含多个测试。如果只需要做简单的子串匹配,就用--plain-name代替。以下命令显示了--name和--plain-name的用法。
$fluttertest--name="smoke\d+"$fluttertest--plain-name=smoke您可以使用--name和--plain-name指定多个匹配条件。要运行的测试需要匹配所有给定的条件。以下命令同时使用了--name和--plain-name。
$fluttertest--name="smoke.*"--plain-name=test测试覆盖率如果你想知道你的测试的覆盖范围,使用参数--coverage。测试结束后,fluttertest生成测试覆盖信息并保存到文件coverage/lcov.info中。可以使用参数--coverage-path指定覆盖信息的输出路径。如果你有基本的覆盖率数据,你可以把它放入路径coverage/lcov.base.info并传递参数--merge-coverage到fluttertest,然后FlutterSDK会使用lcov合并这两个覆盖率文件。
要查看覆盖率报告,您需要安装lcov。在macOS上,可以使用自制软件安装lcov。
$brewinstalllcov命令genhtml从lcov覆盖信息文件生成HTML文件。以下命令生成HTML覆盖率报告。打开生成的文件index.html查看报告。
$genhtmlcoverage/lcov.info--output-directorycoverage_report调试测试如果你想调试一个测试文件,你可以使用参数--start-paused。这种模式下只允许一个测试文件。执行会暂停,直到连接了调试器。以下命令调试文件test/simple.dart。
$fluttertest--start-pausedtest/simple.dart其他选项还有其他有用的论据;参见表2-10。
表2-10
fluttertest的额外参数
||---|---|---||--j,--concurrency|要运行的并发测试的数量。|6||--pub/--no-pub|是否在运行测试之前运行flutterpackagesget。|在|
您的Flutter代码编译成功,并且在测试中看起来不错。但是,您想知道在您的代码中是否有任何潜在的错误或不良的代码实践。
使用命令flutteranalyze。
命令flutteranalyze接受目录列表来扫描Dart文件。如果没有提供路径,flutteranalyze只分析当前工作目录。以下命令分析目录~/my_app/lib。
$flutteranalyze~/my_app/lib分析结果可以用参数--write写入文件。默认情况下,结果会写入控制台。您还可以传递参数--watch让分析器观察文件系统的变化,并连续运行分析。
表2-11显示了flutteranalyze的额外参数。
表2-11
flutteranalyze的额外参数
||---|---|---||--current-package/--no-current-package|是否分析当前项目。如果--no-current-package被启用并且没有指定目录,那么将不进行任何分析。|在||--pub/--no-pub|运行分析前是否运行flutterpackagesget。|在||--preamble/--no-preamble|是否显示正在分析的当前文件。|在||--congratulate/--no-congratulate|是否在没有错误、警告、提示或lints的情况下显示输出。|在||--watch|持续监视文件系统的变化,并运行分析作为响应。||
命令flutteranalyze将代码分析委托给Dartdartanalyzer工具。我们可以使用项目根目录中的文件analysis_options.yaml来定制分析行为。
图2-12显示了在代码中发现一个问题的flutteranalyze的输出。
图2-12
Flutter分析命令的输出
您希望管理FlutterSDK使用的不同模拟器。
使用命令flutteremulators。
在为FlutterSDK设置Android和iOS平台时,我们还为Android和iOS创建了模拟器。对于Android,我们可以使用AVD管理器来管理仿真器。对于iOS,我们可以使用Xcode来管理模拟器。如果我们能以同样的方式管理Android模拟器和iOS模拟器,那将非常方便。命令flutteremulators是管理仿真器的工具。
运行flutteremulators显示所有可供FlutterSDK使用的仿真器;见图2-13。
图2-13
指令Flutter模拟器的输出
$flutteremulators--launchNexus我们还可以使用flutteremulators--create创建一个新的Android模拟器。下面的命令创建一个名为Pixel的新模拟器。此命令只能创建基于像素设备的模拟器。
$flutteremulators--create--namePixel2.15截图问题你想截图你正在运行的应用。
使用命令flutterscreenshot。
Android模拟器和iOS模拟器都提供了截图的原生功能。对于iOS模拟器,这可以使用菜单文件新屏幕截图来完成。对于Android模拟器,这可以通过点击浮动控制栏中的屏幕截图图标来完成。但是使用UI控件并不方便。默认情况下,模拟器拍摄的屏幕截图会保存到桌面。您必须配置模拟器以保存到所需的位置。
命令flutterscreenshot比模拟器中的内置特性更容易使用。可以使用参数-o或--output指定保存截图的位置;请参见以下命令。
$flutterscreenshot-o~/myapp/screenshots/home.pngflutterscreenshot可以拍摄不同类型的截图。参数--type接受表2-12中的值。
表2-12
截图的类型
类型
||---|---||Device|使用设备的原生屏幕截图功能。该屏幕截图包括当前显示的整个屏幕。这是默认类型。||Rasterizer|使用光栅化器渲染的Flutter应用的屏幕截图。||skia|渲染成Skia图片的Flutterapp截图。|
对于rasterizer和skia类型,需要参数--observatory-port提供运行app的Dart天文台端口号。该端口显示在命令flutterrun的输出中。
你的Flutter应用不是用flutterrun启动的,但是你需要想和它互动。
使用命令flutterattach。
当使用flutterrun启动Flutter应用时,我们可以使用控制台进行交互。但是,该应用也可以通过其他方式启动。例如,我们可以关闭设备上的应用,然后再打开它。在这种情况下,我们失去了对正在运行的应用的控制。flutterattach提供了一种连接正在运行的应用的方式。
如果应用已经在运行,并且你知道它的观测站的端口,使用flutterattach--debug-port来连接它。以下命令附加到正在运行的应用。
$flutterattach--debug-port10010如果没有提供观察端口,flutterattach会开始监听和扫描新激活的应用。当检测到一个新的天文台时,这个命令会自动连接到应用。
$flutterattach在图2-14中,flutterattach最初在等待一个新的Flutterapp启动。一旦一个Flutter应用被启动,flutterattach连接到它并显示与flutterrun相同的控制台。
图2-14
Flutter附着命令的输出
你想跟踪一个正在运行的应用的执行。
使用命令fluttertrace。
要开始跟踪,我们需要知道正在运行的应用的观察站端口,并用参数--debug-port将这个端口提供给fluttertrace。默认情况下,跟踪运行10秒,并将结果JSON文件写入当前目录,文件名如trace_01.json、trace_02.json等等。在下面的命令中,观察端口是51240。
$fluttertrace--start$fluttertrace--stop要立即停止跟踪,请使用以下命令。
$fluttertrace--stop-d02.18配置FlutterSDK问题你想配置不同设置的FlutterSDK。
使用命令flutterconfig。
命令flutterconfig允许配置一些FlutterSDK设置。表2-13显示了flutterconfig的参数。
表2-13
flutterconfig的参数
||---|---|---||--analytics/--no-analytics|是否报告匿名工具使用统计和崩溃报告。|在||--clear-ios-signing-cert|清除已存储的用于为iOS设备部署的应用签名的开发证书。|||--gradle-dir|设置Gradle安装目录。|||--android-sdk|设置AndroidSDK目录。|||--android-studio-dir|设置AndroidStudio安装目录。||
要删除设置,只需将其配置为空字符串。以下命令禁用分析报告。
$flutterconfig--no-analytics2.19显示应用日志问题您希望看到运行在模拟器或设备上的Flutter应用生成的日志。
使用命令flutterlogs。
即使我们可以调试Flutter应用的代码来找出某些问题的原因,日志对于错误诊断仍然非常有价值。在Flutter应用中生成日志最简单的方法是调用print()方法。命令flutterlogs监视设备上生成的日志,并打印到控制台。
$flutterlogs如果您想在读取日志之前清除日志历史,请使用参数-c或--clear。
$flutterlogs-c图2-15显示了flutterlogs的输出。
图2-15
Flutter日志命令的输出
您希望确保应用的源代码遵循相同的代码风格。
使用命令flutterformat。
让你的应用拥有相同的代码风格是一个很好的实践,特别是对于开发团队。一致的代码风格也有利于代码评审。命令flutterformat可以格式化源代码文件,以匹配Dart的默认代码样式。
要运行flutterformat,您需要提供一个用空格分隔的路径列表。以下命令格式化当前目录。
表2-14
Flutter格式的额外参数
||---|---||-n,--dry-run|只显示哪些文件将被修改,而不实际修改它们。||--set-exit-if-changed|如果该命令改变了格式,返回退出代码1。||-m,--machine|将输出格式设置为JSON。|
您希望看到所有可以被FlutterSDK使用的连接设备。
使用命令flutterdevices。
FlutterSDK要求在运行某些命令之前至少准备好一个仿真器或设备。FlutterSDK使用术语“设备”来指代Android模拟器、iOS模拟器和真实设备。命令flutterdevices列出了FlutterSDK可以使用的所有设备。图2-16显示了flutterdevices的输出。
图2-16
Flutter装置的输出
您已经使用FlutterDriver编写了集成测试,并且想要运行这些测试。
使用命令flutterdrive。
FlutterDriver是FlutterSDK提供的运行集成测试的工具。当运行集成测试时,应用本身运行在模拟器或设备上,但是测试脚本运行在您的本地机器上。在测试期间,测试脚本连接到正在运行的应用,并向应用发送命令来模拟不同的用户操作。测试脚本可以执行像点击和滚动这样的动作。它还可以读取小部件属性并验证它们的正确性。
flutterdrive是运行集成测试的命令。它可以自己启动应用或连接到现有的运行应用。当flutterdrive启动app时,它可以取与flutterrun相同的参数,包括--debug、--profile、--flavor、--route、--target、--observatory-port、--pub、--no-pub、--trace-startup。这些参数与flutterrun中的含义相同。连接已有app时,需要用已有app的天文台URL指定参数--use-existing-app;请参见以下命令。
$flutterdrive--driver=test_driver/simple.dart如果应用由flutterdrive启动,那么应用将在测试脚本完成后停止,除非参数--keep-app-running被指定为保持运行。当连接到一个现有的应用时,应用在测试脚本完成后继续运行,除非参数--no-keep-app-running被指定来停止它。以下命令在测试后保持应用运行。
$flutterdrive--keep-app-running2.23启用FlutterSDK命令的Bash完成问题当键入FlutterSDK命令时,您希望为您的shell提供完成支持。
使用命令flutterbash-completion设置完成。
有了shell完成支持,当您键入一些命令时,shell会尝试完成它。flutterbash-completion打印设置脚本,以支持bash和zsh的完成。如果没有提供参数,安装脚本将被打印到控制台。如果提供了文件路径,安装脚本将被写入该文件。
在macOS上,我们可以先用自制软件安装bash-completion。
$brewinstallbash-completion如果您正在使用bash,您需要修改文件~/.bash_profile来添加下面一行。
$flutterbash-completion/usr/local/etc/bash_completion.d/flutter最后,您应该运行source~/.bash_profile或重启shell来实现完成。
如果您正在使用zsh,您可以将设置脚本添加到文件~/.zshrc中。首先你需要在~/.zshrc的顶部添加下面一行。
autoloadbashcompinitbashcompinit然后您需要运行下面的命令来将设置脚本添加到~/.zshrc。
$flutterbash-completion>>~/.zshrc最后,您应该运行source~/.zshrc或重启shell来实现完成。
你想要清理Flutter应用的构建文件。
使用命令flutterclean。
命令flutterclean删除build目录中的文件。build目录的磁盘空间可能很大,即使对于小应用也是如此。比如搭建好Fluttersampleapp后,build目录的大小大概是200M。学习Flutter的时候,可能会创建很多小app进行测试。当你认为你已经使用完这些应用时,运行flutterclean是个好主意。你会发现你可以回收大量的磁盘空间。
你想要显式地管理FlutterSDK的缓存。
使用命令flutterprecache。
FlutterSDK在bin/cache目录中保存了所需工件的缓存。该目录包含DartSDK、Flutter引擎、材质字体和Gradlewrapper的二进制文件。如果该缓存不存在,则会自动填充。命令flutterprecache显式更新缓存。除了config、precache、bash-completion和upgrade命令之外,大多数Flutter命令在执行前都会自动更新缓存,所以大多数时候你不需要显式运行这个命令。
e有参数-a或--all-platforms来指定是否应该下载所有平台的工件。默认情况下,只下载当前平台的工件。
$flutterprecache-a2.26摘要这一章是关于你在开发Flutter应用时可能需要用到的工具。您可能不需要使用所有这些工具。在ide的帮助下,您可以执行ide中的大多数操作。这些工具的知识仍然很有价值,因为您可以使用这些工具做更多的事情。在下一章中,我们将看到关于Dart语言基本部分的配方。
你想知道Dart的内置类型。
Dart有内置的数字、字符串、布尔值、列表、地图、符文和符号类型。
Dart有几个内置类型,包括数字、字符串、布尔值、列表、地图、符文和符号。
Dart中的数字可以是不大于64位的整数值或IEEE754标准指定的64位双精度浮点数。类型int和double分别代表这两种类型的数字。类型num是int和double的超类型。与Java中的原始类型不同,Dart中的数字也是对象。他们有办法和他们一起工作。
在清单3-1中,x的类型是int,而y的类型是double。方法toRadixString()通过将值转换成指定的基数返回一个字符串值。方法toStringAsFixed()确保给定的小数位数保存在字符串表示中。double的静态方法tryParse()试图将字符串解析为double文字。
varx=10;vary=1.5;assert(x.toRadixString(8)=='12');assert(y.toStringAsFixed(2)=='1.50');varz=double.tryParse('3.14');assert(z==3.14);Listing3-1Numbers用线串Dart字符串是UTF-16代码单元的序列。单引号或双引号都可以用来创建字符串。用哪个引语并不重要。关键是要在整个代码库中保持一致。Dart内置了对字符串插值的支持。可以使用形式${expression}将表达式嵌入字符串。使用字符串时,会计算嵌入表达式的值。如果表达式是一个标识符,那么{}可以省略。在清单3-2中,name是一个标识符,所以我们可以在字符串中使用$name。
varname='Alex';assert('Thelengthof$nameis${name.length}'=='ThelengthofAlexis4');Listing3-2Stringinterpolation如果您想要连接字符串,您可以简单地将这些字符串文字相邻放置,而不使用+操作符;参见清单3-3。
varlongString='Thisisalong''long''long''string';Listing3-3Stringconcatenation创建多行字符串的另一种方法是使用带单引号或双引号的三重引号;参见清单3-4。
varlongString2="'Thisisalsoalonglonglongstring"';Listing3-4Multi-linestring布尔运算使用类型bool表示布尔值。bool类型只有两个对象:true和false。值得注意的是,if、while、assert中只能使用bool值作为检查条件。JavaScript有更广泛的真值和假值的概念,而Dart遵循更严格的规则。例如,if('abc')在JavaScript中有效,但在Dart中无效。
在清单3-5中,name是一个空字符串。为了在if中使用它,我们需要调用getterisEmpty。我们还需要对null和0进行显式检查。
varname=";if(name.isEmpty){print('nameisemtpy');}varvalue;assert(value==null);varcount=5;while(count--!=0){print(count);}Listing3-5Booleans列表和地图列表和地图是常用的集合类型。在Dart中,数组是List对象。可以使用文字或构造函数创建列表和映射。建议尽可能使用集合文字。清单3-6展示了如何使用文字和构造函数创建列表和映射。
使用枚举类型。
enumTrafficColor{red,green,yellow}voidmain(){assert(TrafficColor.red.index==0);assert(TrafficColor.values.length==3);varcolor=TrafficColor.red;switch(color){caseTrafficColor.red:print('stop');break;caseTrafficColor.green:print('go');break;caseTrafficColor.yellow:print('becareful');}}Listing3-9Enumeratedtype3.3使用动态类型问题你不知道对象的类型或者你不关心类型。
使用dynamic类型。
dynamicvalue=1;print(value.runtimeType);value='test';if(valueisString){print('string');}Listing3-10Usedynamictype3.4了解功能问题您希望了解Dart中的函数。
Dart中的函数非常强大和灵活。
Dart中的函数是对象,类型为Function。函数可以赋值,传入函数参数,并用作函数返回值。在Dart中创建高阶函数非常容易。一个函数可以有零个或多个参数。有些参数是必需的,有些是可选的。必需的参数首先出现在参数列表中,后面是可选参数。可选的位置参数包含在[]中。
当一个函数有一长串参数时,很难记住这些参数的位置和意义。最好使用命名参数。使用@required注释可以根据需要标记命名参数。参数可以使用=指定默认值。如果没有提供默认值,则默认值为null。
在清单3-11中,函数sum()有一个可选的位置参数initial,默认值为0。函数joinToString()有一个必需的命名参数separator和两个可选的命名参数prefix和suffix。joinToString()中使用的箭头语法是只有一个表达式的函数体的简写。语法=>expr与{returnexpr;}相同。使用箭头语法使代码更短,更容易阅读。
varlist=[1,2,3];list.forEach((v)=>print(v*10));Listing3-12Anonymousfunctions3.5使用Typedefs问题你想要一个函数类型的别名。
使用typedefs。
在Dart中使用级联运算符(..)。
Dart有一个特殊的级联操作符(..),允许我们对同一个对象进行一系列操作。为了在其他编程语言中对同一对象进行链式操作,我们通常需要创建一个fluentAPI,其中每个方法都返回当前对象。Dart中的cascade操作符使这一要求变得不必要。即使方法不返回当前对象,它们仍然可以被链接。级联运算符也支持字段访问。在清单3-14中,级联运算符用于访问类User和Address中的字段和方法。
classUser{Stringname,email;Addressaddress;voidsayHi()=>print('hi,$name');}classAddress{Stringstreet,suburb,zipCode;voidlog()=>print('Address:$street');}voidmain(){User()..name='Alex'..email='alex@example.org'..address=(Address()..street='mystreet'..suburb='mysuburb'..zipCode='1000'..log())..sayHi();}Listing3-14Usingcascadeoperator3.7覆盖运算符问题您希望覆盖Dart中的运算符。
为运算符定义类中的重写方法。
使用构造函数。
一个类可以有多个构造函数。您可以以ClassName.identifier的形式命名这些构造函数,以便更好地阐明含义。
在清单3-16中,类Rectangle有一个带四个参数的常规构造函数。它还有一个命名的构造函数Rectangle.fromPosition。
classExpensiveObject{staticExpensiveObject_instance;ExpensiveObject._create(){print('created');}factoryExpensiveObject(){if(_instance==null){_instance=ExpensiveObject._create();}return_instance;}}voidmain(){ExpensiveObject();ExpensiveObject();}Listing3-17Facto+ryconstructor3.9扩展类问题您希望从现有的类中继承行为。
从现有类扩展以创建子类。
Dart是一种面向对象的编程语言。它提供了对继承的支持。一个类可以使用关键字extends从一个超类扩展而来。超类可以在子类中称为super。子类可以覆盖超类的实例方法、getters和setters。重写成员应该用@override注释进行注释。
抽象类是使用abstract修饰符定义的。抽象类不能被实例化。抽象类中的抽象方法没有实现,必须由非抽象子类实现。
在清单3-18中,类Shape是用抽象方法area()抽象的。类Rectangle和Circle都从Shape扩展而来,并实现了抽象方法area()。
import'dart:math'showpi;abstractclassShape{doublearea();}classRectangleextendsShape{doublewidth,height;Rectangle(this.width,this.height);@overridedoublearea(){returnwidth*height;}}classSquareextendsRectangle{Square(doublewidth):super(width,width);}classCircleextendsShape{doubleradius;Circle(this.radius);@overridedoublearea(){returnpi*radius*radius;}}voidmain(){varrect=Rectangle(100,50);varsquare=Square(50);varcircle=Circle(50);print(rect.area());print(square.area());print(circle.area());}Listing3-18Inheritance3.10向类中添加功能问题您希望重用一个类的代码,但受到Dart的单一继承的限制。
使用mixins。
classPerson{Stringname;Person(this.name);}classStudentextendsPersonwithCardHolder{Student(Stringname):super('Student:$name'){holder=this;}}classTeacherextendsPersonwithCardHolder{Teacher(Stringname):super('Teacher:$name'){holder=this;}}mixinCardHolder{Personholder;voidswipeCard(){print('${holder.name}swipedthecard');}}mixinSystemUser{Personuser;voiduseSystem(){print('${user.name}usedthesystem.');}}classAssistantextendsStudentwithSystemUser{Assistant(Stringname):super(name){user=this;}}voidmain(){varassistant=Assistant('Alex');assistant.swipeCard();assistant.useSystem();}Listing3-19Mixins3.11使用接口问题你想有一个契约让课程遵循。
使用类的隐式接口。
classDataLoader{voidload(){print('loaddata');}}classCachedDataLoaderimplementsDataLoader{@overridevoidload(){print('loadfromcache');}}voidmain(){varloader=CachedDataLoader();loader.load();}Listing3-20Interfaces3.12使用泛型问题当您的代码被设计为使用不同的类型时,您希望具有类型安全。
使用泛型类和泛型方法。
使用import导入库以在您的应用中使用它们。
在开发重要的Dart应用时,不可避免地要使用库。这些库可以是DartSDK中的内置库,也可以是社区贡献的库。要使用这些库,我们需要先用import导入它们。import只有一个参数来指定库的URI。内置库有URI方案dart:,比如dart:html和dart:convert。社区包有URI方案package:,由Dartpub工具管理。清单3-24展示了导入库的例子。
import'dart:html';import'package:meta/meta.dart';Listing3-24Importlibraries两个库可能导出相同的标识符。为了避免冲突,我们可以使用as为其中一个库或者两个库提供前缀。在清单3-25中,lib1.dart和lib2.dart都导出了类Counter。在给这两个库分配不同的前缀后,我们可以使用前缀来访问类Counter。
import'lib1.dart'aslib1;import'lib2.dart'aslib2;lib1.Countercounter;Listing3-25Renamelibraries您不需要导入库的所有成员。使用show显式包含成员。使用hide显式排除成员。在清单3-26中,导入库dart:math时,只导入Random;导入库dart:html时,只排除Element。
import'dart:math'showRandom;import'dart:html'hideElement;Listing3-26Showandhidemembers3.14使用异常问题您希望处理Dart应用中的故障。
使用throw报告故障。使用try-catch-finally处理异常。
我们可以使用throw来抛出异常。事实上,所有非null对象都可以被抛出,不仅仅是实现类型Error或Exception的类型。建议只投掷Error和Exception类型的物体。
一个Error对象代表代码中不应该发生的bug。例如,如果一个列表只包含三个元素,试图访问第四个元素会导致抛出一个RangeError。与异常不同,错误不是用来被捕获的。当错误发生时,最安全的方法是终止程序。Error它们携带着关于为什么会发生的清晰信息。
与Errors相比,Exceptions被设计为以编程方式被捕获和处理。例如,发送HTTP请求可能不会成功,因此我们需要在代码中处理异常来处理失败。Exceptions通常携带关于失败的有用数据。我们应该创建从Exception扩展的自定义类型来封装必要的数据。
当抛出异常时,您可以捕捉它以阻止它传播,除非您重新抛出它。捕捉异常的目标是处理它。如果不想处理异常,就不应该捕捉它。使用try、catch和on捕获异常。如果不需要访问异常对象,使用on就足够了。使用catch,您可以访问异常对象和堆栈跟踪。使用on指定要捕获的异常类型。
当你捕捉到一个异常时,你应该处理它。但是,有时您可能只想部分处理它。在这种情况下,您应该使用rethrow来重新抛出异常。捕捉异常但不完全处理它是一种糟糕的做法。
如果您希望无论是否抛出异常都运行一些代码,您可以将代码放在一个finally子句中。如果没有抛出异常,finally子句在try块之后运行。如果抛出异常,finally子句在匹配的catch子句之后运行。
在清单3-27中,函数getNumber()抛出一个自定义异常类型ValueTooLargeException。在函数main()中,异常被捕获并再次抛出。
import'dart:math'showRandom;varrandom=Random();classValueTooLargeExceptionimplementsException{intvalue;ValueTooLargeException(this.value);@overrideStringtoString(){return'ValueTooLargeException{value:$value}';}}intgetNumber(){varvalue=random.nextInt(10);if(value>5){throwValueTooLargeException(value);}returnvalue;}voidmain(){try{print(getNumber());}onValueTooLargeExceptioncatch(e){print(e);rethrow;}finally{print('infinally');}}Listing3-27Useexceptions3.15摘要学习一门新的编程语言不是一件容易的事情。尽管Dart看起来与其他编程语言相似,但Dart仍然有一些独特的功能。本章仅简要介绍Dart中的重要功能。