[译] 使用 Flutter 实现跨平台移动端开发

做者: Mike Bluestein | 译:孙印凤html

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

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

简介:Flutter 使得建立跨平台移动端应用变得垂手可得。本文将介绍 Flutter 并将其与其余移动端开发平台进行比较,还会阐述如何使用它来构建应用程序。github

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

Flutter 为何会出现?为何要使用它呢?

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

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

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

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: New Project;在 Android Studio 或 IntelliJ 中选择 Start a new Flutter Project。不管你是选择使用 IDE 仍是使用首选编辑器里的命令行,新的 Flutter 应用程序模板都为你提供了一个良好的应用起点。

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

工具选项

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

热加载

不管使用哪一种工具,Flutter 均可以很好的支持热加载。这样在许多状况下就能够修改正在运行的应用程序、维护其状态,而没必要中止运行、从新构建和部署了。经过快速迭代,热加载能够极大的提高开发效率。这样也使得这个平台使用起来更友好。

测试

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

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

dependencies:
  image_picker: "^0.4.1"

复制代码

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

import 'package:image_picker/image_picker.dart';

复制代码

Widgets

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

简单的 UI WIDGET 示例

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

import 'package:flutter/material.dart';

void main() {
  runApp(
    Container(color: Colors.lightBlue)
  );
}

复制代码

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

无状态和有状态 widgets

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

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(color: Colors.lightBlue);
  }
}

复制代码

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

class MyStatefulWidget extends StatefulWidget {

  MyStatefulWidget();

  @override
  State createState() {
    return MyWidgetState();
  }
}

复制代码

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

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() 能够更新 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 是 RowColumn。它们能够水平(Row)或者垂直(Column)显示其子 widgets。使用这些布局 widgets 只需将它们包装在子 widgets 的列表中,mainAxisAlignment 会将 widgets 控制在布局轴的位置,不管是居中、开始、结束仍是使用各类间距选项。下面的代码展现了怎样在 Row 或者 Column 中对齐子 widgets。

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 类中。因为它也是个 widget,添加手势识别和在 GestureDetector 中封装子 widgets 同样简单。例如,在一个 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),
    );
  }
}

复制代码

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

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

复制代码

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

绘画

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

class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new DecoratedBox(
      child: Icon(Icons.stars, size: 200.0),
      decoration: new BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: [Colors.red, Colors.blue, Colors.green],
          tileMode: TileMode.mirror
        ),
      ),
    );
  }
}

复制代码

动画

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

class StarWidget extends StatefulWidget {
  @override
  State createState() {
    return StarState();
  }
}

class StarState extends State with SingleTickerProviderStateMixin {
  AnimationController _ac;
  final double _starSize = 300.0;

   @override
  void initState() {
    super.initState();

    _ac = new AnimationController(
      duration: Duration(milliseconds: 750),
      vsync: this,
    );
    _ac.forward();
  }

  @override
  Widget build(BuildContext context) {

    return new AnimatedBuilder(
      animation: _ac,
      builder: (BuildContext context, Widget child) {
        return DecoratedBox(
          child: Icon(Icons.stars, size: _ac.value * _starSize),
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: [Colors.red, Colors.blue, Colors.green],
              tileMode: TileMode.mirror
            ),
          ),
        );
      }
   );
  }
}

复制代码

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

使用原生的功能

平台通道

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

总结

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

扩展阅读

  • 安装 Flutter: https://flutter.io/get-started/install/

  • Dart 语言之旅: https://www.dartlang.org/guides/language/language-tour

  • Flutter Codelabs: https://flutter.io/codelabs/

  • Flutter Udacity 课程: https://www.udacity.com/course/build-native-mobile-apps-with-flutter--ud905

  • 文章源代码: https://gist.github.com/mikebluestein/3350443df4689ddac115b68d1598d18e

索引列表

[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/

文章转自微信公众号“全栈探索”,欢迎扫描下面二维码关注!

相关文章
相关标签/搜索