前端入门Flutter--对比前端和Flutter,降低学习Flutter的门槛

1,924 阅读7分钟

Google推出Flutter已有一年多了,闲鱼作为Flutter的深度应用者,让国内很多开发者看到了Flutter的美好前景。

传统开发方式,同一个APP,需要Android和iOS两个团队来开发。人力、沟通、管理成本都是很昂贵得。Flutter跨Android,iOS两个平台,可以提高成员利用率,降低人力成本,也能有效缩短产品迭代周期,可谓是一举多得。

很多前端(H5)同学对客户端开发不甚了解,甚至一无所知,导致工作中配合得时候经常出现问题。 但是有了Flutter之后,机会就来了。借助Flutter,前端开发者可以更好得体会APP得开发流程,契机涉足客户端开发。

之所以说是个机会,原因有以下几点:

  1. Flutter推出不到2年,市场还没有大规模应用,此时跟随学习,压力不会太大
  2. 优先掌握这门技术,时间上是个优势
  3. Flutter使用Dart来开发,都会Javascript了,Dart就不难了
  4. 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的源码

1

看了上面给出的两段代码,有没有种似曾相识的感觉?我们先来看一个JS中的类和实例化的例子

function Handle(){
    this.type = type;
    this.area = area;
}

var test = new Handle(
    type ='j',
    area ='wh'
);

现在来对比JS和Flutter

2

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会报错提醒,见下图

3

既然属性和Widget是强绑定关系,哪我们的样式怎么复用昵?

可以在公共文件中定义样式(比如颜色,字号等)。也可以申明一个变量,用样式Widget给他赋值,如下所示:

4

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来实现

效果图:

5

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地址:

github.com/INKGROUP/fl…

这里有一个我学习的脑图笔记,帮助梳理知识点

6