关于Flutter,你想知道的都在这里了!
作者|Mike Bluestein译者|胡红星编辑|覃云
Flutter 是 Google 开源的跨平台移动开发框架。 它允许从单个代码库为 iOS 和 Android 构建高性能,美观的应用程序。它也是 Google 即将推出的 Fuchsia 操作系统的开发平台。此外,它的架构可以通过定制的 Flutter 引擎将其引入其他平台。
Flutter 诞生原因
跨平台工具包历来采用以下两种方法之一:
将 Web 视图包装在本机应用程序中,并将应用程序构建为网页。
包装原生平台控件并提供对它们的一些跨平台抽象。
Flutter 采取不同的方法,试图使移动开发更好。 它提供了一个开发人员使用的应用程序框架和一个可移植的运行时引擎。该框架建立在 Skia 图形库上,提供实际呈现的部件,而不仅仅是原生控件的包装。
Flutter 做跨平台开发能够像 web 一样灵活,但同时提供流畅的性能。
Flutter 附带的部件库以及开源部件使其成为一个功能丰富的平台。简单地说,Flutter 是最接近移动开发人员用于跨平台开发的理想平台,灵活性、性能几乎毫无妥协。
Dart
Flutter 使用谷歌开发的 Dart 语言进行开发。 Dart 是一种面向对象的语言,同时支持提前编译和即时编译,非常适合用于构建本地应用程序,同时 Flutter 的热加载有效的提高了开发效率。 Flutter 最近也转向了 Dart 2.0 版本。
Dart 提供了许多其他语言中的功能,包括垃圾回收,异步,强类型,泛型以及丰富的标准库。
Dart 提供了一系列功能,这些功能被来自各种语言的开发人员所熟悉,例如 C#,JavaScript,F#,Swift 和 Java。 另外,Dart 可以编译成 Javascript。 因此 Flutter 可以在网页和移动平台上共享代码。
Flutter 时间表
2015 年 4 月,Flutter(最初代号 Sky)在 Dart Developer Summit 上展示
2015 年 11 月,Sky 重命名为 Flutter
2018 年 2 月,在 2018 年移动世界大会上 Flutter beta 1 官宣
2018 年 4 月,Flutter beta 2 官宣
2018 年 5 月,在 Google I / O 上 Flutter beta 3 官宣。
2018 年 6 月,Flutter 预览版发布
与其他开发平台对比 iOS/Android 原生
原生应用程序采用新功能的过程最为平滑。由于原生应用是使用平台供应商自己(苹果或谷歌)的控件构建,并且通常遵循这些供应商制定的设计准则,因此原生应用倾向于使用户体验更符合给定的平台。
在大多数情况下,原生应用程序的性能会优于使用跨平台构建的应用程序,但在许多情况下,底层跨平台技术可以让与原生应用程序的性能差异忽略不计。
原生应用程序的一大优势是可以立即应用苹果、谷歌在 beta 版本中推出的新技术,而无需等待任何第三方集成。构建原生应用程序的主要缺点是无法做到代码复用,这使得开发成本很高。
React Native
React Native 允许使用 JavaScript 构建应用。 RN 构建的应用使用的实际控件是原生控件,用户拥有与原生应用相近的使用体验。 对于 React Native 抽象层无法满足的应用程序,仍然需要原生开发定制。
如果需要与大量定制的原生代码相结合,那么在 React Native 的抽象层中工作的好处就会减少,这种情况下,原生开发会更有优势。
Xamarin
在讨论 Xamarin 时,有两点需要评估。 对于 Xamarin 的跨平台实现,有 Xamarin.Forms。 尽管该技术与 React Native 非常不同,但它在概念上提供了一种类似的方法,为原生控件提供一个抽象层。 同样,它在定制方面也有类似的缺点。
其次,Xamarin-classic 术语。 这种方法独立使用 Xamarin 的 iOS 和 Android 产品来构建特定平台的功能,就像直接使用苹果 / 安卓原生一样,仅在 Xamarin 情况下使用 C#或 F#。Xamarin 的好处是可以共享非平台特定的代码,例如网络,数据访问,Web 服务等。
与这些替代方案不同,Flutter 试图为开发人员提供更完整的跨平台解决方案,其中包含代码重用,高性能,流畅的用户界面和出色的工具。
关于 Flutter 应用 创建应用程序
安装 Flutter 后,创建应用非常简单,在终端输入 flutter create [app_name] 命令,或在 VS Code 命令面板中选择“Flutter:New Project”,或在 Android Studio、IntelliJ 中选择“Start a new Flutter Project”。
无论使用 IDE 还是你喜欢的编辑器加命令行,Flutter 应用程序模板都提供了一个良好的开端。
该应用程序带来了 flutter/material.dart 包,为应用程序提供了一些基础功能,例如标题栏,material 图标和主题。 它还设置了一个有状态的部件,用于演示在应用程序状态更改时如何更新用户界面。
开发工具
Flutter 在开发工具的选择上很灵活。 应用程序可以通过命令行以及任何编辑器轻松开发,这些编辑器来自受支持的 IDE,如 VS Code,Android Studio 或 IntelliJ。 使用哪种 IDE 取决于用户的偏好。
Android Studio 提供了最多的功能,例如 Flutter Inspector 来分析正在运行的应用程序的窗口部件以及监视应用程序性能。 还提供了开发部件层次结构时很方便的几个重构。
VS Code 提供了更轻松的开发体验,因此它的启动速度往往比 Android Studio / IntelliJ 更快。 每个 IDE 都提供内置的编辑助手,如代码补全,接口定义跳转以及良好的调试支持。
Flutter 也很好的支持命令行,这使得创建,更新和启动应用程序变得容易,除了编辑器之外没有任何其他工具依赖性。
热加载
无论采用何种工具,Flutter 都能为应用程序的热加载提供出色的支持。 这允许在许多情况下修改正在运行的应用程序,维护状态,而不必停止应用程序,重新构建和重新部署。
通过允许更快的迭代,热加载可显着提高开发效率。
测试
Flutter 包含一个 WidgetTester 实用程序,用于与测试中的部件进行交互。 新的应用程序模板包含一个示例测试,用于演示在创建测试时如何使用它,如下所示:
// Test included with the new Flutter application template import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:myapp/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(new MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); // Tap the '+' icon and trigger a frame. await tester.tap(find.byIcon(Icons.add)); await tester.pump(); // Verify that our counter has incremented. expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); }
包管理和插件
Flutter 刚刚开始,但已经有了一个丰富的开发者生态系统:开发人员已有大量可以使用的包和插件。
要添加包或插件,只需在应用程序的根目录下的 pubspec.yaml 文件中包含依赖项即可。 然后从命令行或 IDE 运行 flutter packages get,Flutter 的工具将引入所有必需的依赖关系。
例如,要将流行的图像选择器插件用于 Flutter,只需在 pubspec.yaml 中将其列为依赖项,如下所示:
dependencies: image_picker: "^0.4.1"
然后运行 flutter packages get 拉取所有依耐项,然后可以在 Dart 中导入和使用它:
import 'package:image_picker/image_picker.dart';
部件
Flutter 里的所有东西都是一个一个的部件。 这包括用户界面元素,例如 ListView,TextBox 和 Image,以及框架的其他部分,包括布局,动画,手势识别和主题等。
通过将所有内容都设置为窗口部件,整个应用程序可以在窗口部件层次结构中表示。 拥有一个所有内容都是部件的架构,可以清楚地了解作用于某一部分的属性和行为的来源。 这与大多数其他应用程序框架不同,它们将属性和行为不一致地关联起来,有时将属性和行为从层次结构中的其他组件附加到控件本身,有时自身控制属性和行为。
部件示例
Flutter 应用程序的入口点是 main 函数。 要在屏幕上放置用户界面元素的部件,在 main()中调用 runApp()并将部件层次结构根部的部件作为参数传递。
import 'package:flutter/material.dart'; void main() { runApp( Container(color: Colors.lightBlue) ); }
运行结果为填充屏幕的浅蓝色容器部件:
无状态 VS 有状态
部件有两种形式:无状态和有状态。 无状态部件在创建和初始化后不会更改它们的内容,而有状态部件维护一些程序运行时可变的状态,例如,响应用户交互。
在此示例中,FlatButton 部件和 Text 部件将绘制到屏幕上。 Text 部件从其状态开始时会有默认的 String。 按下按钮会导致状态更改,Text 部件更新,从而显示新的 String。
要封装一个部件,需要创建一个派生自 StatelessWidget 或 StatefulWidget 的类。 例如,浅蓝色容器可以写成如下形式:
class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container(color: Colors.lightBlue); } }
当 Flutter 插入到窗口部件树中时,它会调用窗口部件的 build 方法,因此可以呈现 UI 的这一部分。
对于有状态的部件,从 StatefulWidget 派生:
class MyStatefulWidget extends StatefulWidget { MyStatefulWidget(); @override State createState() { return MyWidgetState(); } }
有状态部件返回一个负责为给定状态构建部件树的 State 类。 状态更改时,将重建窗口部件树的关联部分。
在以下代码中,State 类在单击按钮时更新字符串:
class MyWidgetState extends State { String text = "some text"; @override Widget build(BuildContext context) { return Container( color: Colors.lightBlue, child: Padding( padding: const EdgeInsets.all(50.0), child: Directionality( textDirection: TextDirection.ltr, child: Column( children: [ FlatButton( child: Text('Set State'), onPressed: () { setState(() { text = "some new text"; }); }, ), Text( text, style: TextStyle(fontSize: 20.0)), ], ) ) ) ); } }
状态在传递给 setState()的函数中更新。 当调用 setState()时,该函数可以设置任何内部状态,例如本例中的字符串。然后,将调用 build 方法,更新状态部件树。
还要注意使用 Directionality 部件为其子树中需要它的任何部件设置文本方向,例如 Text 部件。 这里的例子是从头构建代码,所以 Directionality 部件需要在部件层次结构的某处。 但是,使用 MaterialApp 窗口部件(例如使用默认应用程序模板)会隐式设置文本方向。
布局
默认情况下,runApp 函数会使窗口填充整个屏幕。 为了控制窗口部件布局,Flutter 提供了各种布局窗口部件。 一些布局部件用于子部件的垂直或水平对齐,扩展部件以填充特定空间,将部件限制到特定区域,将它们在屏幕上居中,并允许部件相互重叠。
两个常用的部件是行和列。这些部件执行布局以水平(行)或垂直(列)显示其子部件。
使用这些布局部件只需将它们包装在子部件列表中。mainAxisAlignment 用于控制部件如何沿布局轴线摆放,无论是居中,左对齐,右对齐还是使用各种间距对齐。
以下代码显示如何对齐行或列中的多个子部件:
class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( //change to Column for vertical layout mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.android, size: 30.0), Icon(Icons.pets, size: 10.0), Icon(Icons.stars, size: 75.0), Icon(Icons.rowing, size: 25.0), ], ); } }
响应触摸事件
触摸交互是使用手势处理的,手势被封装在 GestureDetector 类中。由于它也是一个部件,因此添加手势识别只需要将子部件封装在 GestureDetector 部件中。
例如,要向 Icon 添加触摸处理,请将其设置为 GestureDetector 的子项,并设置检测器的回调以捕获所需的手势。
class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( onTap: () => print('you tapped the star'), onDoubleTap: () => print('you double tapped the star'), onLongPress: () => print('you long pressed the star'), child: Icon(Icons.stars, size: 200.0), ); } }
在这种情况下,当点击图片,双击或长按时,将打印相关文本: