# APP開發環境配置
下載Android Studio,一路點到底,直到看到這個畫面
https://developer.android.com/studio?hl=zh-tw

下載flutter SDK,解壓縮在C槽跟Progrem file以外的地方
https://developer.android.com/studio?hl=zh-tw
設定flutter的環境變數

在Android Studio中的 **Plugins > Marketplace 中搜尋Flutter並安裝**

重啟後,便可創建Flutter Project了

# Flutter Hello World
## 創建專案
選擇New Flutter Project > 選擇Flutter SDK Path

決定專案名跟專案位置

## 創建模擬器
**點選 Device Manager > Create Device**

選擇手機型號

選擇Andriod系統版本,通常API Level越高越好,Target 選有Google api的為佳

設定模擬器的對話盒

點選這裡即可啟動模擬器

## 撰寫Dart語言程式碼並執行
通常是在lib > main.dart中撰寫的

在模擬器啟動的情況下 便可以直接執行main.dart


# Dart 語法教學
可以在這邊練習:
https://dartpad.dev/?
Dart 語法跟C/C++很像,本篇主要強調不同之處
## 輸出
```dart
void main() {
int a=10,b=20;
print(a); //輸出單變數
print("$a $b"); //一次輸出多個變數,這邊的"$"負責將物件轉成字串
// 輸出
// 10
// 10 20
}
```
## 變數
* 共有五種可用的變數型態
1. int
2. double
3. num: 可以同時代表int及double
4. String: S要大寫
5. bool
* 注意: Dart語言**不可強制轉型**,需要使用對應的method才能實現型別的互相轉換
* Dart語言的變數其實都是物件,故可執行如下的操作
```dart
int x=50;
String y=x.toString();
```
使用範例
```dart
void main() {
int a=10; //宣告的同時賦值
print(a); //基本輸出
String s; //宣告的時候不賦值,當前狀態為NULL
// print(s); //錯誤,根據Dart的Null Safety機制,變數在使用時不可為NULL
s='apple';
print(s); //正確
var b=true; //類似C++的auto,自動判斷變數的型態
// b='banana'; //錯誤,var被賦值之後,型態便不可任意轉換
print(b);
dynamic c=3.14; //類似C++的auto
c='cat'; // 與var不同的是,dynamic被賦值之後,型態還可以再被轉換
print(c);
const d=1e9+7; //宣告常數
print(d);
// 輸出
// 10
// apple
// true
// cat
// 1000000007
}
```
### 變數之間的轉型
因為Dart語言不可強制轉型,故提供一系列api提供型態轉換
```dart
void main() {
//字串轉整數or浮點數
//.parse()稱作「靜態方法」,不須創建物件即可使用
int a=int.parse('3');
double b=double.parse('3.14');
num c=num.parse('1.414');
print("$a $b $c");
//整數or浮點數轉字串
String s1=a.toString();
String s2=b.toString();
print("$s1 $s2");
//整數、浮點數互轉
int temp1=b.toInt();
double temp2=a.toDouble();
print("$temp1 $temp2");
//輸出
// 3 3.14 1.414
// 3 3.14
// 3 3
}
```
## 函式
大致上與C++相同
* 使用"[ ]"來框住選擇性傳入參數,選擇性傳入參數通常會放在最後面
```dart
void f(int a, [String s='apple']) {
print("f: $a $s"); //輸出多個變數的方法
}
void main() {
f(10);
f(10,'banana');
}
// 輸出
// f: 10 apple
// f: 10 banana
```
* 還有使用參數名稱來設定值的特殊用法
```dart
//被大括號括住的變數,需使用"指定參數名稱及值"的方式傳入
// 這些變數只能是以下三種宣告方式之一:
// 加上"required",代表這個變數一定要被傳入
// 給定一個初始值,這樣就算不傳入該變數也不會是NULL
// 加上"?",代表這個變數允許空值
void g({required int a, int b=10, String? s}) {
print("g: $a $b $s");
}
void main() {
g(a:1, b:2, s:'apple');
g(s:'banana', a:10, b:20); //可以亂序傳入
g(a:1); //可以不傳 b 跟 s 的值
// g(10, 'apple'); //錯誤用法
// 輸出
// g: 1 2 apple
// g: 10 20 banana
// g: 1 10 null
}
```
## 類別 (class)
* 沒特別指定的情況下,class內部的變數及函式都是Public的
### 基本用法
```dart
class Person {
//使用 late 關鍵字暫時繞過Null Safety的機制
//允許Non-nullable暫時為空,等等就會賦值
late String name; //class Person中的變數
Person(String inputName) { //constructor,用法同C++
name=inputName;
// this.name=inputName; //與上面那行等價
}
void printInfo() { //class Person中的方法(method)
print(name);
}
}
class Student extends Person { //Student 繼承 Person (使用extends關鍵字)
late String studentID; //class Student中的變數
//constructor,super()代表使用Parent的constructor來協助初始化
//使用super()時,需傳入Parent constructor所需的參數
Student(String inputName, String inputID) : super(inputName) {
studentID=inputID;
}
}
void main() {
var student1=Student('Jason','410410054');
student1.printInfo(); //使用class中的method
print(student1.name); //使用class中的變數
//輸出
//Jason
//Jason
}
```
### constructor 簡寫
可以直接用class成員去對應的位置接
```dart
Person(String inputName) {
name=inputName;
}
//簡寫後
Person(this.name);
```
```dart
Student(String inputName, String inputID) : super(inputName) {
studentID=inputID;
}
//簡寫後
Student(String inputName,this.studentID) : super(inputName);
```
### Overwritten 用法
```dart
class Person {
late String name;
Person(this.name);
void printInfo() {
print(name);
}
}
class Student extends Person {
late String studentID;
Student(String inputName,this.studentID) : super(inputName);
@override // 這行是編譯註解,供compiler 檢查用
void printInfo() { //重新定義Parent的method
print("$name $studentID");
}
}
void main() {
var student1=Student('Jason','410410054');
student1.printInfo();
print(student1.name);
//輸出
//Jason 410410054
//Jason
}
```
# Flutter 開發實戰
## hello world
把以下程式碼貼在 lib > main.dart 中即可
```dart
import 'package:flutter/material.dart';
void main() {
// 宣告一個Text型別的物件,作為appbar的title
//可以想成Text()就是constructor
//const 代表使用"const constructor",可以加快執行的速度
var appTitle=const Text('my first flutter app');
// 再宣告一個Text型別的物件,作為appBody的child
// 設定這個物件的Style;使用TextStyle()這個constructor來指定文字大小
//這個用法就如同函式中 "使用參數名稱來設定值" 的特殊用法
var displayText=const Text(
'Hello World',
style: TextStyle(fontSize: 30),
);
//宣告一個Center型別的物件,作為app的body
var appBody=Center(
child:displayText,
);
//宣告一個AppBar型別的物件,作為app的appBar
var appbar=AppBar(
title: appTitle,
);
//宣告一個MaterialApp型別的物件,作為app的主體
var app=MaterialApp(
home: Scaffold(
appBar: appbar,
body: appBody,
),
);
//執行app物件
runApp(app);
}
```
## StatelessWidget
Flutter 中的物件可以分成兩類:
StatelessWidget: 物件內容不隨使用者操作改變
StatefulWidget: 物件內容會隨著使用者操作改變
hello world範例中的app 屬於StatelessWidget,故可以讓這個app"繼承"StatelessWidget
```dart
import 'package:flutter/material.dart';
//定義class App,並繼承 StatelessWidget
class App extends StatelessWidget {
@override // 覆寫StatelessWidget 中的build method
Widget build(BuildContext context) {
// 宣告一個Text型別的物件,作為appbar的title
//可以想成Text()就是constructor
//const 代表使用"const constructor",可以加快執行的速度
var appTitle=const Text('my first flutter app');
// 再宣告一個Text型別的物件,作為appBody的child
// 設定這個物件的Style;使用TextStyle()這個constructor來指定文字大小
//這個用法就如同函式中 "使用參數名稱來設定值" 的特殊用法
var displayText=const Text(
'Hello World',
style: TextStyle(fontSize: 30),
);
//宣告一個Center型別的物件,作為app的body
var appBody=Center(
child:displayText,
);
//宣告一個AppBar型別的物件,作為app的appBar
var appbar=AppBar(
title: appTitle,
);
//宣告一個MaterialApp型別的物件,作為app的主體
var app=MaterialApp(
home: Scaffold(
appBar: appbar,
body: appBody,
),
);
return app; //回傳app 物件
}
}
void main() {
//執行app物件
//呼叫App的constructor,程式便會回傳一個build完成的App物件
runApp(App());
}
```
## Text 物件
文字是app中最常見的元素之一,可以由constructor Text()所建立
```dart
import 'package:flutter/material.dart';
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
var appTitle=const Text('my first flutter app');
var displayText=const Text(
'Hello World', //欲顯示的文字
style: TextStyle( //設定文字樣式
fontSize: 30, //指定文字大小
color: Colors.red, //設定文字顏色
// 可選值:
// Colors.blue 等等
// Color(0x002f2f2f) 使用Color物件指定
decoration: TextDecoration.lineThrough, //加上線條
// 可選值:
// TextDecoration.underline: 加上底線
// TextDecoration.lineThrough: 加上刪除線
// TextDecoration.overline: 文字上方加一條線
fontWeight: FontWeight.w900, //設定粗細
// 可選值:
// FontWeight.w100 ~ FontWeight.w900
backgroundColor: Colors.green, //設定背景顏色,用法同color
),
textAlign: TextAlign.center, //設定對齊方式
// 可選值:
// TextAlign.center: 置中對齊
// TextAlign.left: 置左對齊
// TextAlign.right: 置右對齊
// TextAlign.justify: 使文字在容器內兩側均勻地對齊,填滿整個行寬
maxLines: 10, //限制該文字最多顯示幾行,多的會直接隱藏
);
var appBody=Center(
child:displayText,
);
var appbar=AppBar(
title: appTitle,
backgroundColor: const Color(0xffffff99), //可以指定appbar的背景色
);
var app=MaterialApp(
home: Scaffold(
appBar: appbar,
body: appBody,
backgroundColor: const Color(0xffadd8e6), //可以指定app的背景色
),
);
return app;
}
}
void main() {
runApp(App());
}
```
## 連接到真機 debug
1. 前置動作 (安裝 google USB Driver)

2. 開啟手機的開發者模式
> 設定 > 關於手機 > 不斷點擊 MIUI 版本直到跳出通知


3. 開啟手機的 USB 除錯模式
> 設定 > 更多設定 > 開發者選項 > USB 偵錯(打開)



4. 連接上 usb,選擇傳輸檔案

5. 回到 Android Studio,便會自動找到連接的設備;不須另外開啟手機模擬器便可以直接執行程式
