站在前端视角入门Flutter

2,869 阅读12分钟

作者

SugarTurboS团队成员——Tomey

前言

笔者作为一名混迹多年的前端开发工程师,经历移到跨平台技术的从最早的Web AppHybrid AppReact Native、再到现在Flutter的技术变迁。

刚好最近三个月,由于公司业务需要,笔者入坑Flutter已完成三款App的开发,个人感受Flutter是一款优秀的跨端技术。因此,站在前端的角度如何入门Flutter,分享给大家。

跨平台技术的诞生

随着智能手机的兴起,移动App开发的需求遍地开花,互联网公司也是层出不穷。

而智能手机主流的系统,以Android、iOS系统两分天下,因此开发一款App,就需要开发两套代码。

部分公司迫于竞争,想要更迅速更省成本地进行开发,不再满足Android端一套代码,iOS端一套代码。

与此同时,其他技术领域和各大公司也都觊觎着这份大蛋糕,纷纷推出相关的技术,这样跨平台技术应运而生,并且开始在公司中生根发芽。

跨端技术发展史

跨平台技术经过多年的发展,其中衍生的技术框架非常的多。很多还没等笔者去学习,就已经没落了,成为跨平台技术发展历程中的垫脚石。而其中具有跨平台技术发展里程碑的代表框架,例如:

image

Web App

Web App是指使用HTML+CSS+JS开发,运行于标准浏览器上的Web的应用,相当于把浏览器打包成App。

2014年,随着年HTML5的标准规范制定完成,网上舆论大有Web App取代Native App的气势。但Web App与Native App之间的差距,是它始终不能与Native App抗衡。主要问题:

  • 对比Native性能低,尤其在早期智能手机性能一般,操作体验不好。
  • 没有离线功能,对网络要求高,优化不好会加载慢和消耗流量。

以上两点仅仅是影响用户体验问题,但WebApp对比Native最大的问题在于,由于运行在浏览器沙箱内无法调用原生API,导致很多功能无法实现。

为了解决Web App不能调用原生API的能力,业内推出了Hybrid App。

Hybrid App

Hybrid App是指采用Web+Native 来进行混合技术开发。UI界面由Web技术开发,Native只需要提供Web调用原生API的能力。

Hybrid App虽然开发效率高,可以跨平台,但是Hybrid渲染还是有Web那一套,体验还是比不上原生。对于需要快速试错、快速占领市场的团队来说,Hybrid App是一个不错的选择,后期团队稳定下来后,最好还是要做体验更好的原生APP或者使用其他体验更好的跨平台技术。

Hybrid App具有代表性的技术框架,比如PhoneGap、Cordova、Ionic等等。

为了解决Hybrid App渲染性能问题,2015年React Native顺势而生。

React Native

React Native是Facebook早先开源的 Web UI框架React在原生移动应用平台的衍生产物,底层对Android和iOS平台的原生代码进行封装,通过使用JavaScript就可以编写出原生代码。

React Native与原生框架通过Bridge进行通信,所有的JavaScript代码运行在Chrome V8引擎中,通过WebSocket和原生代码进行通信。

由于React Native UI采用Native进行渲染,渲染性能基本接近Native。但是在如动画、滚动、长列表等耗性能的场景,性能距离Native还是有一定的差距。

且React Native与Web HTML+CSS+JS开发模式有一定差异,学习门槛较高。

Flutter

Flutter是谷歌的移动UI框架,可以快速在Android和iOS上构建高质量的原生用户界面,性能上能媲美Native,是继Web App之外另一个“真跨端渲染技术”。

跨平台渲染技术

为什么说Flutter是“真跨端渲染技术”呢?笔者来对比一下,目前流行的跨端框架渲染技术架构,如下图所示:

image

从图中可以看到,React Native、Weex都采用Native组件进行渲染,而Web App、Flutter都自带渲染引擎。

笔者把跨平台技术做了相关矩阵对比,如下:

image

可以看到,综合来看Web App是非常优秀的跨平台技术,但受限于渲染性能一般。除此之外,Flutter综合得分良好,且为大家所诟病的跨平台技术的渲染性能问题,在Flutter上得以解决。

前端视角里的Flutter是怎样的呢?

看看下面这张图,左边是值得点赞的地方,右边是不足的地方。

image

可以看到,Flutter唯一不足之处是与Web技术生态完全不共享,自成一套技术生态。对于web前端开发工程师,抛开Web技术,重新学习一门全新的技术,多少有些抵触情绪。就像当初微信小程序推出,没有采用HTML5的标准,被广大开发者吐槽一样。站在开发者的角度,当初笔者不免也想问问作者:“Flutter为什么不使用TypeScript作为开发语言,使用JSX作为模版语言呢”。

初识Flutter

首选来看一张Flutter中文官网的截图,描述如下:

image

从上图中,笔者可以找到关于Flutter描述的关键信息:

Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。

具备以下特性:

  • 快速开发
  • 富有表现力和灵活的UI
  • 原生性能

Flutter框架结构

Flutter框架结构如下图所示,左边是Flutter官方框架结构图,右边是笔者对框架结构图的基本解释。

image

从上图可以看到,在Framework层,Flutter提供一整套完善的UI开发套件,框架底层依据Skia标准图像处理库,实现跨平台渲染UI统一。

Flutter与Web

前面说了一堆废话,终于要回归正题了——“站在前端视角入门Flutter”。说到前端视角,笔者自然就想到Flutter开发与Web开发有哪些异同?

以下是笔者个人使用的相关对比

image

接下来,笔者将介绍Flutter开发与Web开发异同。

Hello World开始

接下来笔者同时使用Web技术和Flutter技术在页面打印“Hello World”。

Web原生实现

实现非常简单,一个HTML的div标签搞定,代码如下:

<div>Hello World</div>
React实现

相对比Web原生,会多写一些代码,代码如下:

import React from 'react';
import ReactDOM from 'react-dom';
class HelloWorld extends React.PureComponent {
  render() {
    return <div>Hello World</div>;
  }
}

ReactDOM.render(<HelloWorld />, document.getElementById('app'));
Flutter实现

相对比Web,会多写一些代码,代码如下:

import 'package:flutter/cupertino.dart';

class HelloWordWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('Hello World');
  }
}

void main() => runApp(HelloWordWidget());

代码实现步骤:

  1. 首选创建HelloWordWidget类(Flutter一切都是Widget)。
  2. HelloWordWidget类渲染函数build,使用Flutter原生Widget Text渲染“Hello World”。
  3. Flutter启动入口函数main挂载HelloWordWidget。
总结

Flutter实现方式,对于使用过React进行开发的同学,会感觉跟Flutter跟React代码结构很相似。两者都是以组件为核心单元,以组件搭积木的方式开发UI。

在React中组件的概念是Component,对应Flutter中组件的概念是Widget,两者都是为了组件复用而生。也都分为有状态和无状态两类,前者内部维护state,后者则是纯渲染用的无状态组件,这个后面会讲到,本处不展开。

Hello World设置字体颜色red

对于Web开发者,设置字体颜色,首先想到的就是CSS,使用Flutter是如何设置字体颜色的呢?接下来一起揭晓。

Web原生实现

方式1:

<div style="color: red;">Hello World</div>

方式2:

<style>
.c-red { color: red; }
</style>
<div class="c-red">Hello World</div>
React实现
// index.css
.c-red { color: red; }
// -------------------------------------------------
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
class HelloWorld extends React.PureComponent {
  render() {
    return <div className="c-red">Hello World</div>;
  }
}
ReactDOM.render(<HelloWorld />, document.getElementById('app'));
Flutter实现
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class HelloWordWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      'Hello World',
      style: TextStyle(
        color: Colors.red,
      ),
    );
  }
}

void main() => runApp(HelloWordWidget());
总结

Flutter没有Web中CSS对标签元素的样式描述DSL语言,在Flutter中一切都是Widget,包括对组件样式的描述,例如定位、布局、尺寸、边距、边框、背景、颜色、阴影、文本、变换、动画等等都是Widget实现。

Flutter官方提供的Widget非常之多,笔者查阅相关资料,目前也没有找到Flutter官方提供了多少的Widget数量,初步估计有300~400之间。阅读文档和学习Flutter官方提供的所有Widget的使用,是学习Flutter最大的成本

笔者初次开发Flutter应用,对于各种UI实现的效果。笔者都是参考Web开发经验,Google Flutter的相关实现,然后查阅widget文档,实现最终的效果。

Hello World点击切换字体颜色red、black

上面笔者有提到,React和Flutter有两类组件,无状态组件和有状态组件。在上面的案例中,笔者使用的都是无状态组件,接下来笔者会使用有状态组件完成本节需求。

React实现

React实现Hello World文本点击切换字体颜色。

  1. 组件需要有当前字体颜色状态的变量isRed
  2. 通过点击事件,通过setState修改当前的字体颜色状态的变量isRed
  3. 重新触发组件渲染render。
// index.css
.c-red { color: red; }
.c-black { color: black; }
// -------------------------------------------------
import React from "react";
import ReactDOM from "react-dom";
class HelloWorld extends React.PureComponent {
  state = { isRed: false };

  onClickHandle() {
    this.setState({ isRed: !this.state.isRed });
  }

  render() {
    return (
      <div
        style={{ color: this.state.isRed ? "red" : "black" }}
        onClick={() => this.onClickHandle()}>
        Hello World
      </div>
    );
  }
}
ReactDOM.render(<HelloWorld />, document.getElementById('app'));
Flutter实现

Flutter实现方式与React类似,不过HelloWordWidget需要继承StatefulWidget而不是StatelessWidget。

  1. 组件需要有当前字体颜色状态的变量isRed
  2. 通过点击事件,通过setState修改当前的字体颜色状态的变量isRed
  3. 重新触发组件渲染build。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class HelloWordWidget extends StatefulWidget {
  @override
  _HelloWordStateWidget createState() => _HelloWordStateWidget();
}

class _HelloWordStateWidget extends State<HelloWordWidget> {
  bool _isRed = false;

  void _onClickHandle(TapDownDetails tapDownDetails) {
    setState(() {
      _isRed = !_isRed;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: _onClickHandle,
      child: Text(
        'Hello World',
        style: TextStyle(
          color: _isRed ? Colors.red : Colors.black,
        ),
      ),
    );
  }
}

void main() => runApp(HelloWordWidget());
总结

Flutter与React对于有状态组件的使用方式,基本一致。关于StatelessWidget和StatefulWidget介绍,可参考笔者前端组的小伙伴的文章

不过React的PureComponent,自身或父组件调用setState,状态没有变更的情况下,render函数不会触发。

而在Flutter中,自身或父组件调用setState,状态没有变更的情况下,build函数也会触发,因此不建议把非Widget执行逻辑写在build函数处理。

React与Flutter有状态组件生命周期

侵权申明:以下素材来自网络图片,如有侵权,请联系笔者删除。

React生命周期

image

Flutter生命周期

image

总结

从上图,React与Flutter生命周期比较来看,整体流程基本一致,有三个部分组件。

  1. 组件初始化
  2. 内部状态变更setState、父组件状态变更setState,都会触发组件重渲染流程。
  3. 组件卸载

只不过React生命周期更新流程更加细致,相对于之下,Flutter生命周期简单许多。

组件状态管理库

说起React开发,不得不提及全局组件状态管理库。开发过React的同学,应该能感受到对于中大型应用,缺少全局组件状态管理库,跨组件状态传递将是一件非常痛苦的事情。

Flutter开发,同样有组件状态管理库可供选择,如下图所示:

image

笔者在开发中使用的是flutter_bloc,使用方式笔者在这就不讲解了,如果读者开发Flutter应用,可依据团队的情况选择合适的组件状态管理库。

最后

文章写到这基本到尾声了,以上是笔者初次入坑Flutter三个月的总结,期望对准备入坑Flutter开发的同学有所帮助和启发。

最后针对在社区大家对Flutter的提出的关于“Flutter值不值得前端开发的同学学习”的问题,笔者也谈谈个人看法和感受。

这个问题笔者也思考过,笔者认为技术是为业务服务的,要回答上面的问题,首先思考下面三个问题。

  1. 需要看Flutter对于业务的价值点在哪?
  2. 目前存在的同类跨平台技术,Flutter是不是具备足够优势?
  3. 是否符合自身现阶段技术成长的方向?

跨平台技术的价值:文章开始有提到跨平台技术的解决最大的问题是效率问题,对于互联网竞争如此激烈的今天,掌握效率不但可以降低成本,更能掌握了市场主动权,快速试错。所以跨平台技术的需求会一直存在。

Flutter移动跨平台技术的优势:在跨平台技术发展史中,笔者可以看到,最早的移动跨平台技术Web App发展到Hybrid App,基本上解决了移到跨平台技术的落地。但后续发展一直至于力解决的是跨平台技术与Native性能的差距,性能响应用户体验,这是用户不能容忍的,也是产品经理不能容忍的。Flutter能媲美Native性能,关这一点,足够笔者关注它。

是否符合自身现阶段技术成长的方向:这也是笔者思考很久的一个问题,Flutter目前确实与Web前端生态完全不相关,是完全两个不同的发展方向。但对于笔者而言,技术的成长不能局限在具体的开发语言和工具。更多应该关注软技能的成长,例如业务方案设计能力、工程化能力等等,这些在大前端领域都是共通的,无论开发Web还是Flutter,都能有所成长。