Flutter基础系列之入门(一)

1.Flutter是什么?

官方介绍:Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。

从官方介绍可以看到,Flutter有如下特点:

  • 跨平台:现在Flutter至少可以跨4种平台,甚至支持嵌入式开发。我们常用的有Linux、Android、IOS,甚至可以在谷歌最新的操作系统上Fuchsia进行运行,经过第三方扩展,甚至可以跑在MacOS和Windows上,到目前为止,Flutter算是支持平台最多的框架了,良好的跨平台性,直接带来的好处就是减少开发成本。
  • 原生用户界面: 它是原生的,让我们的体验更好,性能更好。用官方的话讲就是平滑而自然的滑动效果和平台感知,为您的用户带来全新的体验。
  • 开源免费:我们只要学会并使用,这些都是免费的。

现在越来越多的公司加入了Flutter阵容:阿里巴巴、京东、腾讯、头条....

这里,我们通过官方的一张图来看一下Flutter的架构:

  • Flutter Framework:这是一个纯 Dart实现的 SDK,它实现了一套基础库,自底向上, 用于处理动画、绘图和手势等。并且基于绘图封装了一套 UI组件库,细分为两种风格的组件(Material和Cupertino)。
    1. 底下两层(Foundation和Animation、Painting、Gestures)在Google的一些视频中被合并为一个dart UI层,对应的是Flutter中的dart:ui包,它是Flutter引擎暴露的底层UI库,提供动画、手势及绘制能力。
    2. Rendering层,这一层是一个抽象的布局层,它依赖于dart UI层,Rendering层会构建一个UI树,当UI树有变化时,会计算出有变化的部分,然后更新UI树,最终将UI树绘制到屏幕上,这个过程类似于React中的虚拟DOM。Rendering层可以说是Flutter UI框架最核心的部分,它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。
    3. Widgets层是Flutter提供的的一套基础组件库,在基础组件库之上,Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而我们Flutter开发的大多数场景,只是和这两层打交道。
  • Flutter Engine:这是一个纯 C++实现的框架层,包含了 Skia引擎(高性能渲染引擎)、Dart运行环境、文字排版引擎等,在代码调用 dart:ui库时,调用最终会走到Engine层,然后实现真正的绘制逻辑。它可以以 JIT(即时编译)、JIT Snapshot 或者 AOT(预先编译)的模式运行 Dart代码。

更多Flutter的框架介绍,可以参考:https://book.flutterchina.club/chapter1/flutter_intro.html

2.和主流框架的对比

  • Cordova:Cordova基于网页技术进行包装,利用插件的形式开发移动应用,无论是性能还是体验,Flutter都可以完胜Cordova。
  • RN(React Native)/Weex:RN/Weex的效率由于是将View编译成了原生View,所以效率上要比基于Cordova的HTML5高很多,但是它也有效率问题,RN/Weex的渲染机制是基于前端框架的考虑,复杂的UI渲染是需要依赖多个view叠加。比如我们渲染一个复杂的ListView,每一个小的控件,都是一个native的view,然后相互组合叠加。想想此时如果我们的list再需要滑动刷新,会有多少个对象需要渲染。所以也就有了前面所说的RN/Weex的列表方案不友好。
  • Flutter:吸收了前两者的教训之后,在渲染技术上,选择了自己实现(GDI),由于有更好的可控性,使用了新的语言Dart,避免了RN的那种通过桥接器与Javascript通讯导致效率低下的问题,所以在性能方面比RN更高一筹;有经验的开发者可以打开Android手机开发者选项里面的显示边界布局,发现Flutter的布局是一个整体.说明Flutter的渲染没用使用原生控件进行渲染。

【总结】:Flutter采用GPU渲染技术,所以性能极高。Flutter编写的应用可以达到120fps(每秒传输帧数),这也就是说,它完全可以胜任游戏的制作。而我们常说的RN的性能只能达到60fps,这也算是Flutter的一个超高竞争力。官方宣称Flutter甚至会超过原生性能。

经过上面的对比,我们来分析一下Flutter的优劣势。

2.1优势

  • 性能强大,流畅。对比weexreact native,Flutter直接在两个平台上重写了各自的UIKit,对接到平台底层,减少UI层的多层转换,UI性能可以比肩原生。而Weex和RN,都是基于DOM树来渲染原生组件。
  • 灵活、组件库易维护、UI外观保真度和一致性高。由于UI渲染不依赖原生控件,也就不需要根据不同平台的控件单独维护一套组件库,所以代码容易维护。由于组件库是同一套代码、同一个渲染引擎,所以在不同平台,组件显示外观可以做到高保真和高一致性;另外,由于不依赖原生控件,也就不会受原生布局系统的限制,这样布局系统会非常灵活。

2.2不足

  • 不支持热更新。Weex和RN支持动态下发JS来支持热更新。
  • 对原生的基本技能要求。虽然Flutter是跨平台的,但大部分我们的开发,是需要集成到原生中的。全新的Flutter,也需要有原生的基础知识。
  • 开发思维的变换。原生开发中,我们主要基于继承的思路来进行视图的开发,比如说继承UIView,重写UIView的某个生命周期函数,再添加一些方法和属性,来完成一个自定义的View。在Flutter中,我们不能在它的生命周期中修改属性,二是需要嵌套组合几种Widget,例如RowContainerListViewWidget
  • 资源的添加和使用,相对来说比较麻烦(现在可以添加文件夹了:https://flutter.dev/docs/development/ui/assets-and-images)。
  • Apple生态圈。
  • 安装包大小(iOS大了12M左右;安卓大了5M左右)。

3.Flutter生态

4.认识Flutter

环境搭建参考 Flutter安装

4.1创建Flutter工程

方式一:使用命令。

flutter create myapp   //创建Flutter工程
flutter devices  //查看运行的设备
flutter run  //运行应用程序
flutter build ios --debug //iOS debug模式打包
flutter build ios --release //iOS release模式打包(release包不能在模拟器上正常运行)
flutter channel //查看当前channel
flutter channel beta //切换到beta channel
flutter upgrade //升级

方式二:使用Visual Studio Code/Android Studio。这里使用Android Studio进行示例。

打开Android Studio,选择“Start a new Flutter project”:

接下来会进入一个工程类型选择页面:

  • Flutter Application:标准的Flutter App工程,包含标准的Dart层与Native平台层;
  • Flutter Module:Flutter组件工程,仅包含Dart层实现,Native平台层子工程为通过Flutter自动生成的隐藏工程;
  • Flutter Plugin:Flutter平台插件工程,包含Dart层与Native平台层的实现;
  • Flutter Package:Flutter纯Dart插件工程,仅包含Dart层的实现,往往定义一些公共Widget。

 这里选择“Flutter Application”,继续下一步:

后面直接Finish就可完成工程的创建。创建出来的工程结构如下:

4.2示例代码解析

打开main.dart,我们对这里的代码做一个分析:

//导入Material UI组件库,Material是一种标准的移动端和web端的视觉设计语言
import 'package:flutter/material.dart';
//应用入口
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  //build()方法用来描述如何构建UI界面
  @override
  Widget build(BuildContext context) {
    //MaterialApp 是Material库中提供的Flutter APP框架,通过它可以设置应用的名称、主题、语言、首页及路由列表等
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

//MyHomePage 是应用的首页,它继承自StatefulWidget类,表示它是一个有状态的widget
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    //Scaffold 是 Material库中提供的一个widget, 它提供了默认的导航栏、标题和包含主屏幕widget树的body属性
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
    );
  }
}

使用“flutter run”命令运行之后,会有一段文字提示:

To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
An Observatory debugger and profiler on iPhone XR is available at: http://127.0.0.1:53619/
For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".

从上面可以看到,我们可以使用快捷键进行几个操作:

  • r 键:点击后热加载,也就是重新加载。
  • p 键:显示网格,这个可以很好的掌握布局情况,工作中很有用。
  • o 键:切换android和ios的预览模式。
  • q 键:退出调试预览模式。

下面我们从几个核心概念,对示例代码做一个解析。

4.2.1程序入口

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

上面的代码是Dart语法中特有的简写形式,展开等价于如下代码:

void main() {
  return runApp(MyApp());
}

上面的runApp函数,是Flutter框架的入口,可以将给定的组件(widget)显示在屏幕上。

这里,大家可能会问一个问题?如果在main函数中,不使用runApp函数,会怎么样?

不使用runApp函数的话,程序仍会正常运行,但屏幕上什么都不会显示,相当于就是一个Dart控制台程序。

4.2.2StatelessWidget和StatefulWidget

从上面的示例代码可以看到,类MyApp继承自StatelessWidget,类MyHomePage继承自StatefulWidget。

class MyApp extends StatelessWidget {
  //...
}

class MyHomePage extends StatefulWidget {
  //...
}

class _MyHomePageState extends State<MyHomePage> {
  //...
}

那么StatelessWidget和StatefulWidget是什么呢?

  • StatefulWidget:有状态组件,持有的状态可能在Widget生命周期中发生变化。例如 CheckboxRadioSliderInkWellForm, and TextField等;
  • StatelessWidget:无状态组件,是不可变的,它的属性不能改变,所有的值都是最终的。 例如Icon、 IconButton, 和Text等。

查看继承关系,这两者都继承自widget类。StatelessWidget由于不需要维护状态的场景,它通常在build方法中通过嵌套其它Widget来构建UI,在构建过程中会递归的构建其嵌套的Widget。

从上面的代码可以看到,实现一个StatefulWidget,至少需要两个类:

  • 一个是StatefulWidget类,这个类里有一个方法createState(),这个方法直接调用了State类,用于创建和Statefulwidget相关的状态。
  • 一个State类,StatefulWidget类本身是不变的,但是State类在Widget生命周期中始终存在,用于更新StatefulWidget的状态。

State类的各个生命周期函数,对于我们使用StatefulWidget很重要,这里介绍一个各个生命周期的作用,具体大家可以写一个StatefulWidget,打印日志,来详细查看调用时机。

  • initState:当Widget第一次插入到Widget树时会被调用,对于每一个State对象,Flutter framework只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。
  • didChangeDependencies():当State对象的依赖发生变化时会被调用;例如:在之前build() 中包含了一个InheritedWidget,然后在之后的build() 中InheritedWidget发生了变化,那么此时InheritedWidget的子widget的didChangeDependencies()回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时,Flutter framework会通知widget调用此回调。
  • build():主要是用于构建Widget子树的,会在如下场景被调用:
    1. 在调用initState()之后。
    2. 在调用didUpdateWidget()之后。
    3. 在调用setState()之后。
    4. 在调用didChangeDependencies()之后。
    5. 在State对象从树中一个位置移除后(会调用deactivate)又重新插入到树的其它位置之后。
  • reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用,此回调在Release模式下永远不会被调用。
  • didUpdateWidget():在widget重新构建时,Flutter framework会调用Widget.canUpdate来检测Widget树中同一位置的新旧节点,然后决定是否需要更新,如果Widget.canUpdate返回true则会调用此回调。
  • deactivate():当State对象从树中被移除时,会调用此回调。在一些场景下,Flutter framework会将State对象重新插到树中,如包含此State对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。
  • dispose():当State对象从树中被永久移除时调用;通常在此回调中释放资源。

 实际开发中,我们主要是在initState和build方法中做一些事情。

从上面也可以看到,使用StatefulWidget相对来说,比较复杂,那么有没有其他方式来管理状态呢?这里先留一个悬念,后面在实战系列里做介绍。

4.2.3MaterialApp

在MyApp的build方法中,直接返回了一个MyApp对象:

 Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }

这个MaterialApp是什么呢?

它属于flutter/material.dart包,因此基本上每个flutter程序的代码,第一行代码都会引入这个包。这个包是 Flutter 实现 Material Design设计风格(谷歌推出的一套视觉设计语言)的基础包, 里面有文本输入框( Text)、图标( Icon)、图 片( Image)、行排列布局( Row)、 列排列布局( Column)、 Decoration (装饰器)、动画等组件。

MaterialApp代表使用Material Design设计风格的应用,里面包含了其所需要的基本控件。 一个完整的 Flutter项目就是从 Materia!App这个主组件开始的。该类的常见属性如下所示:

这里先介绍代码中出现的两个重要的属性。

Theme(主题)

为了在整个应用中使用同一套颜色和字体样式,可以使用“主题”这种方式 。 定义主题有两种方式 : 使用全局主题或使用 Theme来定义应用程序局部的颜色和字体样式。 

上面示例代码中,是直接初始化了一个ThemeData对象,并配置了primarySwatch属性。实际上,ThemeData具有的属性很多:

home(主页)

home用于设置应用的主页,也就是整个应用的主组件。这里设置为MyHomePage

4.2.4Scaffold(脚手架组件)

Scaffold 实现了基本的 Material Dsign布局。 只要是在 Material Design中定义过的单个界面显示的布局组件元素,都可以使用 Scaffold 来绘制。 上面实例中,MyHomePage对应状态类的build中,就是用Scaffold来进行页面的布局的:

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
    );
  }

Scaffold的常见属性如下:

4.3配置文件

在我们实际开发过程中,经常会使用到一些第三方库等,在iOS中,我们通过使用CocoaPods来进行第三方库的管理;安卓中,一般使用Gradle。Flutter使用pubspec.yaml来配置第三方文件的使用,这样可以使得我们快速的应用第三方提供的功能,而不用重复造轮子。

【提醒】:Dart的包仓库地址(https://pub.dartlang.org/)

怎么使用第三方的包呢?

第一步:添加需要的包到pubspec.yaml文件,如下图所示:

第二步:运行指令 “flutter packages get”指令,获取所需要的库到工程中。

第三步:使用。

5.延伸:现有工程怎么集成Flutter?

怎么将Flutter集成到现有App中呢?这个Flutter官方提供了一个解决方案:https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps

这种方式的缺点是,过于依赖本地环境和侵入Native工程,这样会影响到Native的开发,那么该怎么改进呢?这里可以思考两种方案,不过本人还没有在具体的工程中进行实践。

方案一:闲鱼的 FlutterBoost

方案二:组件化架构方案

6.怎么学习?

【基础篇】:

【实战篇】:

【公司篇】:

7.参考资料 

 

posted @ 2019-05-05 10:06  LeeGof  阅读(...)  评论(...编辑  收藏