# Flutter初探:基本觀念筆記
(還有待整理跟補充)
## Flutter框架結構
1. 框架層Framework(Dart):
why Dart(開發時JIT即時編譯/發布時AOT提前編譯)
+ Material/Cupertino
+ Widgets
+ Rendering
+ Animation/Painting/Gestures
2. 引擎層Engine(C/C++)
3. 嵌入層Platform-specific
## Flutter環境設置
1. install flutter and revise bash path for flutter
- code . ~/.bash_profile 編輯檔案新增下面那行指定路徑
- export PATH="$PATH:/Users/florachen/flutter/bin"
- echo $PATH 或是 which flutter 檢查路徑對不對
- flutter doctor檢查開發環境
- shift command P 創建 => Flutter:New Project
- flutter run (要開模擬器即時更改要切debug模式)
2. Xcode(IOS simulator)
- sudo gem install xcode-install
- 輸入xcversion會跳出apple認證如果認證失效可以輸入( rm $HOME/.fastlane/spaceship/*/cookie )
- xcversion list 列出所有版本以及已有安裝的版本
- xcversion install 13.4.1
- xcversion installed
- xcrun --version(確認有沒有安裝Xcode command line tool)
- xcrun simctl list(列出所有可用的機型id)
- xcrun simctl boot 5EE6154A-A997-46AF-811C-A95918878C43(在vscode中選取ios simulator)
- xcrun simctl shutdown [device id]
3. VScode 安裝 flutter & dart
4. android studio
- brew install --cask android-studio
5. firebase
- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
- nvm alias default {{ 預設版本號 }} nvm list nvm use {{ 版本號 }} 切換當前命令列所使用的 Node.js 版本
- npm install -g firebase-tools (firebase需要nodejs14)
- firebase login
- flutterfire configure連結現有data or 創建
- initialize firebase 必須在一開始
- 若設置使用者可以用信箱跟密碼登入 必須在註冊後收驗證信再讓使用者登入才能取得已驗證的userCredential
## Dart
1. variable:可更改(var)但型別不能改
2. const 不可
3. final:不可更改的var//可以省略類型聲明
final str = "hi world";=>final String str = "hi world";
*late 現在還沒但之後會有的變數(?
dynamic dynamic是所有物件的基礎類型,也就是說它可以代表任何物件。
4. 空安全(null-safety)
=>fun?.call() //fun 不为空时则会被调用
5. dynamic和Object: dynamic與Object聲明的變量都可以賦值任意對象,且後期可以改變賦值的類型
6. 函數
- ex:
```
String getName(String firstName,String lastName){return firstName+''+lastName;} `
//其實就是把function加上型別
```
```
void user(String user){
if(user != null){
print('user is $user');
}else{
print('user is root');
}}
===>void user(String user){
user ??= 'root';
print('user is $user');
}
```
7. 集合
- Lists(同義於array) 可在定義時帶入型別或是長度,常見的方法:reversed/contains/map/forEach/...
+ fold(用於Lists)
```
- ex1
final numbers=[1,2,3];
final sum=numbers.fold(
0,(
int previousValue,
int thisValue
)=>reviousValue+thisValue)));
=====>result is 6(起始值,作用的fun) 相似於reduce
final numbers=[1,2,3];
- ex2
final names=['a','b','c'];
final result=names.fold(
'',(
result,
str,
)=>'$result ${str.toUpperCase()}');
=====>result is " A B C"
```
Collection if & Collection for
```
var isRoot = true
var users = [
'user1',
'user2',
'user3',
if(isRoot) 'root'];
const A=['a','b'];const B=['a','b'];===>A=B是true
```
+ 如果要不能修改可以使用UnmodifiableListView(users)
- Sets
+ Sets 是沒有索引值、!不可重複!的集合,以hashCode的值來決定是否相等。
+ var sets = <int>{1,2,3,4};//如果初始化未給定型別 dart會自動判斷
+ 範例:
```
final person1=Person(age:10,name:'foo');
final person2=Person(age:10,name:'foo');
final persons={person1,person2};
print (persons)=====>{Person:foo,10,Person:foo,10}
此時若加上:bool operator==(Object other)=>
identical(this,other)||other is Person &&name==other.name&&age==ther.age;
print (persons)=====>{Person:foo,10}
```
- Maps
+ putIfAbsent():當該key(height)無值時插入值170 /或是直接info['height']=180/remove(Object):
ex:info.putIfAbsent('height',()=>170)
+ update(key, value) containsKey(key) containsValue(value)
8. enum 是一種型別,用於自定義 type,若用在 switch 上,IDE 會自動告訴我們有哪一個 enum 尚未考慮到。
9. 宣告class時預設都是public 可加(_)變private,
10. 使用operator來處理等價與否,
```
ex:class Point{
const Point(this.x,this.y);
final int x;
final int y;
@override
String toString()=>'Point($x.$y)';
@override
// print(Point(0,0)==Point(0,0))//result is false so we need to do the next line
bool operator==(Object other){
if(other is Point){
return x==other.x&&y==other.y;
}
return false;
}
=======>可以寫成這樣~
bool operator==(covariant Point other){
return x==other.x&&y==other.y;
}
}
```
11.
```
Iterable.generate(20,(i)=>getName(i))
for (final name in iterable.take(2)){print(name)}
========>result 只會print i=0&i=1 因為Iterable是lazy的?
```
## Widgets
1. StatelessWidget:是一個不需要自己紀錄內部狀態的組件,需要的資料只能靠外部提供。
(stl or stf vscode打了就會自動生成)
2. StatefulWidget:是有自己內部狀態的組件,可以建立自己的state也可以接受外部的prop,一個StatefulWidget會分開定義「widget」和「widget所使用的狀態」。
```
- class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title; // 外部傳入的props
@override
State<MyHomePage> createState() => _MyHomePageState();
}
- class _MyHomePageState extends State<MyHomePage> {
int _counter = 0; // state
void _incrementCounter() {
setState(() {
_counter++; // 更動自己的state
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
......
```
3. WHY???由於Flutter 的widget創建後內部的資料是不能更動(immutable)的,要更改畫面必須重新創建,因此採用「創建widget」之後「注入state」方法,由state來掌管可變的狀態。
此外將state與widget拆分,也可以確保每次widget重製時,state可以獨立於widget之外仍然保留狀態。
4. 盡量只讓需要變動的組件採用StatefulWidget,而可以單純依照外部資料顯示的組件採用StatelessWidget,才能最有效地分配運算資源。
## 佈局約束策略
1. xConstraints go down.
Child Widget並不是長寬設為多少就會長成多少,必須接受來自上層的Parent Widget最大長寬度的限制。
2. Sizes go up.
各個Child Widget皆會將自身期望的長寬包含約束條件向上傳遞給Parent Widget。
3. Parent sets position.
Parent統整了所有Child Widget的尺寸資訊後,便開始在畫面上依照大小決定各自的位置
## Layout
1. Single Child Layout Widget
Align:對齊
AspectRatio:指定比例
Center:置中
ConstratinedBox:長寬限制
Container:外框容器
Expanded:盡可能佔據剩餘空間
Padding:與外框的間隙:
- fromLTRB(double left, double top, double right, double bottom):分别指定四个方向的填充。
- all(double value) : 所有方向均使用相同数值的填充。
- only({left, top, right ,bottom }):可以设置具体某个方向的填充(可以同时指定多个方向)。
- symmetric({ vertical, horizontal }):用於對稱方向的填充,vertical指top和bottom,horizontal指left和right。
```
Padding(
//上下各添加8像素
padding: EdgeInsets.symmetric(vertical: 8),
child: Text("I am Jack"),
),
```
SingleChildScrollView:加上頁面捲軸
2. Multi Child Layout Widget
Column:垂直排列
GridView:網格式排列
ListView:列表式排列
Row:水平排列
Stack:堆疊排列(後面元素在前)
3. Leaf Widget
Widget樹的葉子節點,用於沒有子節點的widget,通常基礎組件都屬於這一類,如Image。
## 導頁 - navigation
route stack:進入新的頁面,頁面所在的路徑會依序被加入(push)到route stack,退出時也會從「最後進入的頁面」開始退出(pop),從route stack刪除。
- 返回上一頁
要返回的頁面設置Navigation.pop()
- 指定路徑
routes: {
'/first': (context) => const FirstScreen(),
},
## Future
當我們呼叫一個非同步函式時,會回傳一個「未完成的Future」來等待此函式的非同步行為執行完畢,並獲得結果,也可能在執行的過程中出現問題而回傳錯誤(promise感) 一樣會有.then .catchError 最後要做的事=> whenComplete
延遲function (setTimeOut感)
`Future.delayed(const Duration(seconds: 2), () => print('Here I come!')); `
## 事件處理
1. 原始指針(pointer):鼠標、觸控筆
this.onPointerDown, //手指按下回调
this.onPointerMove, //手指移动回调
this.onPointerUp,//手指抬起回调
this.onPointerCancel,//触摸事件取消回调
2. 手勢:拖拉、雙擊等等
GestureDetector是一個用於手勢識別的功能性組件,我們通過它可以來識別各種手勢。
## Flutter state management: 有很多方法 內建的inherit widget或是provider flutterhook redux等等
- Provider/Consumer (額外加入 類似redux)
1. 設定一個「狀態管理組件」(ChangeNotifierProvider),放在想要傳遞資料以及使用資料的組件(在專案中是指MainPage, FavoritePage)的上層。
2. 接著,「狀態管理組件」會創建一筆「可訂閱的資料模型」(extends ChangeNotifier),也就是我們這次指稱的app state,供各組件訂閱使用(Comsumer)或操作(Provider.of)。
3. 當資料被修改,ChangeNotifier可以通知訂閱此資料模型的組件,依照最新的資料重繪。
4. Consumer:在子組件中取用Provider/ChangeNotifierProvider提供的資料
其他:
print=console.log
command + .==>快速wrap或是unwrap
$變數 如果要印$就要加\$
void是沒有return東西的function 有return就會在一開始宣告return的東西的型別
在main()的東西不會hot reload 需要restart
只有雙等號而已