iT邦幫忙

2025 iThome 鐵人賽

DAY 4
0
自我挑戰組

攜手 AI 從零開始打造一款 Flutter 應用程式系列 第 4

Day 4: 創建「省錢拍拍」、解構 Flutter App 的骨架

  • 分享至 

  • xImage
  •  

前言

大家好!在 Day 3 我們快速掌握了 Dart 語言的核心精華,為接下來的 Flutter 之旅鋪平了道路。有了 Dart 這把利劍,我們今天終於要正式踏入 Flutter 的世界,親手創建我們的專案——「省錢拍拍 (SnapSaver)」。

今天的目標是:創建專案,並透過逐行程式碼,徹底解構 Flutter 預設 App 的每一塊積木。不只要知道概念,更要看懂程式碼是如何實現這些概念的。

準備好了嗎?讓我們正式啟動專案!

Step 1: 一行指令,創建專案

方法一:使用指令

請打開 VS Code 的終端機 (Terminal),切換到你想要存放專案的資料夾路徑。然後,輸入以下指令:

flutter create snapsaver

完成後,會看到終端機提示:

All done!
In order to run your application, type:

  $ cd snapsaver
  $ flutter run

Your application code is in snapsaver/lib/main.dart.

接著,請切換到我們剛創建的 snapsaver 資料夾。

方法二:使用 VS Code 圖形介面

除了指令,你也可以透過 VS Code 的介面來建立專案:

  1. 打開 VS Code,按下 F1 叫出命令面板,輸入 flutter 並點選 New Project
  2. 點選 Application,選擇你想要存放專案的資料夾。
  3. 為專案取名(我這裡同樣叫 snapsaver),按下 Enter 即可創建成功。

flutter-create

flutter-app

flutter-name

Step 2: 探索專案的藏寶圖 - 資料夾結構

打開專案後,左側會有一堆資料夾和檔案。別緊張,我們初期只需要關注幾個最核心的部分即可。

flutter 資料夾結構

  • lib/: 我們未來 95% 的時間都會待的地方,所有 Dart 程式碼都存放在此。
    • main.dart: App 的入口檔案,程式從這裡開始執行。
  • pubspec.yaml: 專案的「身分證」與「說明書」。管理專案名稱、版本、依賴套件與資源。
  • ios/android/: 存放原生平台的專案檔,初期可先忽略。

Step 3: 解構 main.dart - App 的骨架與血肉

現在,我們打開 lib/main.dart ,聚焦在 MyHomePage 這個 Widget,看看畫面是如何由程式碼一行行搭建的。

在深入程式碼前,我們先看 MyApp 的類別宣告:

class MyApp extends StatelessWidget

這行程式碼的意思是:MyApp 繼承了 StatelessWidget,因此 MyApp 本身就是 Flutter Widget 的一種。

基石一:MaterialApp - App 的風格總管

main.dart 的頂部,MyApp Widget 返回了 MaterialApp。它就像是 App 的「總設定檔」,定義了 App 的根路由(home)、主題(theme)等。

class MyApp extends StatelessWidget {
  // ...
  @override
  Widget build(BuildContext context) {
    return MaterialApp( // 返回 MaterialApp
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      // 指定 MyHomePage 為我們的首頁
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

基石二:Scaffold - 撐起頁面的骨架

MyHomePagebuild 方法是整個畫面的核心。在這裡,它返回了一個 Scaffold Widget。Scaffold 負責搭建頁面的基本結構,如頂部導航欄、頁面主體等。

直接來看程式碼:

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

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    // Scaffold Widget 提供了標準的頁面佈局結構
    return Scaffold(
      // 1. appBar: 頁面頂部的導航欄
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      // 2. body: 頁面的主體內容
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('You have pushed the button this many times:'),
            Text(
              '$_counter', // 顯示計數器變數
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      // 3. floatingActionButton: 右下角的懸浮按鈕
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,  // 按下時觸發 _incrementCounter 函式
        tooltip: 'Increment',
        child: const Icon(Icons.add),  // 按鈕中的 '+' 圖示
      ),
    );
  }
}

這段程式碼中的 Scaffold 三大屬性:

  1. appBar: 這裡我們傳入了一個 AppBar Widget,它會自動被放置在頁面頂部。AppBar 內部有一個 title 屬性,我們再放入一個 Text Widget 來顯示標題文字。

結構:Scaffold -> appBar -> AppBar -> title -> Text

  1. body: 這是頁面的主要內容區域。範例中,為了讓文字垂直置中,這裡使用了一個 Widget 組合技:
    • Center: 將它的子元件(child)置於中心。
    • Column: 將它的子元件們(children)垂直排列。
    • Text x 2: 兩個 Text Widget,一個顯示說明文字,另一個顯示 _counter 變數的數值。

結構:Scaffold -> body -> Center -> Column -> [Text, Text]

  1. floatingActionButton: 這裡我們放入一個 FloatingActionButton Widget。
    • onPressed: 指定了按鈕被按下時要執行的函式,這裡連結到了 _incrementCounter
    • child: 在按鈕內部要顯示的內容,這裡是一個 Icon Widget。

Step 4: 串連一切的「狀態」 - Stateless vs. Stateful

在 Flutter 的世界裡,萬物皆為 Widget (元件)

而 Widget 分為兩大類:

  1. StatelessWidget (無狀態元件)

    • 特性:靜態的、不可變的。
    • 比喻:一張印刷好的海報。海報印出來之後,上面的內容就固定了,不會再改變。
    • 用途:用於展示靜態資訊的場景,例如 App 的 Logo、一個固定的標題文字、一個純粹裝飾用的圖示。在 main.dart 範例中,MyApp 這個類別就是一個 StatelessWidget
  2. StatefulWidget (有狀態元件)

    • 特性:動態的、可變的。
    • 比喻:一個電子計分板。當使用者點擊按鈕時,計分板上的數字會跟著改變。
    • 用途:當畫面需要根據使用者互動或資料變化而更新時,就必須使用它。例如,計數器 App 中那個會變動的數字、一個可以勾選的核取方塊。在範例中,MyHomePage 就是一個 StatefulWidget

StatefulWidget 的特別之處在於,它會將「狀態 (State)」交由一個獨立的 State 物件來管理。當我們呼叫 setState() 這個方法時,Flutter 就會知道:「嘿!資料變了,快幫我把畫面重新畫一次!」

當使用者點擊 FloatingActionButton 時,會觸發 onPressed 指定的 _incrementCounter 函式。

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

setState() 會做兩件事:

  1. 更新 _counter 的數值。
  2. 通知 Flutter:「狀態變了!」

當 Flutter 收到通知後,就會重新執行 _MyHomePageStatebuild 方法,用新的 _counter 數值來重繪 Text Widget,於是我們就會在螢幕上看到了數字的更新。

今日結語

今天,我們從無到有創建了「省錢拍拍」專案,掌握了 Flutter App 的核心結構:MaterialApp 負責全局,Scaffold 搭建骨架,各種基礎 Widget 填充內容,並理解 StatelessWidgetStatefulWidget 的核心差異。


上一篇
Day 3: 學習 Flutter 的第一步 - Dart 語言速成
下一篇
Day 5: UI 佈局基礎 - Row, Column, Container
系列文
攜手 AI 從零開始打造一款 Flutter 應用程式8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言