要说2018年最火的跨端技术,当属于 Flutter
莫属,应该没人质疑吧。一个新的技术的趋势,最明显的特征,就是它必定想把“前浪”拍死在沙滩上。这个前浪,就是"react Native","weex"。目前随便在搜索引擎上 搜索"Flutter reactNative",就全是这两个技术的对比,评测。javascript
是的,错过了react Native
, weex
这些 “炸” 翻前端的技术,不能在错过 Flutter
了,这年头,你不会一门,跨端技术,怎么好意思说本身是【前端】。css
Flutter是谷歌的移动UI框架,能够快速在iOS和Android上构建高质量的原生用户界面。 Flutter能够与现有的代码一块儿工做。在全世界... ... html
话说隔壁师兄,“闲鱼” 是最先一批与谷歌展开合做,并在重要的商品详情页中使用flutter技术上线的BU。一路走来,积累了大量的开发经验。“闲鱼” flutter 相关文章的都颇有深度,足见功力。前端
Flutter
的web前端同窗来讲仍是好了,回到标题,笔者做为一名传统 web前端,想从前端最熟悉的视角 “躺” 着把 Flutter
了解一遍,不要敬仰,平视它!!!先从兴趣开始。java
Flutter 环境的搭建,其实有不少资源能够参考。这里就不累述了(知道有不少坑,在后续文章中,有机会把我的遇到的坑汇总一下 )。react
有兴趣能够参考 flutter安装环境的搭建 , 在这里建议各位,必定要本身亲自搭一下环境,跑一下官方demo, 小马过河,焉知深浅,本身定的位才是最准确的。git
有了环境,先配置编辑器。github
而后 建立一个Flutter项目。web
项目建立完成,能够先用 flutter run 跑一下。segmentfault
flutter run
复制代码
好了,跑起来了吧,你会看到一个计数的官方示例,点击加号图片能够作加运算。
这时候咱们看项目的project 目录里 有一个入口文件叫 main.dart。而后打开 main.dart 就像下面这样:
( 为何大家看到代码比个人长,由于我折叠了!!! ) 这不是重点,重点是每一个类继承的都是一个尾号为Widget
的字符。
聪明的你必定会以为 Widget
和 Flutter
有着某种神秘的联系。
“Binggo!",是的,Flutter
有两个重型武器,一个叫 Dart
,另外一个就是 Widget
了。
Dart
一切皆来自 Object
, Flutter
的组件皆来自 Widget
。关于强大的Dart,今天暂且不表,后面有时间能够独立篇幅来聊聊Dart。
先祭出一张 Flutter
的架构老图。
在 Flutter
的世界里,包括views,view controllers,layouts等在内的概念都创建在Widget之上。
Widget
是 Flutter
组件的抽象描述。因此掌握Flutter的基础就是学会使用 Widget
开始。
在Flutter界面渲染过程分为三个阶段:布局、绘制、合成,布局和绘制在Flutter框架中完成,合成则交由引擎负责:
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding,PaintingBinding, RendererBinding, WidgetsBinding { ... }
abstract class Widget extends DiagnosticableTree { ... }
abstract class StatelessWidget extends Widget { ... }
abstract class StatefulWidget extends Widget { ... }
abstract class RenderObjectWidget extends Widget { ... }
abstract class Element extends DiagnosticableTree implements BuildContext { ... }
abstract class RenderObjectElement extends Element { ... }
class StatelessElement extends ComponentElement { ... }
class StatefulElement extends ComponentElement { ... }
复制代码
上面这些类的主要做用以下:
Widget = 样式(css) + 标记语义(标签) + 组件化(官方) + 数据绑定(props)
“什么?这不就是 react 吗?"
对, React 的概念和
Flutter
的Widget
是有相通性的。
“既然有react的概念,难道还有state,setState吗?“
又对, Flutter 还真有 相似 state 状态机制的概念,并且也确实有 setState 的方法。
“dome里有两个基类,StatelessWidget 和 StatefulWidget 是作什么用的?”
StatelessWidget 和 StatefulWidget,这里两个类特别重要,几乎全部的组件都是基于他们建立的。
StatelessWidget 是状态不可变的widget,称为
无状态widget
。初始状态设置之后就不可再变化。若是须要变化须要从新建立。
StatefulWidget 能够保存本身的状态,称为
有状态widget
。Flutter
首先保存了初始化时建立的State,状态是经过改变State,来从新构建Widget
树来进行UI变化。改变状态的方法,就是咱们用的最多的神器"setState",而单纯改变数据是不会引起UI改变的,这个概念和咱们的 React 同样同样的。
Flutter
能够不用记忆这么多组件基类,只用记住如下式子就能够, 不夸张的说,熟悉这个式子就能够开发 Flutter 项目了:为何不称为 “模版”,“标签”,“element" ,而叫"标记语义",是由于flutter的 widget 结构并不仅是 “模版”,“标签”,“element"。widget 描述结构更像是 React 的虚拟dom阶段,浓缩了相关上下文,以对象化的结构展现。
咱们先来看看,React 建立出来的虚拟dom结构( 伪代码 ):
var newTree = el('div', {'id': 'container'}, [
el('h1', {style: 'color: red'}, ['simple virtal dom']),
el('p', ['Hello, virtual-dom']),
el('ul', [el('li'), el('li')])
])
复制代码
再来看看,flutter 用Dart 建立的 widget 代码结构:
Widget build(BuildContext context) {
return new Column(
children: <Widget>[ new Container( padding: new EdgeInsets.only(top:100.0), child: new Text('这是一个组件') ), new Container( decoration: new BoxDecoration(border: new Border.all(width:1.0,color: Colors.blue)), padding: new EdgeInsets.all(20.0), child: new Text('来自输入框:'+active) ) ], ); } 复制代码
是否是这样看就熟悉不少了。
注:不少前端er 会不习惯这中书写方式,目前有开发者在社区推进,在编译前,使用jsx标签,编译后再解析成标记树,好比这个DSX设计的提案。
虽然jsx->标记树 还只是提案,但其实能够帮助咱们更容易理解,此提案想表达的样式像这样:
class MyScaffold extends StatelessWidget {
build(context) {
return <Material>
<Column>
<MyAppBar
title={<Text
text='Example title'
style={Theme.of(context).primaryTextTheme.title},
/>}
/>
<Expanded>
<Center>
<Text text='Hello, world!'/>
</Center>
</Expanded>
</Column>
</Material>;
}
}
复制代码
上面这段 jsx 要是用 Dart 来写是什么样的?以下:
class MyScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Material(
child: Column(
children: <Widget>[
MyAppBar(
title: Text(
'Example title',
style: Theme.of(context).primaryTextTheme.title,
), // Text
), // MyAppBar
Expanded(
child: Center(
child: Text('Hello, world!'),
), // Center
), // Expanded
], // <Widget>[]
), // Column
); // Material
}
}
复制代码
虽然 Flutter
与 react 这么类比多少有些牵强,但能够我的总结一些方法,方便理解:
Flutter
项目后,自动追加上去的,像 jsx 语言的标签封闭,方便发现标注的起始节点。对于 Flutter
样式的理解,能够查看官方的这篇文档,也是一样用类比的方式,很直观的了解,HTML、css样式和 flutter
之间的联系. 能够参考这里: flutter.io/docs/get-st… flutterchina.club/web-analogs…
笔者摘选其中样例的重点部分,对比展现来讲明( 因为篇幅问题, 父子关系css 结构,用tab方式来表示 ):
左边是css 写法,右边是 flutter-dart写法
.demo1 { background-color: #e0e0e0; width: 320px; height: 240px; font: 900 24px Georgia; letter-spacing: 4px; text-transform: uppercase; }
复制代码复制代码
var demo1 = new Container(
child: new Text(
"Lorem ipsum".toUpperCase(), // 对应 左边的文本转换大小写
style: new TextStyle( // 对应 左边的 font
fontSize: 24.0
fontWeight: FontWeight.w900,
fontFamily: "Georgia",
letterSpacing: 4.0,
),
),
width: 320.0, // 对应 左边的 width
height: 240.0, // 对应 左边的 height
color: Colors.grey[300], // 对应 左边的 background-color
);
复制代码
.demo2 { display: flex; align-items: center; justify-content: center; }
复制代码复制代码
var demo2 = new Container(
child: new Center( // 对应左边的整个 flex 属性
child: new Text("Lorem ipsum")
)
);
);
复制代码
.container{ width:300px .demo3 { width: 100%; max-width: 240px; } }
复制代码复制代码
// 对于嵌套容器,若是父级的宽度小于子级宽度,则子级容器将自行调整大小以匹配父级。
var container = new Container(
child: new Center(
child: new Text("Lorem ipsum"),
decoration: new BoxDecoration( ... ), // constraints属性,建立一个新的BoxConstraints来设置minWidth或maxWidth
width: 240.0, // 对应左边的 max-width
),
width:300.0
),
复制代码
.box { transform: rotate(15deg); }
复制代码复制代码
var container = new Container( // gray box
child: new Transform( // 对应左边的 transform
child: new Container( ... ),
alignment: Alignment.center, // 对应左边的 transform
transform: new Matrix4.identity() // 对应左边的 transform
..rotateZ(15 * 3.1415927 / 180),
),
)
);
复制代码
.box { transform: scale(1.5); } 复制代码复制代码
var container = new Container( // gray box child: new Transform( // 对应左边的 transform child: new Container( ... ), alignment: Alignment.center, // 对应左边的 transform的中心 transform: new Matrix4.identity() // 对应左边的 transform ..scale(1.5), ), ) ); 复制代码复制代码
.greybox {
position: relative;
.redbox {
position: absolute;
top: 24px;
left: 24px;
}
}
复制代码
var container = new Container( // grey box child: new Stack( // 相对跟容器位置 relative children: [ new Positioned( // 相对父容器位置 absolute child: new Container( ... ), left: 24.0, top: 24.0, )], ) ); 复制代码复制代码
.redbox { background: linear-gradient(180deg, #ef5350, rgba(0, 0, 0, 0) 80%); }
复制代码复制代码
var container = new Container( // grey box child: new Center( child: new Container( // red box child: new Text( ... ), decoration: new BoxDecoration( gradient: new LinearGradient( // 对应左边的 background: linear-gradient begin: const Alignment(0.0, -1.0), end: const Alignment(0.0, 0.6), colors: [ const Color(0xffef5350), const Color(0x00ef5350) ], ), ) ), ) ); 复制代码复制代码
.box { border-radius: 8px; } 复制代码复制代码
var container = new Container( // grey box child: new Center( child: new Container( // red circle child: new Text( ... ), decoration: new BoxDecoration( borderRadius: new BorderRadius.all( const Radius.circular(8.0), ), ) ), ) ); 复制代码复制代码
.box { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.8), 0 6px 20px rgba(0, 0, 0, 0.5); } 复制代码复制代码
var container = new Container( // grey box child: new Center( child: new Container( // red box child: new Text( ... ), decoration: new BoxDecoration( boxShadow: [ // 对应左边的 box-shadow new BoxShadow ( color: const Color(0xcc000000), offset: new Offset(0.0, 2.0), blurRadius: 4.0, ), new BoxShadow ( color: const Color(0x80000000), offset: new Offset(0.0, 6.0), blurRadius: 20.0, ), ], ) ), ) ); 复制代码复制代码
.circle { text-align: center; width: 160px; height: 160px; border-radius: 50%; } 复制代码复制代码
var container = new Container( // grey box child: new Center( child: new Container( // red circle child: new Text( ... ), decoration: new BoxDecoration( color: Colors.red[400], shape: BoxShape.circle, // 画圆和圆角不太同样,用的是BoxShape绘制图像能力 ), width: 160.0, height: 160.0, ), ) ); 复制代码复制代码
// css 的内联结构 .greybox { font: 900 24px Roboto; .redbox { em { font: 300 48px Roboto; font-style: italic; } } } 复制代码复制代码
var container = new Container( // grey box child: new Center( child: new Container( // red box child: new RichText( text: new TextSpan( style: bold24Roboto, children: [ new TextSpan(text: "Lorem "), // 继承内联样式 new TextSpan( text: "ipsum", style: new TextStyle( // 具备自定义样式的单独样式 fontWeight: FontWeight.w300, fontStyle: FontStyle.italic, fontSize: 48.0, ), ), ], ), ), ), ) ); 复制代码复制代码
官方的widgets目录
点击每个card后,里面还有子card, 以一个展开的纬度来看,是这样的:
这真是一个庞大的组件系统,这不是社区提供的,而是官方的。 flutter 团队事无巨细的实现了目前市面上基本上能见到的组件方式和类型。 我的认为这样作优缺点并存的。 先说缺点:
1.学习成本增长,曲线也仍是比较陡峭的。
2.组件直接的继承关系路径比较零乱,好比 继承自 Widget 是这些大类道还清晰: PreferredSizeWidget ProxyWidget RenderObjectWidget StatefulWidget StatelessWidget。 可是,StatelessWidget和StatefulWidget的子类就过于平行化了,名称上晦涩,没有抽象架构化,分层或者塔型级别。
StatelessWidget:
![]()
StatefulWidget
![]()
Flutter
官方就不建议使用第三方组件,这个也未有定论。再说优势:
1.能够阻止之后轮子泛滥,在团队僵持不下使用哪一个轮子库时,最好的理由就是“官方”二字,由于使用官方,能够弱化和规避一些问题,好比: 版本迭代不一样步,性能瓶颈,规范不统一等问题,也能快速支持官方的辅助工具。
2.正是因为官方的标准划一,为自动化编译,自动化搭建,测试调优,可视化带来便利,甚至为Ai前端模型化业务场景,提供支撑,都说前端的组件像搭乐高
,Flutter
就是颗粒标准统一的乐高
。
3.天下之势,分久必合,合久必分。
前端在经历了 flash
一统pc页面的富媒体时代,后被乔布斯和H5瓦解;
以后又有H5
的繁盛和框架、语法、构建模式的乱战;
再到有“别再更新,老子学不动"的呼声下,但愿有个一统江湖的跨平台系统出现;
让开发者更专一于,提升业务内容,创造 “新, 酷,炫” 的展示形势上下功夫。
也许,真有一个前端江湖的王者的诞生。
实际的 Flutter Widget
要复杂的更多的多,在眼花缭乱的 Widget
组件中,笔者想用本身的一些理解,去粗取精,来逐步理解 Flutter
这个新家伙 ,文中有理解不到位的地方,欢迎你们指正。 笔者团队也正在开发一套《Flutter GO》的APP,帮助你们熟悉复杂的 Flutter Widget
。
更多学习 Flutter的小伙伴,欢迎入QQ群 Flutter Go :679476515
《Flutter GO》项目地址 alibaba/flutter-go