作者: Mike Bluestein   |

原文地址:[https://www.smashingmagazine.com/2018/06/google-flutter-mobile-development/]

【译者注:链接序号对应下面索引列表,另外可以点击阅读原文查看详细的链接文章】

Flutter 是一款由 Google 开发的开源、跨平台移动端开发框架。它允许使用同一个代码库构建高性能、漂亮的 iOS 和 Android 应用,同时它也是 Google 即将推出的 Fuchsia 操作系统的开发平台。此外,通过自定义的 Flutter 引擎可以将其嵌入到其他平台。

Flutter 为什么会出现?为什么要使用它呢?

一直以来,跨平台工具采用以下两种方法之一:

  • 在原生应用程序中嵌入 web view ,像构建网站一样构建应用程序。
  • 封装原生平台里的控件并为它们提供一些跨平台的参数。

为了使移动端开发变得更好,Flutter 尝试了一种不同的方法。它提供了开发人员工作的框架应用程序和能够托管应用程序的可移植运行时的引擎。该框架依托 Skia 图形库而构建,提供了实际渲染时用到的 widgets,而不仅仅是原生应用控件的包装器。就像 web 包装器选项提供的那样,该方法可以灵活的以完全自定义的方式构建跨平台应用程序,同时还会提供流畅的性能体验。与此同时,Flutter 自带的丰富的 widget 库以及一些开源的 widgets 使其成为一个功能丰富的平台。简言之,Flutter 目前是移动端开发者接触到的最接近跨平台开发的东西。

Dart

Flutter 应用程序使用 Dart 编写,Dart 是最初由 Google 开发的一种编程语言。它是一种支持预编译和实时编译的面向对象语言,所以比较适合开发原生应用程序,配合 Flutter 的热加载可以提供高效的开发工作流程。Flutter 最近也转向使用 Dart 2.0 版。Dart 语言提供了许多其他编程语言具有的功能,包括垃圾收集、异步等待、强类型、泛型以及丰富的标准库等等。这些功能对于各种编程语言的开发者们来说都比较熟悉,例如 C#、JavaScript、F#、Swift 和 Java。此外,Dart 可以编译为 Javascript,与 Flutter 结合可以在 web 和移动平台实现代码共享。

事件历史时间表

  • 2015.04

在 Dart 开发者峰会 [1] 上提出 Flutter(最初命名为 Sky )。

  • 2015.11

Sky 重新命名为 Flutter。

  • 2018.02

在2018年世界移动通信大会上,Flutter beta 1 [2] 版本发布。

  • 2018.04

Flutter beta 2 [3] 版本发布

  • 2018.05

Flutter beta 3 [4] 版本在 Google I/O 上发布。Google 宣布 Flutter 可以用于开发应用程序。

与其他开发平台比较

APPLE/ANDROID NATIVE

原生应用程序在使用新功能时带来的困扰是最少的。由于应用程序是使用平台供应商自己(Apple 或 Google)的控件构建,为了让用户体验更加符合给定的平台,因此他们通常遵循这些供应商制定的设计指南。大多数情况下,原生的应用将会比那些跨平台构建的应用性能要好一些,尽管在很多情况下两者的差异可以忽略不计,不过具体还要取决于底层跨平台技术。原生应用的一大优势是:当需要时,他们可以立即采用 Apple 和 Google 在测试版中开发的新技术而不用等待第三方的集成。构建原生应用的主要缺点是缺乏跨平台的代码复用,如果同时开发 iOS 和 Android 应用,那么开发成本可能会很高。

REACT NATIVE

React Native 允许原生应用使用 JavaScript 构建。应用中用到的控件实际上都是原生平台里的控件,所以用户使用起来感觉和原生应用一样。对于那些 React Native 没有提供的需要自定义的应用,仍然需要使用原生开发。当需要定制的模块比较多时,某些情况下,在 React Native 中开发不如使用原生开发更合适。

XAMARIN

当谈到 Xamarin 时,有两种不同的方法将会被提及。跨平台方法:Xamarin.Forms。该方法不同于 React Native,但是从概念上讲是相似的,因为它也是抽象原生控件。同样的,在定制方面它也有和 React Native 同样的缺点。第二种方法:Xamarin-classic。该方法分开使用 Xamarin 的 iOS 和 Android 产品来构建适用于特定平台的功能,就像直接使用 Apple/Android 原生功能一样,只不过在 Xamarin 中需要使用 C# 或 F# 。使用 Xamarin 的好处是可以共享非平台特定的代码,例如网络、数据访问、Web 服务等。

与上面的替代方法不同,Flutter 试图给开发者一个更加完整的跨平台解决方案,包括代码复用、高性能、流畅的用户界面和出色的工具。

一个 Flutter 应用概述

创建一个应用程序

安装 Flutter [5] 之后,使用 Flutter 创建应用程序则非常简单:打开命令行,输入 flutter create[app_name], 在 VS Code 中选择 Flutter:NewProject;在 Android Studio 或 IntelliJ 中选择 StartanewFlutterProject。无论你是选择使用 IDE 还是使用首选编辑器里的命令行,新的 Flutter 应用程序模板都为你提供了一个良好的应用起点。

该应用程序引入了 flutter/material.dart 包,它为应用程序提供了一些基本的元素,例如标题栏、icons 和主题。它还设置了一个带有状态的 widget 来演示当应用程序里的 state 发生变化时用户界面是如何更新的。下面是 Flutter 应用运行在 iOS 和 Android 上的图片:

工具选项

Flutter 在工具方面提供了令人难以置信的灵活性。就像可以从支持的 IDE( 比如 VSCode、 AndroidStudio、或 IntelliJ )中进行开发一样,应用程序可以简单的在任何编辑器的命令行中进行开发。使用何种开发工具很大程度上取决于开发者的喜好。 AndroidStudio提供了大部分的功能,比如用于分析正在运行应用的 widgets 以及监控应用程序性能的 Flutter 检查器。它还提供了一些重构模板,在开发带有层次结构的 widget 时用起来将会很方便; VSCode 提供了更加轻快的开发体验,因为它启动的速度比 AndroidStudio 或 IntelliJ 都要快,而且每个 IDE 都内置了编辑助手,例如代码补全、各种 API 处理以及良好的调试支持;命令行也很好的支持了 Flutter 命令,这使得创建、更新和发布应用都变得简单 ,除了编辑器外不再依赖于其他工具。以下是在各种环境中使用 Flutter 的情景:

热加载

无论使用哪种工具,Flutter 都可以很好的支持热加载。这样在许多情况下就可以修改正在运行的应用程序、维护其状态,而不必停止运行、重新构建和部署了。通过快速迭代,热加载可以极大的提升开发效率。这样也使得这个平台使用起来更友好。

测试

Flutter 包含 WidgetTester 实用程序,用于和测试中的 widgets 交互。新的应用程序模板包含一个示例测试,用来演示在编写测试时如何使用它,如下所示:

  1. // Test included with the new Flutter application template
  2.  
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter_test/flutter_test.dart';
  5.  
  6. import 'package:myapp/main.dart';
  7.  
  8. void main() {
  9. testWidgets('Counter increments smoke test', (WidgetTester tester) async {
  10. // Build our app and trigger a frame.
  11. await tester.pumpWidget(new MyApp());
  12.  
  13. // Verify that our counter starts at 0.
  14. expect(find.text('0'), findsOneWidget);
  15. expect(find.text('1'), findsNothing);
  16.  
  17. // Tap the '+' icon and trigger a frame.
  18. await tester.tap(find.byIcon(Icons.add));
  19. await tester.pump();
  20.  
  21. // Verify that our counter has incremented.
  22. expect(find.text('0'), findsNothing);
  23. expect(find.text('1'), findsOneWidget);
  24. });
  25. }

包和插件的使用

虽然 Flutter 刚宣布可以使用,但已经有一个丰富的开发者生态系统可以使用:A plethora of packages and plugins [6]。当添加一个包或者插件时,只需要在应用程序根目录下的 pubspec.yaml 文件中添加依赖即可。然后通过命令行或 IDE 运行 flutter packagesget, Flutter 就会引入所需的全部依赖。例如,在 Flutter 中使用比较流行的 image picker 插件,则只需要在 pubspec.yaml 文件中添加依赖,如下所示:

  1. dependencies:
  2. image_picker: "^0.4.1"

接着运行 flutter packagesget 命令就会引入使用时所需的一切东西,然后在 Dart 中导入和使用:

  1. import 'package:image_picker/image_picker.dart';

Widgets

在 Flutter 中一切皆 widget 。widget 包括用户界面元素,例如 ListViewTextBox 和 Image以及框架的其他部分,例如布局、动画、手势识别和主题等等。widget 化的设计使得整个应用程序也可以嵌入带有层次结构的其他 widget 中(整个应用程序也是一个 widget)。widget 化的体系结构可以清楚的追踪应用程序中一部分的属性和行为,这与其他大部分应用程序框架不同,大多数的框架是将属性和行为不一致的关联起来,有时将它们与层次结构中的其他组件相关联,有时将其与控件本身相关联。

简单的 UI WIDGET 示例

一个 Flutter 应用的入口是其主要功能。如下所示,在用户界面元素中放入一个 widget,在函数 main() 中调用 runApp() 。

  1. import 'package:flutter/material.dart';
  2.  
  3. void main() {
  4. runApp(
  5. Container(color: Colors.lightBlue)
  6. );
  7. }

结果是一个淡蓝色的 Container widget 铺满了屏幕。

  

无状态和有状态 widgets

widgets 分为两种:无状态的 widgets 和有状态的 widgets 。无状态的 widgets 在他们创建和初始化之后内容不再改变,而有状态的 widgets 当应用程序在运行中时允许改变某些状态,例如与用户之间的交互。举个例子,在应用中同时引用了 FlatButtonwidget 和 Textwidget。 Textwidget 的 state 设置了默认的 String。当按下按钮时导致 state 值改变,这会引起 Textwidget 更新从而显示一个新的 String 值。如果要对 widget 封装则需要创建一个派生自 StatelessWidget 或 StatefulWidget的类。例如,淡蓝色的 Container 可像下面这样写:

  1. class MyWidget extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return Container(color: Colors.lightBlue);
  5. }
  6. }

当创建的 widget 插入到 widget 树中后,Flutter 将会调用 widget 的 build 方法,所以 UI 会被渲染。对于一个有状态的 widget 应该是派生自 StatefulWidget 类:

  1. class MyStatefulWidget extends StatefulWidget {
  2.  
  3. MyStatefulWidget();
  4.  
  5. @override
  6. State createState() {
  7. return MyWidgetState();
  8. }
  9. }

有状态的 widget 将会返回一个 State 类,该类负责为给定的 state 构建 widget 树。当 state 改变时,相应的 widget 树将会重新构建。在下面的代码中,当按钮被点击时,State 类将会更新 String 的值:

  1. class MyWidgetState extends State {
  2. String text = "some text";
  3.  
  4. @override
  5. Widget build(BuildContext context) {
  6. return Container(
  7. color: Colors.lightBlue,
  8. child: Padding(
  9. padding: const EdgeInsets.all(50.0),
  10. child: Directionality(
  11. textDirection: TextDirection.ltr,
  12. child: Column(
  13. children: [
  14. FlatButton(
  15. child: Text('Set State'),
  16. onPressed: () {
  17. setState(() {
  18. text = "some new text";
  19. });
  20. },
  21. ),
  22. Text(
  23. text,
  24. style: TextStyle(fontSize: 20.0)),
  25. ],
  26. )
  27. )
  28. )
  29. );
  30. }
  31. }

通过 setState() 可以更新 state 值。当 setState() 被调用时,这个函数可以重置任何内部的 state 值,像上述例子中的 String;然后调用 build 方法,更新状态 widget 树。

  

还要注意可以使用 Directionality widget 为其子树中需要它的 widget 设置文本方向,比如 Text widgets。这里的示例是从头开始构建代码,所以在 widget 层次结构的一些地方是需要使用 Directionality。然而,使用 MaterialApp widget 会隐式设置文本方向(例如使用默认应用程序模板)。

布局

默认情况下,runApp 函数会将 widget 放大至铺满整个屏幕。为了控制 widget 的布局,Flutter 提供了很多布局 widgets。这些 widgets 允许垂直或水平对齐子 widgets、将 widget 放大以铺满某个特定区域、将 widget 限制在某个区域中、将其置于屏幕中心以及允许 widgets 之间相互重叠。比较常用的两个 widgets 是 Row 和 Column。它们可以水平(Row)或者垂直(Column)显示其子 widgets。使用这些布局 widgets 只需将它们包装在子 widgets 的列表中, mainAxisAlignment 会将 widgets 控制在布局轴的位置,不论是居中、开始、结束还是使用各种间距选项。下面的代码展示了怎样在 Row 或者 Column 中对齐子 widgets。

  1. class MyStatelessWidget extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return Row( //change to Column for vertical layout
  5. mainAxisAlignment: MainAxisAlignment.center,
  6. children: [
  7. Icon(Icons.android, size: 30.0),
  8. Icon(Icons.pets, size: 10.0),
  9. Icon(Icons.stars, size: 75.0),
  10. Icon(Icons.rowing, size: 25.0),
  11. ],
  12. );
  13. }
  14. }

  

响应触摸

触摸交互是由手势处理的,手势是封装在 GestureDetector 类中。由于它也是个 widget,添加手势识别和在 GestureDetector 中封装子 widgets 一样简单。例如,在一个 Icon 上添加触摸事件,将其封装在 GestureDetector中并设置处理程序以捕获所需的手势。

  1. class MyStatelessWidget extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return GestureDetector(
  5. onTap: () => print('you tapped the star'),
  6. onDoubleTap: () => print('you double tapped the star'),
  7. onLongPress: () => print('you long pressed the star'),
  8. child: Icon(Icons.stars, size: 200.0),
  9. );
  10. }
  11. }

在这种情况下,当轻击,双击或长按图标时,将打印相关文本:

  1. To hot reload your app on the fly, press "r". To restart the app entirely, press "R".
  2. An Observatory debugger and profiler on iPhone X is available at: http://127.0.0.1:8100/
  3. For a more detailed help message, press "h". To quit, press "q".
  4. flutter: you tapped the star
  5. flutter: you double tapped the star
  6. flutter: you long pressed the star

除了简单的点击手势外,还有很多丰富的识别功能,适用于从平移、缩放及拖动的所有内容,这也使得构建带有交互的应用程序变得简单。

绘画

Flutter 还提供了很多绘画相关的 widgets,包括修改不透明度、设置剪切路径和应用设置。通过使用 CustomPaintwidget、 CustomPainter 和 Canvas 类的结合,它还支持普通的绘画功能。绘画 widget 的一个示例是 DecoratedBox,可以在屏幕中画一个 BoxDecoration。下面的例子说明了如何使用 DecoratedBox 和渐变填充填充屏幕。

  1. class MyStatelessWidget extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return new DecoratedBox(
  5. child: Icon(Icons.stars, size: 200.0),
  6. decoration: new BoxDecoration(
  7. gradient: LinearGradient(
  8. begin: Alignment.topCenter,
  9. end: Alignment.bottomCenter,
  10. colors: [Colors.red, Colors.blue, Colors.green],
  11. tileMode: TileMode.mirror
  12. ),
  13. ),
  14. );
  15. }
  16. }

  

动画

Flutter 包含一个 AnimationController 类,它可以控制一段时间内的动画播放,包括启动和停止动画以及将值变为动画。此外,有一个 AnimatedBuilder 的 widget 允许和 AnimationController 一起使用构建动画。任何的 widget 都包含它的动画属性,像前面展示的装饰星星。例如,将代码重构为一个 StatefulWidget,因为动画也是 state 变化并且将 AnimationController 传递给 State 类允许在构建 widget 时使用动画值。

  1. class StarWidget extends StatefulWidget {
  2. @override
  3. State createState() {
  4. return StarState();
  5. }
  6. }
  7.  
  8. class StarState extends State with SingleTickerProviderStateMixin {
  9. AnimationController _ac;
  10. final double _starSize = 300.0;
  11.  
  12. @override
  13. void initState() {
  14. super.initState();
  15.  
  16. _ac = new AnimationController(
  17. duration: Duration(milliseconds: 750),
  18. vsync: this,
  19. );
  20. _ac.forward();
  21. }
  22.  
  23. @override
  24. Widget build(BuildContext context) {
  25.  
  26. return new AnimatedBuilder(
  27. animation: _ac,
  28. builder: (BuildContext context, Widget child) {
  29. return DecoratedBox(
  30. child: Icon(Icons.stars, size: _ac.value * _starSize),
  31. decoration: BoxDecoration(
  32. gradient: LinearGradient(
  33. begin: Alignment.topCenter,
  34. end: Alignment.bottomCenter,
  35. colors: [Colors.red, Colors.blue, Colors.green],
  36. tileMode: TileMode.mirror
  37. ),
  38. ),
  39. );
  40. }
  41. );
  42. }
  43. }

在这种情况下,该值可以用于改变此 widget 的大小。只要动画值发生变化就会调用构建器函数,从而导致当装饰星星的大小变化超过750毫秒时产生了规模效应。

  

使用原生的功能

平台通道

为了给 Android 和 iOS 上的原生平台 APIs 提供支持,Flutter 应用可以使用平台通道。这将允许 Flutter Dart 代码向托管的 iOS 或 Android 应用程序发送消息。许多可用的开源插件都是使用平台通道上的消息传递构建的。学习如何使用平台通道,Flutter 文档 [7] 包含了一个访问原生电池 APIs 的好文档。

总结

即使是 beta 版本,Flutter 也提供了一个很好的构建跨平台应用程序的解决方案。凭借其出色的工具和热加载,给用户带来了非常好的开发体验;丰富的开源软件包和详细的文档让你可以轻松入门。接下来,除了 iOS 和 Android,Flutter 的开发者将目标指向了 Fuchsia。考虑到 Flutter 引擎架构的可扩展性,看到它应用在其他各种平台上也不会让我感到惊讶。随着社区的发展,现在正是加入 Flutter 的好时机。

扩展阅读

索引列表

[1] https://www.youtube.com/watch?v=PnIWl33YMwA&feature=youtu.be

[2] https://medium.com/flutter-io/announcing-flutter-beta-1-build-beautiful-native-apps-dc142aea74c0

[3] https://medium.com/flutter-io/https-medium-com-flutter-io-announcing-flutters-beta-2-c85ba1557d5e

[4] https://developers.googleblog.com/2018/05/ready-for-production-apps-flutter-beta-3.html

[5] https://flutter.io/get-started/install/

[6] https://pub.dartlang.org/flutter

[7] https://flutter.io/platform-channels/

文章转自公众号”全栈探索”,欢迎关注:

  

  

  

  

  

  

  

【译】使用 Flutter 实现跨平台移动端开发的更多相关文章

  1. 为什么 Flutter 是跨平台开发的终极之选

    跨平台开发是当下最受欢迎.应用最广泛的框架之一.能实现跨平台开发的框架也五花八门,让人眼花缭乱.最流行的跨平台框架有 Xamarin.PhoneGap.Ionic.Titanium.Monaca.Se ...

  2. 使用nodegui 开发高性能的跨平台桌面端应用

    nodegui 是基于qt + nodejs 的跨平台桌面开发方案,官方同时也提供了很不错的文档 简单使用 使用官方的starter clone 代码 git clone https://github ...

  3. 大前端时代搞定PC/Mac端开发,我有绝招

    如果你是一位前端开发工程师,对"跨平台"一词应该不会感到陌生.像常见的前端框架:比如React.Vue.Angular,它们可以做网页端,也可以做移动端,但很少能做到跨PC.Mac ...

  4. 移动端开发概览【webview和touch事件】

    作为一个前端,而且作为一个做移动端开发的前端,那意味着你要有三头六臂,跟iOS开发哥哥一起打酱油,跟Android开发哥哥一起修bug... Android vs Ios 我在webkit内核的chr ...

  5. [ionic开源项目教程] - 手把手教你使用移动跨平台开发框架Ionic开发一个新闻阅读APP

    前言 这是一个系列文章,从环境搭建开始讲解,包括网络数据请求,将持续更新到项目完结.实战开发中遇到的各种问题的解决方案,也都将毫无保留的分享给大家. 关注订阅号:TongeBlog ,查看移动端跨平台 ...

  6. 移动跨平台开发框架Ionic开发一个新闻阅读APP

    移动跨平台开发框架Ionic开发一个新闻阅读APP 前言 这是一个系列文章,从环境搭建开始讲解,包括网络数据请求,将持续更新到项目完结.实战开发中遇到的各种问题的解决方案,也都将毫无保留的分享给大家. ...

  7. HTML5-前端开发很火且工资很高?

    前言 晚上逛论坛看到一篇对从事HTML5前端开发的文章写的非常不错,和目前的市场形势差不多,然后我在其基础上给大家进行加工总结一下分享给大家.今天我们谈论的话题是<<为什么从事HTML5前 ...

  8. 移动端开发用touch事件还是click事件

    前端开发现在包含了跨浏览器,跨平台(不同操作系统)和跨设备(不同尺寸的设备)开发. 在移动开发的过程中,到底选取touch事件还是click事件?对了,请不要鄙视click,click在移动端开发用着 ...

  9. Flutter基础系列之混合开发(二)

    1.混合开发的场景 1.1作为独立页面加入 这是以页面级作为独立的模块加入,而不是页面的某个元素. 原生页面可以打开Flutter页面 Flutter页面可以打开原生页面 1.2作为页面的一部分嵌入 ...

随机推荐

  1. mvc设计模式的优点

    软件设计的理念是:高内聚,低耦合.采用三层: UI:(jsp,servlet), service:(具体的业务实现), dao:(对数据库的操作) 的设计模式来指导项目开发可以使得项目各层之间是一个粗 ...

  2. golang 调用windows API 中文的处理

    Go语言发展势头很猛,其实缺点也很多,好在有广大爱好者提供了无数的库,把优点表现得太好了,搞得什么都是拿来就使用,基本完全不理会指针,性能还不错. 最近在windows下使用遇到一个中文的问题,首先要 ...

  3. Nginx作为HTTP服务器--Nginx配置图片服务器

      首先安装nginx安装环境 nginx是C语言开发,建议在linux上运行,本教程使用Centos6.5作为安装环境. --> gcc 安装nginx需要先将官网下载的源码进行编译,编译依赖 ...

  4. mac上修改host

    host文件下载地址: https://github.com/highsea/Hosts/blob/master/hosts https://github.com/racaljk/hosts 备份ma ...

  5. QLabel class

    Help on class QLabel in module PyQt5.QtWidgets: class QLabel(QFrame)  |  QLabel(parent: QWidget = No ...

  6. Var的用法解析

    C#关键字是伴随着.NET 3.5以后,伴随着匿名函数.LINQ而来, 由编译器帮我们推断具体的类型.总体来说,当一个变量是局部变量(不包括类级别的变量),并且在声明的时候初始化,是使用var关键字的 ...

  7. MySQL工作原理

    Mysql是由SQL接口,解析器,优化器,缓存,存储引擎组成的.  mysql原理图各个组件说明: 1. connectors 与其他编程语言中的sql 语句进行交互,如php.java等. 2. M ...

  8. Asp.Net Core Options模式的知识总结

    Options模式是Asp.Net Core中用于配置的一种模式,它利用了系统的依赖注入,并且还可以利用配置系统.它使我们可以采用依赖注入的方法直接使用绑定的一个POCO对象,这个POCO对象就叫做O ...

  9. Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock32 error was 14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"

     今天安装完带图形界面的CentOS 7后,在Terminal中运行yum安装命令时报了以下错误: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ...

  10. Acitiviti数据库表设计(学习笔记)

    ACT_ID_*:与权限,用户与用户组,以及用户与用户组关系相关的表 ACT_RU_*:代表了流程引擎运行时的库表,RU表示Runtime ACT_HI_*:HI表示History当流程完成了节点以后 ...