Flutter介绍

来源:http://www.chinese-glasses.com 作者:Web前端 人气:141 发布时间:2020-04-29
摘要:时间: 2019-09-17阅读: 287标签: Flutter Flutter是Google开发的新一代跨平台方案,Flutter可以实现写一份代码同时运行在iOS和Android设备上,并且提供很好的性能体验。Flutter使用Dart作为开发语言

时间: 2019-09-17阅读: 287标签: Flutter

图片 1

Flutter是Google开发的新一代跨平台方案,Flutter可以实现写一份代码同时运行在iOS和Android设备上,并且提供很好的性能体验。Flutter使用Dart作为开发语言,这是一门简洁、强类型的编程语言。Flutter对于iOS和Android设备,提供了两套视觉库,可以针对不同的平台有不同的展示效果。

开始之前

本人使用Flutter开发的一个图文App《每日图文》,可以同时运行在Android和iOS上,欢迎体验,喜欢的话还不吝Star一下。

叮叮叮~ 传送门

图片 2demo.gif

Flutter原本是为了解决Web开发中的一些问题,而开发的一套精简版Web框架,拥有独立的渲染引擎和开发语言,但后来逐渐演变为移动端开发框架。正是由于Dart当初的定位是为了替代JS成为Web框架,所以Dart的语法更接近于JS语法。例如定义对象构建方法,以及实例化对象的方式等。

Flutter为何物?

随着移动App开发成本越来越高,近几年,移动跨平台开发的呼声层出不穷,如FaceBook的推出React-Native,大受欢迎,但其性能并不如人意。

2018年2月27日世界移动大会上,谷歌发布一个跨平台开发框架,名曰Flutter,用于构建高性能的原生Android和iOS两大平台的App。

目前还处于测试版本阶段:Beta 3。

在Google刚推出Flutter时,其发展很缓慢,终于在18年发布第一个Bate版之后迎来了爆发性增长,发布第一个Release版时增长速度更快。可以从Github上Star数据看出来这个增长的过程。在19年最新的Flutter 1.2版本中,已经开放Web支持的Beta版。

Flutter编程语言

Flutter使用的编程语言是Dart,Dart是一门动态语言,几年前扬言要替换JavaScript地位,结果可想而知。

想必还是很多人都没听说过这门语言吧,原谅我也没听过。没想到如今Dart以这样的形式再次出现在开发者面前。

Dart虽然是一门动态语言,但是像极了Java,作为一名Android开发者,几乎可以无缝切换,跟Kotlin也有几分相似。相比Java,Dart更为简洁。

具体可以到Dart语言中文社区了解学习。

目前已经有不少大型项目接入Flutter,阿里的咸鱼、头条的抖音、腾讯的NOW直播,都将Flutter当做应用程序的开发语言。除此之外,还有一些其他中小型公司也在做。

Flutter特点

  • 支持热加载,运行之后,修改代码无需重新安装和启动,提升开发调试效率
  • 支持数据状态绑定,修改数据后自动刷新页面
  • 所有的UI都是Widget,包括页面

整体架构

Flutter环境配置

首先clone Flutter的repo

git clone -b beta https://github.com/flutter/flutter.gitexport PUB_HOSTED_URL=https://pub.flutter-io.cn //国内用户需要设置export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //国内用户需要设置export PATH=`pwd`/flutter/bin:$PATH

然后检查是否需要安装其它依赖,根据提示来安装其它的依赖

flutter doctor

如果是Mac的话,需要配置Flutter全局环境变量,在命令行输入:

$HOME/.bash_profile

在.bash_profile文件添加以下环境变量:

其中[PATH_TO_FLUTTER_GIT_DIRECTORY]是上面clone的Flutter Repo的本地路径

export PUB_HOSTED_URL=https://pub.flutter-io.cn //国内用户需要设置export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //国内用户需要设置export PATH= PATH_TO_FLUTTER_GIT_DIRECTORY/flutter/bin:$PATH

最后运行来生效当前命令行窗口:

source $HOME/.bash_profile

更多平台配置可到【Flutter中文网】了解

Flutter可以理解为开发SDK或者工具包,其通过Dart作为开发语言,并且提供Material和Cupertino两套视觉控件,视图或其他和视图相关的类,都以Widget的形式表现。Flutter有自己的渲染引擎,并不依赖原生平台的渲染。Flutter还包含一个用C++实现的Engine,渲染也是包含在其中的。

Flutter开发工具配置

Flutter App可以使用VS Code、Android Studio、IntelliJ进行开发。我直接用的Android Studio。

开始之前,你需要安装两个插件:

  • Flutter插件: 支持Flutter开发工作流 (运行、调试、热重载等).
  • Dart插件: 提供代码分析 (输入代码时进行验证、代码补全等).

如果在AS中无法在线安装,可以到以下链接中下载离线安装:

  • Flutter插件下载
  • Dart插件下载

注意下载的插件版本一定要和Android Studio JRE版本对应上,可以在Android Studio -> About Android Studio中查看版本。

Engine

模拟器配置

Android Studio支持Android模拟器和iPhone模拟器预览,具体安装参见Flutter中文网,这里不再赘述。

Flutter是一套全新的跨平台方案,Flutter并不像React Native那样,依赖原生应用的渲染,而是自己有自己的渲染引擎,并使用Dart当做Flutter的开发语言。Flutter整体框架分为两层,底层是通过C++实现的引擎部分,Skia是Flutter的渲染引擎,负责跨平台的图形渲染。Dart作为Flutter的开发语言,在C++引擎上层是Dart的Framework。

新建Flutter App工程

安装了以上两个插件后,就可以在Android Studio中新建Flutter工程了,按照提示新建即可。

图片 3新建工程

工程目录如下:

图片 4工程目录

可以看到,工程目录结构主要分为3部分,分别是:

  • android 存放Android相关的东西,如App图标
  • ios 存放iOS相关的东西,如App图标
  • lib 存放Flutter源码

重点来看Flutter的入口:lib/main.dart

import 'package:flutter/material.dart';void main() => runApp(new MyApp;class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Welcome to Flutter', home: new Scaffold( appBar: new AppBar( title: new Text('Welcome to Flutter'), ), body: new Center( child: new Text('Hello World'), ), ), ); }}

整个App的入口在void main()方法中。接下来,就来理清一下这个简单的代码。

Flutter不仅仅提供了一套视觉库,在Flutter整体框架中包含各个层级阶段的库。例如实现一个游戏功能,上面一些游戏控件可以用上层视觉库,底层游戏可以直接基于Flutter的底层库进行开发,而不需要调用原生应用的底层库。Flutter的底层库是基于Open GL实现的,所以Open GL可以做的Flutter都可以。

Flutter页面架构

App开发最重要的就是UI框架,Flutter重新定制一套自己的UI框架,在底层使用了Skia进行渲染。

在Flutter中,一切皆是Widget,页面是Widget,普通的控件也是Widget。

从main.dart中可以看到,Flutter的布局是一层一层嵌套形成的。

  • 第一层是Scaffold,一个实现了Material design的布局控件,包含了一个AppBar和一个body,分别对应标题栏和页面。

  • 第二层body是一个Center布局控件,该布局用于使其子布局即child,在父布局中居中显示

  • 最后的子布局是一个Text控件。

整个布局就是在页面的中间显示了Hello World。

MyApp继承的StatelessWidget,就是其中一个页面相关的Widget,这个窗口的特点是静态,页面中的数据一旦渲染后,就不能在更改,一般用于显示静态页面。

另一个StatefulWidget,则是可以改变状态的Widget,页面的显示随着状态改变而改变。通过setState方法可以非常方便的修改页面状态,与现在web动态绑定框架是类似的。

一个StatefulWidget页面看起来是这样的:

class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super; final String title; @override _MyHomePageState createState() => new _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState { _counter++; }); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new Text( 'You have pushed the button this many times:', ), new Text( '$_counter', style: Theme.of.textTheme.display1, ), ], ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: new Icon(Icons.add), ), ); }}

可以看到,StatefulWidget的业务逻辑基本上都在_MyHomePageState中,这个类继承了State。

当点击了FloatingActionButton时,就会调用_incrementCounter()方法进行setState(),页面就会跟着改变了。

注:Dart使用下划线_表示私有,如上面的_count和_incrementCounter()

由于Flutter的布局模式,当页面比较复杂时,整个布局会变得非常复杂,可读性也大大降低了,修改起来非常麻烦。

另外,Flutter为了布局灵活性,定义了太多的布局控件,几十个布局看得眼花缭乱,从来没见过这么多布局的框架。

视觉库

Flutter插件管理

Flutter使用pubspec.yaml管理插件库,具体如下:

name: dailydescription: A application to look up photo and article#依赖插件配置dependencies: flutter: sdk: flutter cupertino_icons: ^0.1.2dev_dependencies: flutter_test: sdk: flutterflutter: uses-material-design: true #本地资源配置 assets: - images/ic-pic-loading.png - images/ic-pic-article.gif

在上层Framework中包含两套视觉库,符合Android风格的Material,和符合iOS风格的Cupertino。也可以在此基础上,封装自己风格的系统组件。Cupertino是一套iOS风格的视觉库,包含iOS的导航栏、button、alertView等。

Run App

通过Android Studio就可以直接在模拟器上安装App了,当然,前提是你要先配置好模拟器。

如果修改了代码,就可以直接通过热加载,直接显示修改的内容,无需重新安装打开,大大节省了调试时间。

Flutter对不同硬件平台有不同的兼容,例如同样的Material代码运行在iOS和Android不同平台上,有一些平台特有的显示和交互,Flutter依然对其进行了区分适配。例如滑动ScrollView时,iOS平台是有回弹效果的,而Android平台则是阻尼效果。例如iOS的导航栏标题是居中的,Android导航栏标题是向左的,等等。这些Flutter都做了区分兼容。

总结

  • Flutter的对于熟悉Java或者Android开发的人来说,还是比较友好的,可以比较快的熟悉和掌握基本的开发工具和开发理念;
  • Flutter App的性能确实还是不错的,整个的体验与原生App基本差不多;
  • 热加载非常好用,可以节省不少时间;
  • 支持数据和状态自动绑定,通过setState可以非常方便的修改页面状态;
  • Flutter 布局嵌套非常坑爹,布局复杂将会非常痛苦;
  • 基本上实现Android和iOS一套代码通用,开发效率大大提高了。
  • 仍然有许多需要改进的地方,许多特性支持也不太好,如webview这些需要使用第三方插件,或自己定制。

现在Flutter仍然为Beta版本,希望后面可以带来更多惊喜和更好的体验吧。

除了Flutter为我们做的一些适配外,有一些控件是需要我们自己做适配的,例如AlertView,在Android和iOS两个平台下的表现就是不同的。这些iOS特性的控件都定义在Cupertino中,所以建议在进行App开发时,对一些控件进行上层封装。

例如AlertView则对其进行一个二次封装,控件内部进行设备判断并选择不同的视觉库,这样可以保证各个平台的效果。

虽然Flutter对于iOS和Android两个平台,开发有cupertino和material两个视觉库,但实际开发过程中的选择,应该使用material当做视觉库。因为Flutter对iOS的支持并不是很好,主要对Android平台支持比较好,material中的UI控件要比cupertino多好几倍。

Dart

Dart是Google在2011年推出的一款应用于Web开发的编程语言,Dart刚推出的时候,定位是替代JS做前端开发,后来逐步扩展到移动端和服务端。

Dart是Flutter的开发语言,Flutter必须遵循Dart的语言特性。在此基础上,也会有自己的东西,例如Flutter的上层Framework,自己的渲染引擎等。可以说,Dart只是Flutter的一部分。

Dart是强类型的,对定义的变量不需要声明其类型,Flutter会对其进行类型推导。如果不想使用类型推导,也可以自己声明指定的类型。

Hot Reload

Flutter支持亚秒级热重载,Android Studio和VSCode都支持Hot Reload的特性。但需要区分的是,热重载和热更新是不同的两个概念,热重载是在运行调试状态下,将新代码直接更新到执行中的二进制。而热更新是在上线后,通过Runtime或其他方式,改变现有执行逻辑。

AOT、JIT

Flutter支持AOT(Ahead of time)和JIT(Just in time)两种编译模式,JIT模式支持在运行过程中进行Hot Reload。刷新过程是一个增量的过程,由系统对本次和上次的代码做一次snapshot,将新的代码注入到DartVM中进行刷新。但有时会不能进行Hot Reload,此时进行一次全量的Hot Reload即可。

而AOT模式则是在运行前预先编译好,这样在每次运行过程中就不需要进行分析、编译,此模式的运行速度是最快的。Flutter同时采用了两种方案,在开发阶段采用JIT模式进行开发,在release阶段采用AOT模式,将代码打包为二进制进行发布。

在开发原生应用时,每次修改代码后都需要重新编译,并且运行到硬件设备上。由于Flutter支持Hot Reload,可以进行热重载,对项目的开发效率有很大的提升。

由于Flutter实现机制支持JIT的原因,理论上来说是支持热更新以及服务器下发代码的。可以从服务器。但是由于这样会使性能变差,而且还有审核的问题,所以Flutter并没有采用这种方案。

实现原理

Flutter的热重载是基于State的,也就是我们在代码中经常出现的setState方法,通过这个来修改后,会执行相应的build方法,这就是热重载的基本过程。

Flutter的hot reload的实现源码在下面路径中,在此路径中包含run_cold.dart和run_hot.dart两个文件,前者负责冷启动,后者负责热重载。

~/flutter/packages/flutter_tools/lib/src/run_hot.dart

热重载的代码实现在run_hot.dart文件中,有HotRunner来负责具体代码执行。当Flutter进行热重载时,会调用restart函数,函数内部会传入一个fullRestart的bool类型变量。热重载分为全量和非全量,fullRestart参数就是表示是否全量。以非全量热重载为例,函数的fullRestart会传入false,根据传入false参数,下面是部分核心代码。

FutureOperationResult restart({ bool fullRestart = false, bool pauseAfterRestart = false, String reason }) async { if (fullRestart) { // ..... } else { final bool reloadOnTopOfSnapshot = _runningFromSnapshot; final String progressPrefix = reloadOnTopOfSnapshot ? 'Initializing' : 'Performing'; final Status status = logger.startProgress( '$progressPrefix hot reload...', progressId: 'hot.reload' ); OperationResult result; try { result = await _reloadSources(pause: pauseAfterRestart, reason: reason); } finally { status.cancel(); } }}

调用restart函数后,内部会调用_reloadSources函数,去执行内部逻辑。下面是大概逻辑执行流程。

在_reloadSources函数内部,会调用_updateDevFS函数,函数内部会扫描修改的文件,并将文件修改前后进行对比,随后会将被改动的代码生成一个kernel files文件。

随后会通过HTTP Server将生成的kernel files文件发送给Dart VM虚拟机,虚拟机拿到kernel文件后会调用_reloadSources函数进行资源重载,将kernel文件注入正在运行的Dart VM中。当资源重载完成后,会调用RPC接口触发Widgets的重绘。

跨平台方案对比

现在市面上RN、Weex的技术方案基本一样,所以这里就以RN来代表类似的跨平台方案。Flutter是基于GPU进行渲染的,而RN则将渲染交给原生平台,而自己只是负责通过JSCore将视图组织起来,并处理业务逻辑。所以在渲染效果和性能这块,Flutter的性能比RN要强很多。

跨平台方案一般都需要对各个平台进行平台适配,也就是创建各自平台的适配层,RN的平台适配层要比Flutter要大很多。因为从技术实现来说,RN是通过JSCore引擎进行原生代码调用的,和原生代码交互很多,所以需要更多的适配。而Flutter则只需要对各自平台独有的特性进行适配即可,例如调用系统相册、粘贴板等。

Flutter技术实现是基于更底层实现的,对平台依赖度不是很高,相对来说,RN对平台的依赖度是很高的。所以RN未来的技术升级,包括扩展之类的,都会受到很大的限制。而Flutter未来的潜力将会很大,可以做很多技术改进。

Widget

在Flutter中将显示以及和显示相关的部分,都统一定义为widget,下面列举一些widget包含的类型:

用于显示的视图,例如ListView、Text、Container等。用来操作视图,例如Transform等动画相关。视图布局相关,例如Center、Expanded、Column等。

在Flutter中,所有的视图都是由Widget组成,Label、AppBar、ViewController等。在Flutter的设计中,组合的优先级要大于继承,整体视图类结构继承层级很浅但单层很多类。如果想定制或封装一些控件,也应该以组合为主,而不是继承。

在iOS开发中,我也经常采用这种设计方案,组合大于继承。因为如果继承层级过多的话,一个是不便于阅读代码,还有就是不好维护代码。例如底层需要改一个通用的样式,但这个类的继承层级比较复杂,这样改动的话影响范围就比较大,会将一些不需要改的也改掉,这时候就会发现继承很鸡肋。但在iOS中有Category的概念,这也是一种组合的方式,可以通过将一些公共的东西放在Category中,使继承的方便性和组合的灵活性达到一个平衡。

Flutter中并没有单独的布局文件,例如iOS的XIB这种,代码都在Widget中定义。和UIView的区别在于,Widget只是负责描述视图,并不参与视图的渲染。UIView也是负责描述视图,而UIView的layer则负责渲染操作,这是二者的区别。

了解Widget

在应用程序启动时,main方法接收一个Widget当做主页面,所以任何一个Widget都可以当做根视图。一般都是传一个MaterialApp,也可以传一个Container当做根视图,这都是被允许的。

在Flutter应用中,和界面显示及用户交互的对象都是由Widget构成的,例如视图、动画、手势等。Widget分为StatelessWidget和StatefulWidget两种,分别是无状态和有状态的Widget。

StatefulWidget本质上也是无状态的,其通过State来处理Widget的状态,以达到有状态,State出现在整个StatefulWidget的生命周期中。

当构建一个Widget时,可以通过其build获得构建流程,在构建流程中可以加入自己的定制操作,例如对其设置title或视图等。

return Scaffold( appBar: AppBar( title: Text('ListView Demo'), ), body: ListView.builder( itemCount: dataList.length, itemBuilder: (BuildContext context, int index) { return Text(dataList[index]); }, ),);

有些Widget在构建时,也提供一些参数来帮助构建,例如构建一个ListView时,会将index返回给build方法,来区别构建的Cell,以及构建的上下文context。

itemBuilder: (BuildContext context, int index) { return Text(dataList[index]);}

StatelessWidget

StatelessWidget是一种静态Widget,即创建后自身就不能再进行改变。在创建一个StatelessWidget后,需要重写build函数。每个静态Widget都会有一个build函数,在创建视图对象时会调用此方法。同样的,此函数也接收一个Widget类型的返回值。

class RectangleWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Center ( // UI Code ); }}

StatefulWidget

Widget本质上是不可被改变的,但StatefulWidget将状态拆分到State中去管理,当数据发生改变时由State去处理视图的改变。

下面是创建一个动态Widget,当创建一个动态Widget需要配合一个State,并且需要重写createState方法。重写此函数后,指定一个Widget对应的State并初始化。

下面例子中,在StatefulWidget的父类中包含一个Key类型的key变量,这是无论静态Widget还是动态Widget都具备的参数。在动态Widget中定义了自己的成员变量title,并在自定义的初始化方法中传入,通过下面DynamicWidget类的构造方法,并不需要在内部手动进行title的赋值,title即为传入的值,是由系统完成的。

class DynamicWidget extends StatefulWidget { DynamicWidget({Key key, this.title}) : super (key : key); final String title; @override DynamicWidgetState createState() = new DynamicWidgetState();}

由于上面动态Widget定义了初始化方法,在调用动态Widget时可以直接用自定义初始化方法即可。

DynamicWidget(key: 'key', title: 'title');

State

StatefulWidget的改变是由State来完成的,State中需要重写build方法,在build中进行视图组织。StatefulWidget是一种响应式视图改变的方式,数据源和视图产生绑定关系,由数据源驱动视图的改变。

改变StatefulWidget的数据源时,需要调用setState方法,并将数据源改变的操作写在里面。使用动态Widget后,是不需要我们手动去刷新视图的。系统在setState方法调用后,会重新调用对应Widget的build方法,重新绘制某个Widget。

下面的代码示例中添加了一个float按钮,并给按钮设置了一个回调函数_onPressAction,这样在每次触发按钮事件时都会调用此函数。counter是一个整型变量并和Text相关联,当counter的值在setState方法中改变时,Text Widget也会跟着变化。

class DynamicWidgetState extends StateDynamicWidget { int counter = 0; void _onPressAction() { setState(() { counter++; }); } @override Widget build(BuildContext context) { return new Scaffold( body: Center( child: Text('Button tapped $_counter.') ), floatingActionButton: FloatingActionButton( onPressed: _onPressAction, tooltip: 'Increment', child: Icon(Icons.add) ) ); } }

主要Widget

在iOS中有UINavigationController的概念,其并不负责显示,而是负责控制各个页面的跳转操作。在Flutter中可以将MaterialApp理解为iOS的导航控制器,其包含一个navigationBar以及导航栈,这和iOS是一样的。

在iOS中除了用来显示的视图外,视图还有对应的UIViewController。在Flutter中并没有专门用来管理视图并且和View一对一的类,但从显示的角度来说,有类似的类Scaffold,其包含控制器的appBar,也可以通过body设置一个widget当做其视图。

theme

theme是Flutter提供的界面风格API,MaterialApp提供有theme属性,可以在MaterialApp中设置全局样式,这样可以统一整个应用的风格。

new MaterialApp( title: title, theme: new ThemeData( brightness: Brightness.dark, primaryColor: Colors.lightBlue[800], accentColor: Colors.cyan[600], ));

如果不想使用系统默认主题,可以将对应的控件或试图用Theme包起来,并将Theme当做Widget赋值给其他Widget。

new Theme( data: new ThemeData( accentColor: Colors.yellow, ), child: new FloatingActionButton( onPressed: () {}, child: new Icon(Icons.add), ),);

有时MaterialApp设定的统一风格,并不能满足某个Widget的要求,可能还需要有其他的外观变化,可以通过Theme.of传入当前的BuildContext,来对theme进行扩展。

Flutter会根据传入的context,顺着Widget树查找最近的Theme,并对Theme复制一份防止影响原有的Theme,并对其进行扩展。

new Theme( data: Theme.of(context).copyWith(accentColor: Colors.yellow), child: new FloatingActionButton( onPressed: null, child: new Icon(Icons.add), ),);

网络请求

Flutter中可以通过async、await组合使用,进行网络请求。Flutter中的网络请求大体有三种:

本文由10bet发布于Web前端,转载请注明出处:Flutter介绍

关键词:

上一篇:没有了

下一篇:代码审查问题手册

最火资讯