【Flutter 基礎】Hello World

林小帥發表於2021-07-06

注:本文從個人公眾號(島前嶼端)中遷移重新發布

Flutter 是谷歌的移動 UI 框架,可以從單個程式碼庫快速的為移動端(iOS & Android)、Web、桌面端、嵌入式裝置上構建高質量的原生使用者介面和應用程式。


在說完 環境及配置開發環境/建立虛擬機器 之後那我們就可以來看看它的真正面目,是時候揭開它的神祕面紗了。

建立 flutter 專案

不知是否還記得之前的建立 flutter 專案的命令?

flutter create myapp
複製程式碼

這裡我們建立一個 名叫 myappflutter 專案。(別忘了要先進入你的工作資料夾)

image.png
(建立 flutter 專案 myapp)

稍等一下,等待專案建立完成……

這裡建立專案會被分為 3部分執行:

  1. create 部分 建立所需的檔案結構、檔案、程式碼以及編輯器所需配置資訊。(66個檔案)
  2. 檔案部分建立完成後會執行 flutter pub get 命令來更新所需依賴。
  3. 依賴更新完成後,會執行檢查開發環境,檢查完成後提示 cd myapp & flutter run 來執行你的應用。

image.png
(建立 flutter 專案完成)

專案結構

Ok,建立完成後,就簡單說一下專案結構吧。

image.png
(flutter 專案結構)

資料夾裡更具體的內容就需要自己探索了,我就不細說了。如果要全部說一遍那就太長了。

對了,部分程式碼裡會有 Do not edit. 的註釋,這就需要自己發現這個彩蛋了。

做為 flutter 開發還是要關注重點的,那就是 lib 資料夾。這裡是主要的編碼目錄,我們編寫的程式碼也是放在這個目錄下。該目錄會有預設的一個入口檔案 ,main.dart 通過這個檔案字尾就可以知道 flutter 主要是使用 dart 語言來進行編寫。

wecom1.gif
(myapp 的預設 main.dart)

當你一開啟這個 main.dart 檔案就會感覺 “哎喲,這麼長這麼多,看不懂啊!”

那我就先把這些註釋刪除……

image.png
(刪除註釋的 main.dart)

這還是好長啊……

沒關係,如果你之前看過 flutter 中文網的話,程式碼編寫部分會有讓你刪除 main.dart 的內容。然後複製教程提供的程式碼執行得到 hello world。

從 0 開始

這時你會想,這就完事了?

當然沒完!

我是來學習的,不能複製貼上一把梭就敷衍了事了。

注意:這裡以我的為例,以後均以此配置和環境為基礎。

作業系統: Window 10
編輯器: Visual Studio Code
外掛: Flutter(自帶安裝Dart SDK),yaml
選裝外掛: Java or C/C++
工具: Flutter 依賴下面這些命令列工具
Git for Windows (Git命令列工具) 裝置: 虛擬機器 or 真機
專案型別: Flutter
專案名稱: myapp
程式語言: Dart

main

既然是要學習,那就要拿出學習的精神來,從 0 開始!

所以…… 我們還是把程式碼刪了吧。

image.png

大俠且慢,別打臉!且聽我說……

因為預設的例子和官網教程雖然是可以執行了,但是我覺得還是不夠詳細。所以既然是要學習的話,為什麼不從 0 開始學習,一點一點來把他搞懂來呢?對吧?

現在開始進入正題,程式碼都清空了之後怎麼辦?

不要著急清空程式碼先,新建檔案 lib\main1.dart 把官網的程式碼複製過去再清空,這樣一來有個參照理解對吧?

就像這樣。

image.png
(複製參照程式碼)

先說第一行,有點基礎的同學們都知道這是引入一個東西,但具體是什麼東西呢?我就要來解釋一下啦:

// 引入 Material Design 設計語言(基於 Dart 的 flutter 版本)
import 'package:flutter/material.dart';
複製程式碼

解釋:

Material Design 設計語言 (基於 Dart 的 flutter 版本),Material Design 是由 Google 推出的全新的設計語言。谷歌表示,這種設計語言旨在為手機、平板電腦、桌上型電腦和“其他平臺”提供更一致、更廣泛的“外觀和感覺”。其他人怎麼翻譯我不太清楚,但是我認為中文可以翻譯為 ** “質感設計”** 。

(這麼長記不住怎麼辦?!)

記不住沒關係,這時候我們就要善用 VS Code 或其他編輯器工具的提示功能了。只要輸入如下內容就會有提示了,這時候只要選第一行就Ok啦!

import 'pfm';
複製程式碼

image.png
(引入包提示)

接下來會看到這一句:

// 每個應用都需要有個頂級的 main() 入口函式才能執行。
// main() 函式的返回值為 void
void main() => runApp(new MyApp());
複製程式碼

main 是 dart 的頂級函式,也是入口。

對於不太懂的少俠們可以看看 [Dart 中文網]:dart.goodev.org/

箭頭函式?這個箭頭函式和 JavaScript 的箭頭函式不太一樣。在還沒深入理解Dart語法的時候我們儘量不要使用較為高階的用法。還原如下:

void main() {
  runApp(new MyApp());
}
複製程式碼

這樣看起來是不是就比較熟悉了?

這裡還有呼叫了一個 runApp 的函式,這是什麼東西?

runApp

runApp 是 Flutter 的入口函式,所以如果要使用的話那就必須呼叫 runApp 才能啟動 flutter 專案,不然的話就會報錯了。

image.png
(runApp 提示)

這裡程式碼提示還告訴我們 runApp 函式僅接受一個叫 Widget 的引數,這個又是什麼東西?

Flutter 中文網 - Widget 框架概述

Flutter Widget 採用現代響應式框架構建,這是從 React 中獲得的靈感,中心思想是用 widget 構建你的UI。Widget描述了他們的檢視在給定其當前配置和狀態時應該看起來像什麼。當 widget 的狀態發生變化時,widget 會重新構建 UI,Flutter 會對比前後變化的不同, 以確定底層渲染樹從一個狀態轉換到下一個狀態所需的最小更改(譯者語:類似於React/Vue中虛擬DOM的diff演算法)。

那現在應該就能理解 Widget 了,Widget 是一個用來構建UI的框架,則 runApp 函式接受給定的 Widget 並使其成為 Widget 樹的根。

關於 Widget 框架的內容:Widgets 目錄

瞭解了 main、runApp、Widget 之後我們就可以再次改寫程式碼:

void main() {
  runApp(
    Center( // 居中
      child: Text( // 文字物件
        'Hello World!', // 文字內容
        textDirection: TextDirection.ltr, // 文字輸出方向
        // textDirection 在使用虛擬裝置時需要寫明,不然無法編譯通過。
        // 使用真實裝置 或 Material 時無需寫明文字方向,均會自動處理。
      ), 
    ),
  );
}
複製程式碼

啟動專案後就可以看到一個無 Material 的 hello world

image.png
(無 Material 的 hello world)

恭喜你,你已經可以對外宣稱 “精通 flutter hello world 的編寫”* 。

接下來我們繼續往下看,程式碼中定義了一個叫 MyApp 的類。

這時候你以為我的標題要寫 MyApp

State x Widget

不好意思,你猜錯了!

image.png

MyApp 它繼承自 StatelessWidget 。所以我要說的是關於繼承的這個東西。

Flutter 中文網 - Widget 框架概述

在編寫應用程式時,通常會建立新的 widget,這些 widget 是無狀態的 StatelessWidget 或者是有狀態的 StatefulWidget, 具體的選擇取決於您的 widget 是否需要管理一些狀態。

widget 的主要工作是實現一個 build 函式,用以構建自身。

換句話說 StatelessWidgetStatefulWidget 都是 Widget 的抽象類。唯一不同的是:

  • tatelessWidget 是無狀態的,意味著無法通過資料變更而更新。
  • StatefulWidget 是有狀態的,意味著可以通過資料變更而更新,需要通過setState 來管理狀態。

由於 StatelessWidget 和 StatefulWidget 都是 Widget 的抽象類。

所以我們在使用的時候就需要重寫 Widget 類來實現具體的程式碼和邏輯。重寫 Widget 時我們就需要用到 @override 來裝飾需要重寫的部分。(@override 裝飾關鍵字,如果對於後端語言不熟悉的少俠們那就需要動手查詢資料啦,不然說起來就太長了)

參照右邊程式碼的話這時我們就可以正式使用 MaterialApp 了。

// 繼承自 StatelessWidget
class MyApp extends StatelessWidget {
  // 重寫 Widget 類實現
  @override
  // Widget 實現構建 build 函式
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp();
  }
}
複製程式碼

儲存之後,在控制檯中按下 r 命令 鍵就能看到效果了!!!

一個紅彤彤的的錯誤警告資訊。

什麼?你居然不知道 r 命令 鍵什麼意思?那你現在還不立刻馬上趕緊回去複習?!!複習傳送門

image.png
(錯誤警告資訊 - MaterialApp)

MaterialApp

少俠莫慌,不要害怕!我來告訴你這是怎麼回事。讓我們來先看一下 MaterialApp 的原始碼部分。

image.png
(MaterialApp 部分原始碼)

唉?這個1234 是不是很熟悉啊?是不是好像哪裡見過?

唉~沒錯!,就是剛剛錯誤警告資訊裡的1234,只不過螢幕太小沒有顯示完全。這裡我們看到最後一行的說明:

If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null, and [builder] is not null, then no [Navigator] is created.

翻譯:

如果 [home], [routes], [onGenerateRoute][onUnknownRoute] 都為 null 並且 [builder] 不為 null 那麼將不會建立 [Navigator]

這裡還是沒說為什麼啊。那我們繼續往下看……

image.png
(MaterialApp 部分原始碼)

我們來看,注意這一句話:

Creates a MaterialApp.
At least one of [home], [routes], [onGenerateRoute], or [builder] must be non-null.

翻譯:

建立 MaterialApp
[home][routes][onGenerateRoute][builder] 中的至少一個必須為非 null。

那現在應該瞭解了。我們缺少了必要的程式碼實現,那就先來個最簡單的 [home],所以可以將程式碼修改為:

// 繼承自 StatelessWidget
class MyApp extends StatelessWidget {
  // 重寫 Widget 類實現
  @override
  // Widget 實現構建 build 函式
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      // 實現 home 函式
      home: Center( // 居中
        child: Text('hello world'), // 文字內容
      ),
    );
  }
}
複製程式碼

儲存程式碼,然後 R 命令 一下。 (注意:我沒有打錯字,因為對程式碼的渲染結構進行了更改,所以需要使用 R)

什麼?你居然不知道 R 鍵什麼意思?那你現在還不立刻馬上趕緊回去複習?!!

什麼?你居然還分不清 rR 的區別?!!你取關吧!

image.png
(使用 material 的 hello world)

哇!怎麼這麼醜?誰家APP介面會長這樣啊?

使用主題

少俠不要著急嘛,再說了 “羅馬都不是一天建成的” 要打好基礎循序漸進。

參照右邊的程式碼會發現這裡 用 Scaffold 來實現了 home 函式。我們在來看看原始碼,看看這到底是個什麼東西:

image.png
(Scaffold 部分原始碼)

我們來看到這一句:

Creates a visual scaffold for material design widgets.
為“質感設計”部件建立一個視覺化的支架。

噢(恍然大悟)~原來這才是 UI 的框架啊?

也不全對,我的理解應該是這樣

  • Material 是設計規範 (標準)
  • Scaffold 是實現了設計規範的視覺化支架 (標準實現)
  • Widgets 是 UI 框架,但實現了更具體的一些的物件,例如文字框(Text),行(Row),列(Colum)等 (具體實現)

所以只要 Widget 提供了所需的內容給 Scaffold,那麼 Scaffold 就會實現為對應的設計標準 Material

所以最終的程式碼和執行結果:

import 'package:flutter/material.dart';

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

// 繼承自 StatelessWidget
class MyApp extends StatelessWidget {
  // 重寫 Widget 類實現
  @override
  // Widget 實現構建 build 函式
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold( // 使用 Scaffold 實現 home 函式
        // 導航欄(頂部)顯示文字
        appBar: AppBar( // 導航欄(頂部)
          title: Text('Welcome to Flutter'), // 文字內容
        ),
        
        // Scaffold 的容器 body
        body: Center( // 在容器中居中顯示文字
          child: Text('Hello World'),
        ),
      ),
    );
  }
}
複製程式碼

image.png
(flutter - hello world)

完美!


總結

  • 在學習一門新的程式語言或者框架時,切勿著急囫圇吞棗,複製貼上一把梭只能讓你學會“形”而學不會“意”
  • 知道為什麼,才能更好的在實踐當中去運用。
  • 官方文件永遠是最好的入門資料之一。官網是英文的怎麼辦?國內 IT 行業已經發展了多年,中文社群大概率是會有的。
  • 學好英文,太前沿的領域和內容就需要自己去探索了。

配合文章一同食用的程式碼已同步更新到 Github 地址:github.com/linxsbox/my…

前置閱讀:

參考

相關文章