Google推出Flutter已有一年多了,闲鱼作为Flutter的深度应用者,让国内很多开发者看到了Flutter的美好前景。
传统开发方式,同一个APP,需要Android和iOS两个团队来开发。人力、沟通、管理成本都是很昂贵得。Flutter跨Android,iOS两个平台,可以提高成员利用率,降低人力成本,也能有效缩短产品迭代周期,可谓是一举多得。
很多前端(H5)同学对客户端开发不甚了解,甚至一无所知,导致工作中配合得时候经常出现问题。 但是有了Flutter之后,机会就来了。借助Flutter,前端开发者可以更好得体会APP得开发流程,契机涉足客户端开发。
之所以说是个机会,原因有以下几点:
- Flutter推出不到2年,市场还没有大规模应用,此时跟随学习,压力不会太大
- 优先掌握这门技术,时间上是个优势
- Flutter使用Dart来开发,都会Javascript了,Dart就不难了
- Google大力推广,未来一定大有可为,Flutter也支持Web,git上的star已经7万了。
今天就从前端开发者的角度,对比前端和Flutter的不同,来认识下Flutter。
1、页面结构
前端:用一系列的HTML标签来实现
Flutter:用一系列Widget来实现,Widget可以理解为组件(在Flutter中,一切皆是Widget)
HTML和Flutter都定义了一系列的元素,来表示容器、布局、基本信息,参考下表(只列出部分展示,更多请查阅官网)
| ' ' | HTML | Flutter |
|---|---|---|
| 基本元素/组件 | p,img,H1-H6,a,span,i,input,button, | Text,TextStyle,Image,Icon,RaiseButton,Switch,Checkbox,TextField |
| 布局 | div,table,tbody,tfoot,ul,ol,li | Row,Column,Flex,Wrap,Flow,Stack,Positioned,Align |
| 容器 | html,head,body,header,footer,section,article,nav | Container,Padding |
Flutter中,Widget的名称是严格区分大小写的,如果写错了,IDE会报错提醒,而在HTML中,是不严格区分,大小写都可以,通常大家都用小写。
Flutter中容器通常接受一个子Widget(child),布局通常接受一个Widget数组(children),HTML中没有这样的规则约束。
看一个代码片段:
Container(
height: 56.0,
padding: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: BoxDecoration(
color: Colors.red[500],
),
child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null,
),
Expanded(
child: title,
),
IconButton(
icon: Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
);
从上面的代码可以看出,在Flutter中,通过child或children来实现层级嵌套。
查看源码,可以发现所有的Widget,其实都是一个类(见下图)。此刻你肯定明白了,为啥Widget是大写字母开头的。
下面这段代码是Container的源码
看了上面给出的两段代码,有没有种似曾相识的感觉?我们先来看一个JS中的类和实例化的例子
function Handle(){
this.type = type;
this.area = area;
}
var test = new Handle(
type ='j',
area ='wh'
);
现在来对比JS和Flutter
JS中先定义类,然后传参实例化,Flutter中,同样是先定义类,然后传参实例化。除了小部分书写格式不一样,二者竟然惊人的相似。由于所有Widget都是类,所以你可以认为,Flutter中,就是不断的把类实例化,然后构建出我们的页面。对于前端同学来说,这样对比,足以把Flutter的面纱撕下来了。
2、样式
前端:
- 行内style:与html标签绑定在一起
- style标签内写css
- 独立的css文件,less,sass、postcss
- 样式和html元素通过选择器进行关联
Flutter:
前端中我们一致推荐css和html分离,但是在flutter中,无法做到这一点。
前面我们说到,Flutter中一切都是Widget,所以它的样式也通过属性的方式体现出来。每个Widget的属性是事先规定好的(Widget到底支持哪些样式属性昵,这个不用死记硬背,忘记了就看源码就好了),只能给他设置指定的样式。看下面的例子
Container(
margin: EdgeInsets.only(top: 8),
child: Text(
label,
style: TextStyle(
color: color,
fontSize: 12,
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.center,
),
),
给Container设置了margin样式,
给Text设置了style(这个style和html中的不一样),textAlign样式,
Container没有style这个属性,如果我们强行设置,IDE会报错提醒,见下图
既然属性和Widget是强绑定关系,哪我们的样式怎么复用昵?
可以在公共文件中定义样式(比如颜色,字号等)。也可以申明一个变量,用样式Widget给他赋值,如下所示:
3、事件
前端中,大部分标签都支持html事件属性,但是Flutter中,事件并不是一个大多数Widget都具有的属性,比如Container和Text就没有事件属性,对应到html中,div和p是有事件属性的。
我们可以查看源码,看Widget是否有事件属性。
支持事件的Widget,属性中会出现on+事件名称的属性,如下面的代码
RaisedButton(
onPressed: _increment,
child: Text('Increment'),
),
对于不支持事件属性的Widget,Flutter提供了两种方式来设置事件
- GestureDetector--手势识别
- Listener--监听原始触摸事件 上面说到的两种事件,同样是Widget,使用的时候,将他们作为父元素使用。
看下面的例子,用GestureDetector,实现点击Container的时候,响应onTapDown,onTapUp,onTap,onTapCancel事件。
GestureDetector(
onTapDown: _handleTapDown,
onTapUp: _handleTapUp,
onTap: _handleTap,
onTapCancel: _handleTapCancel,
child: Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: Colors.lightGreen[700],
border: Border.all(
color: Colors.teal[700],
width:10.0,
)
),
),
);
再看一个Listener的例子,实现同样的功能
Listener(
onPointerUp: _handlePointerUp,
onPointerDown:_handlePointerDown,
onPointerMove: _handlePointerMove,
onPointerCancel:_handlePointerCancel,
child: Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: Colors.lightGreen[700],
border: Border.all(
color: Colors.teal[700],
width:10.0,
)
),
),
),
两者用法基本一样,但是GestureDetector是基于Listener做的封装。
4、状态
在业务开发中,状态是一个必不可少概念。前端有Vuex、Redux,Flutter有Mobx、Redux、ScopedModel...
这里不对三方库来做介绍,说一下Flutter中最基础的:StatefulWidget,StatelessWidget。
- StatefulWidget:有状态的Widget--用户交互或数据改变会导致Widget改变
- StatelessWidget:无状态的Widget--一个Widget是最终态的或不可改变的
有、无状态的区别,可以简单理解为,是否产生数据变化,发生数据变化,这个Widget就是StatefulWidget类型,没有数据变化,就是StatelessWidget类型。 查看源码的时候,你会发现,大部分Widget都继承自StatelessWidget或StatefulWidget。
看下面的源码
StatelessWidget :
class Text extends StatelessWidget {
/// Creates a text widget.
///
/// If the [style] argument is null, the text will use the style from the
/// closest enclosing [DefaultTextStyle].
///
/// The [data] parameter must not be null.
const Text(
this.data, {
Key key,
this.style,
......
StatefulWidget :
class Form extends StatefulWidget {
/// Creates a container for form fields.
/// The [child] argument must not be null.
const Form({
Key key,
@required this.child,
this.autovalidate = false,
......
看一个简单的例子-计数器,结合StatelessWidget和StatefulWidget来实现
效果图:
import 'package:flutter/material.dart';
//CounterDisplay-创建文本
class CounterDisplay extends StatelessWidget {
CounterDisplay({this.count});
final int count;
@override
Widget build(BuildContext context) {
return Text('Count:$count',style: TextStyle(fontSize: 30),);
}
}
//CounterIncrementor -创建按钮
class CounterIncrementor extends StatelessWidget {
CounterIncrementor({this.onPressed});
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text('Increment'),
onPressed: onPressed,
);
}
}
//Counter- 计数器,数字是变化的
class Counter extends StatefulWidget {
@override
_CounterState createState()=> _CounterState();
}
class _CounterState extends State<Counter>{
int _counter = 0;
void _increment(){
setState(() {
++_counter;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CounterDisplay(count: _counter),
SizedBox(width: 20.0),
CounterIncrementor(onPressed: _increment),
],
),
);
}
}
精简一下代码,看看最核心的代码结构
StatelessWidget:
class WidgetName StatelessWidget {
......
@override
Widget build(BuildContext context) {
return ......
}
}
StatefulWidget:
class WidgetName extends StatefulWidget {
@override
_WidgetName createState()=> _WidgetName ();
}
class _WidgetName extends State<WidgetName >{
//在事件回调会函数内部,调用setState方法
setState(() {
......
});
@override
Widget build(BuildContext context) {
return ......
}
}
到这里,我们介绍了,Flutter的页面结构、样式、事件、状态。这是Flutter的入门内容,经过这样对比,降低前端同学对Flutter的认知难度,加快学习的步伐。
要想熟练应用Flutter,还需要大量的coding。
这里有本人学习时,留存的代码,今后也会不断补充,有需要可以直接下载,
github地址:
这里有一个我学习的脑图笔记,帮助梳理知识点