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.參考資料 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM