Flutter Hello world - DashingDigit001/WikiPage GitHub Wiki

專案名稱 Flutter Basic

簡介

Flutter Basic

語言 : Dart

框架:Flutter

主旨 :

  • Flutter UI
  • Flutter Router
  • Flutter Provider
  • Flutter Animation
  • Flutter + Firebase
  • Flutter Android Deployment

快速開始

環境建立

  1. 到Flutter 官網下載 Flutter SDK 並解壓縮到想要的位置
  2. 變更系統環境變數 -> Windows 開始 -> 輸入 "編輯系統環境變數" -> 進階 -> 環境變數 -> Path -> 加入 Flutter SDK bin 的位置 (e.g. D:\flutter\bin)
  3. 下載 Visual Studio -> 執行 visual studio installer -> 勾選安裝 C++ 桌面開發套件
  4. 下載並安裝 Android Studio -> 前往 BIOS -> CPU -> 開啟 Virtual Machine Acceleration -> 開啟 Android Studio -> 點擊 Virtual Machine Manager -> Create Virtual Device -> 效能選取 Hardware - GLES 2.0
  5. VS Code Extention -> Install Flutter
  6. 檢查是否有尚未安裝的 dependence -> powershell -> flutter doctor

專案建立

  1. VS Code -> Invoke View -> Command Palette
  2. 輸入 flutter -> 選擇 Flutter: New Project -> 選擇 Application -> 選擇專案要放的位置 -> 輸入專案名字
  3. 新增 App launcher icon -> google - > app icon generator -> 放入 png ,檔名輸入 "ic_launcher" 產生完後把壓縮黨裡面的資料夾丟到 [project]/android/app/src/main/res/ 取代原本的 -> 到AndroidManifest.xml 檢查 <application android:icon="@mipmap/ic_launcher" 名稱是否正確
  4. VS Code 的右下角 status bar -> 選擇要模擬的裝置 -> 在 main.dart 按下 F5

使用介紹

  • Hello World

    1. 修改 main.dart
    import 'package:flutter/material.dart';
    
    void main() {
    runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);
    
    // This widget is the root of your application.
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Flutter Demo',
        // is not restarted.
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Flutter Demo Home Page'),
          ),
          body: const Center(
            child: Text('Hello World'),
          ),
        ),
      );
    }
    }

UI Layout

  • MaterialApp & Scaffold:

    MaterilApp class 是一個很方便的元件,他提供一大堆用來實現 material design 的元件

    Scaffold class 是 material design layout 最基本視覺排版元件,materialApp 下第一個元件建議為 Scaffold

    return const MaterialApp(
          home: Scaffold(
            body: Text('Hello World'),
          ),
        );
  • Text: 顯示文字,可以加入參數設定字型、大小、顏色、對齊

    Text(
      'Hello,How are you?',
    )
  • Button:

    //ElevatedButton建議放在MaterialApp下
    ElevatedButton(
        onPressed:(){print("clicked")},
    	child:const Text('ElevatedButton'),
    )
        
    TextButton(
     onPressed:(){print("clicked")},
     child:const Text('TextButton'),
    )
  • Column

    把子元件垂直顯示的 layout 元件

    Column(
      children: const [
        Text('Deliver features faster'),
        Text('Craft beautiful UIs'),   
        Text('Craft beautiful UIs'),         
      ],
    )
  • Row

    把子元件水平顯示的 layout 元件

    Row(
      children: const [
        Text('Deliver features faster'),
        Text('Craft beautiful UIs'),   
        Text('Craft beautiful UIs'),         
      ],
    )
  • Container

    提供設定 padding、margin、寬高、顏色、形狀、角度的元件

    child: Container(
        margin: const EdgeInsets.all(10.0),
        color: Colors.amber[600],
        width: 48.0,
        height: 48.0,
      ),
  • Stack & Positioned

    Positioned 可以設定子元件的位置,而子元件的位置設定值相對於 Stack 自己的邊界

    Stack(alignment: AlignmentDirectional.center, children: const [
              Text("11"),
              Text("12"),
              Text("13"),
              Text("14"),
              Text("15454455455"),
            ])
  • Sizedbox

    可以設定自己的寬高的 layout 元件

    const SizedBox(
      width: 200.0,
      height: 300.0,
      child: Text('Hello World!'),
    )
  • Expanded 可以設定自己寬要填滿父元件

    Row(
              children: <Widget>[
                Expanded(
                  flex: 2,
                  child: Container(
                    color: Colors.amber,
                    height: 100,
                  ),
                ),
                Container(
                  color: Colors.blue,
                  height: 100,
                  width: 50,
                ),
                Expanded(
                  child: Container(
                    color: Colors.red,
                    height: 100,
                  ),
                ),
              ],
            )
  • Builder

    這是一個工具元件,目的是為了讓子元件可以在父元件建立完成後才建立。

    body: Builder(
          builder: (BuildContext context) {
            return Center(
              child: TextButton(
                onPressed: () {
                  print(Scaffold.of(context).hasAppBar);
                },
                child: Text('hasAppBar'),
              ),
            );
          },
        ),
  • Listview & ScrollBar

    Listview 可以捲動的元件

    ScrollBar 用來控制 listview 的 scrollbar的元件

    Scrollbar(
            isAlwaysShown: true,
            child: ListView(
              children: <Widget>[
                Container(
                  height: 50,
                  color: Colors.amber[600],
                  child: const Center(child: Text('Entry A')),
                ),
                Container(
                  height: 500,
                  color: Colors.amber[500],
                  child: const Center(child: Text('Entry B')),
                ),
                Container(
                  height: 450,
                  color: Colors.amber[100],
                  child: const Center(child: Text('Entry C')),
                ),
              ],
            ),
          )

業務邏輯

  • stateless widget

    當你沒有資料要在 widget 生命週期內更改時,就用 stateless widget

  • stateful widget

    當你的資料要在 widget 生命週期內更改時,並要通知 UI Render 時,就用 stateful widget

    • setState

      當你要更改 state 時要使用這個函式,才可以通知 UI 重新 Render 畫面

    class Test extends StatefulWidget {
      const Test({Key? key}) : super(key: key);
    
      @override
      _TestState createState() => _TestState();
    }
    
    class _TestState extends State<Test> {
      var age = 10;
      @override
      Widget build(BuildContext context) {
        setState(() {
          age = age + 1;
        });
        return Container();
      }
    }

常用插件

  • Provider 提供一個方便管理全域變數的插件

    1. ChangeNotifier 建立一個倉庫,存放變數及 gettet、setter,notifyListeners() 是用來提醒全部有使用到全域變數的 widget 更新畫面

      class DataModel extends ChangeNotifier {
        int _count = 0;
       
        int get count => _count;
       
        void increment() {
          _count++;
          notifyListeners();
        }
      }
    2. ChangeNotifierProvider

      將要使用全域變數的 widget 放在 ChangeNotifierProvider 之下並初始化 ChangeNotifier

      void main() {
        runApp(
          ChangeNotifierProvider(
            create: (context) => DataModel(),
            child: const MyApp(),
          ),
        );
      }
    3. Consumer

      透過 ChangeNotifier 提供的 getter 、setter 存取、更改全域變數

      class _MyWidgetState extends State<MyWidget> {
        @override
        Widget build(BuildContext context) {
          return Consumer<DataModel>(
            builder: (context, data, child) {
              return Row(
                children: [
                  ElevatedButton(
                    child: const Text("Click me"),
                    onPressed: () {
                      data.increment();
                    },
                  ),
                  Text('Hello' + data.count.toString())
                ],
              );
            },
          );
        }
      }
  • router 切換顯示頁面,有兩種切頁方式

    • 直接切頁

      TextButton(
                child: Text('POP'),
                onPressed: () {
                  Navigator.pop(context);
                },
              ),
    • 具名切頁

  • firebase

    firebase core

    1. flutter pub add firebase_core

    2. flutterfire configure -> 會自動創建一個 lib/firebase_options.dart ,內容是 firebase 的基本資料類似於 vue 中的 firebase.json

    3. import

      //main.dart
      
      import 'package:firebase_core/firebase_core.dart';
      import 'firebase_options.dart';
    4. lib/main.dart 修改 main()

      void main() async {
        WidgetsFlutterBinding.ensureInitialized();
        await Firebase.initializeApp(
          options: DefaultFirebaseOptions.currentPlatform,
        );
        runApp(MyApp());
      }
    5. Android 平台

      到 Firebase console -> 專案設定 -> 找到你的 flutter 應用程式 -> 輸入 SHA 憑證指紋 -> 下載 google-services.json -> 放在專案資料夾/android/app 下

    firestore

    1. flutter pub add cloud_firestore

    2. import firestore import 'package:cloud_firestore/cloud_firestore.dart';

    3. 新增一個 lib/firebaseUtils.dart

      //firebaseUtils.dart
      import 'package:cloud_firestore/cloud_firestore.dart';
      
      class User {
        late FirebaseFirestore _firestore;
        late CollectionReference _usersRef;
      
        User() {
          _firestore = FirebaseFirestore.instance;
          _usersRef = _firestore.collection('users');
        }
          //...
      }
    • Create

      class User{
          //...
          addUser() {
          _usersRef.add({
            'name': 'John Doe',
            'age': 35,
            'city': 'Miami5555',
            'state': 'FL',
            'country': 'USA555',
          });
        }    
      }
    • Read

      class User{
          //...
          readUser() {
          _usersRef.doc('m1gtCAgQb6OAzy0PJHMV').get().then((value) {
            print(value.data());
          });
        }
      }
    • Update

      class User{
          //...
          updateUser() {
          _usersRef.doc('m1gtCAgQb6OAzy0PJHMV').update({
            'name': 'John Doe',
            'age': 999,
            'city': 'Miami',
            'state': 'aaaaa',
            'country': 'TW',
          });
          }
      }
    • Delete

      class User{
          //...
          deleteUser() {
          	_usersRef.doc('m1gtCAgQb6OAzy0PJHMV').delete();
      	}  
      }
    1. import firebaseUtils import './firebaseUtils.dart';

    2. 修改 main.dart

      class _MyAppState extends State<MyApp> {
        late User _user;
          
         @override
        Widget build(BuildContext context) {
          _user = User();
          return MaterialApp(home: Scaffold(body: Consumer<DataModel>(
            builder: (context, data, child) {
              return Row(
                children: [           
                  ElevatedButton(
                    child: const Text(
                      "Create",
                    ),
                    onPressed: () {
                      _user.addUser();
                    },
                  ),
                  ElevatedButton(
                    child: const Text(
                      "Read",
                    ),
                    onPressed: () {
                      _user.readUser();
                    },
                  ),
                  ElevatedButton(
                    child: const Text(
                      "update",
                    ),
                    onPressed: () {
                      _user.updateUser();
                    },
                  ),
                  ElevatedButton(
                    child: const Text(
                      "Delete",
                    ),
                    onPressed: () {
                      _user.deleteUser();
                    },
                  ),
                  Text('Hello' + data.count.toString())
                ],
              );
            },
          )));
        }
      }

Firebase Auth

  1. install firebase_core and initialize Firebase
  2. flutter pub add firebase_auth
  • google login

    1. flutter pub add google_sign_in

    2. import 'package:google_sign_in/google_sign_in.dart';
      
      Future<UserCredential> signInWithGoogle() async {
        // Trigger the authentication flow
        final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
      
        // Obtain the auth details from the request
        final GoogleSignInAuthentication? googleAuth = await googleUser?.authentication;
      
        // Create a new credential
        final credential = GoogleAuthProvider.credential(
          accessToken: googleAuth?.accessToken,
          idToken: googleAuth?.idToken,
        );
      
        // Once signed in, return the UserCredential
        return await FirebaseAuth.instance.signInWithCredential(credential);
      }
    3. 登入

      signInWithGoogle();
    4. 登出

      await FirebaseAuth.instance.signOut();
      await GoogleSignIn().signOut();

debug.keystore

預設資料

Keystore name: "debug.keystore"
Keystore password: "android"
Key alias: "androiddebugkey"
Key password: "android"
CN: "CN=Android Debug,O=Android,C=US"

取得 SHA1 key 的步驟

  1. powershell -> keytool -list -v -alias androiddebugkey -keystore <debug.keystore 資料夾位置>

佈署

Build Windows Application

  1. powershell 輸入 flutter build windows
  2. 到專案根目錄 -> build/windows/runner -> release 資料夾就是 build 出來的執行檔

Build Android APK

  1. 到 Android Studio -> Build -> Generate Signed Bundle/APK
  2. click Create new -> 取得 keystore(.jks) 並把他放在 C:\Users\User\.android 目錄下

觀念

Material Design

所有 Material Design 的元件建議要放在 MaterialApp 之下

元件

State

元件的變數,可以被用來存取、變更現在的數值,數值會影響元件的表現

Stateless Widget

這個 widget 的顯示畫面是沒有紀錄狀態的,所以當 stateless 的widget build 完並顯示在畫面上後,即使 class 內的屬性被更改也不會影響已經 Render 出來的 Widget 內的屬性

Stateful Widget

這個元件的顯示畫面是有紀錄狀態的,當 State class 的屬性更新時,畫面也會隨著 widget 狀態的改變重新 Render

Widget 生命週期

下圖所有方法都可以加入 @override,有使用 @override都要搭配 super. 1_9kFme2gBIl9HtYFidAZw0w

⚠️ **GitHub.com Fallback** ⚠️