# Dart Flutter 基礎
## Dart
### Naming convention and Style
#### UpperCamelCase
首字母大寫駝峰且不能分割
- Classes
- enum types
- typedefs
- type parameter
```dart
class SliderMenu { ... }
class HttpRequest { ... }
typedef Predicate<T> = bool Function(T value);
```
#### lowerCamelCase
首字母小寫駝峰
- const
- variables
- function
- parameters
```dart
var item;
const bookPrice = 3.14;
final urlScheme = RegExp('^([a-z]+):');
void sum(int bookPrice) {
// ...
}
```
#### lowercase_with_underscores
單字之間使用底線分割
- file name
```dart
library peg_parser.source_scanner;
import 'file_system.dart';
import 'slider_menu.dart';
```
#### 縮寫
一般情況下縮寫應該依照駝峰設計,當作一般單詞
```dart
👍
class HttpConnection {}
class DBIOPort {}
class TVVcr {}
class MrRogers {}
var httpRequest = ...
var uiHandler = ...
Id id;
```
```dart
👎
class HTTPConnection {}
class DbIoPort {}
class TvVcr {}
class MRRogers {}
var hTTPRequest = ...
var uIHandler = ...
ID iD
```
##### 例外
縮寫為兩個單字以上組成縮寫詞 要為全大寫
ex: Input/output => IO
其他的縮寫則照以上規則
ex: identification => Id
https://dart.dev/guides/language/effective-dart/style
https://medium.com/flutter-community/flutter-best-practices-and-tips-7c2782c9ebb5
#### 字串
單個字串建議使用單引號 ``'test'`` 而非雙引號 `"test"` 除非字串內使用單引號
三引號為多行
```dart
String str =
'''
hello
my
world
'''
```
#### Formatting Rules
- Spaces, not tabs
使用空白好處在其他IDE看起來一至,並且比較好貼到其他顯示平台
🤔
#### import 使用相對位置
```dart
// Don't
import 'package:demo/src/utils/dialog_utils.dart';
// Do
import '../../../utils/dialog_utils.dart';
```
#### Class member使用具體的type
*local variable不用
```dart
//Don't
var item = 10;
final car = Car();
const timeOut = 2000;
//Do
int item = 10;
final Car bar = Car();
String name = 'john';
const int timeOut = 20;
```
#### 避免使用 as 改使用 is
`as`會拋exception, 在一般情況下可以使用 `is`
```dart
//Don't
(item as Animal).name = 'Lion';
//Do
if (item is Animal)
item.name = 'Lion';
```
#### 使用 if 來取代 判斷表示式
```dart
//Don't
Widget getText(BuildContext context) {
return Row(
children: [
Text("Hello"),
Platform.isAndroid ? Text("Android") : null,
Platform.isAndroid ? Text("Android") : SizeBox(),
Platform.isAndroid ? Text("Android") : Container(),
]
);
}
//Do
Widget getText(BuildContext context) {
return Row(
children:
[
Text("Hello"),
if (Platform.isAndroid) Text("Android")
]
);
}
```
#### 使用 spread collections 增加array
```dart
//Don't
var y = [4,5,6];
var x = [1,2];
x.addAll(y);
//Do
var y = [4,5,6];
var x = [1,2,...y];
```
#### 使用 Cascades Operator
```dart
var path = Path();
path.lineTo(0, size.height);
path.lineTo(size.width, size.height);
path.lineTo(size.width, 0);
path.close();
// Do
var path = Path()
..lineTo(0, size.height)
..lineTo(size.width, size.height)
..lineTo(size.width, 0)
..close();
```
#### 避免使用 print()
有時候輸出太多 Android 會被斷行,可以使用 `debugPrint()` 取代
#### 多物件使用 ListView builder
ListView會一次性將所有物件建立,如果記憶體較少會閃退
ListView.builder 是 lazy init
https://github.com/dart-lang/dart_style/wiki/Formatting-Rules
### var
可變數,一但一開始給與值就不能變換型別,否則使用`dynamic` (泛型)
```
var test = 10;
test = '';
❌
```
```
var test;
test = '';
test = 10;
✅ 此例將var視為 dynamic
```
### object
所有物件的`object`子類,與`dynamic`類似
但`dynamic`會嘗試所有的物件方法,反之`object`
```dart
dynamic a;
Object b;
a = "";
b = "";
print(a.length); //no waring
print(b.length); //waring `The getter 'length' is not defined for the class 'Object'`
```
### final & const
不可變
- `final` 第一次使用初始化, 限制唯獨
- `const` 編譯時常量
### Private variables
不像Java Swift有 `public` `protect` `private` 等等可以使用
要使用private 在最前面加上`_`
> `_` 範圍為 file scope,因此同一個檔案目錄下還是讀得到
### Function
`return type` + `function name` + `paramaters(type name)`
```dart
int fibonacci(int n) {
if (n == 0 || n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
var result = fibonacci(20);
```
可以不加回傳類型(不建議)
```dart
intReturn(int value) {
return value;
}
void printInt(int value) {
print(value);
}
printInt(intReturn(19));
```
若只有一行建議使用`=>` 為匿名function 快速寫法
簡潔寫法
```dart
double lbTokg(double lb) {
return lb * 0.45359237;
}
```
```dart
double lbTokg(double lb) => lb * 0.45359237;
```
funtion -> var
```dart
var say = (str){
print(str);
};
say("hi world");
```
function 帶 callback
```dart
void execute(var callback) {
callback();
}
execute(() => print("xxx"));
```
### 多載 Overloading
不支援多載
```dart
class Test {
void isFunctionA(String str) {}
void isFunctionA(String str, String str2) {} //Error!!!
}
```
但可以使用 optional parameters,但需要加在最後
- parameters named 使用 {}
```dart
void isFunction(String strA, {String strB, String strC}) {}
isFunction('A');
isFunction('A', strB: 'B');
isFunction('A', strB: 'B', strC: 'C');
```
- parameters unname 使用 []
```dart
void isFunction(String strA, [String strB, String strC]) {}
isFunction('A');
isFunction('A', 'B');
isFunction('A', 'B', 'C');
```
### Init
```dart
class User {
final String name;
final int age;
User(this.name, this.age);
}
```
or
```dart
class User {
final String name;
final int age;
User.fromInit(String str)
: name = 'finn', age = 29 {
print('name: ' + name + ' age: ' + age.toString());
}
or
User.fromInit(String str)
: name = 'finn', age = 29;
}
```
### 繼承
```dart
class Person {
//Person無任何建構式,Dart 會自動預設,我們為了證明新增子類別物件時會去執行父類別的建構式,故在此新增一個無引數建構來印出我們預期的內容
Person() {
print('Person default constructor');
}
}
class Employee extends Person {
// Person 有預設建構式 (無引數建構)
// 子類別Employee 的建構式可不用加super()
Employee(Map data) {
print('Employee constructor $data');
}
}
main() {
Employee a = Employee({'x':4,'y':5});
/*印出 Person default constructor
Employee constructor {x: 4, y: 5}
*/
}
```
```dart
class Person {
Person(Map data) {
print('Person $data');
}
}
class Employee extends Person {
// Person 沒有無引數建構式
// 子類別Employee 的建構式需要滿足繼承的規則,一定要加super(data)
Employee(Map data) :super(data){
print('Employee $data');
}
//or
Employee(Map data) :super(data);
}
```
### 多型
可抽象多型
```dart
abstract class Shape {
double get area;
}
class Circle extends Shape {
final double _radius;
Circle(this._radius);
@override
double get area => pi * (_radius * _radius);
}
class Square extends Shape {
final double _width;
Square(this._width);
@override
double get area => (_width * _width);
}
```
若為實體繼承,父類別需要空實作
// mixin
### Future
async處理數據
- then, catchError, whenComplete
```dart
Future.delayed(new Duration(seconds: 2),(){
// delayed模擬假的api
// 會回傳 Futrue 所以可以一直binding
//return "hi world!";
//throw AssertionError("Error");
}).then((data){
//執行成功
print(data);
}).catchError((e){
//執行失敗
print(e);
}).whenComplete((){
//無論如何都會走這
//例如要把loading關掉等等
});
```
- wait
需要用`[]`將Future包起來`
```dart
Future.wait([
// 2秒後返回
Future.delayed(new Duration(seconds: 2), () {
return "hello";
}),
// 4秒後返回
Future.delayed(new Duration(seconds: 4), () {
return " world";
})
]).then((results){
print(results[0]+results[1]);
}).catchError((e){
print(e);
});
```
四秒後回來 'hello world'
future寫法有助於減少 `Callback Hell` 波動拳
```dart
Future<String> firstAPI(String input){
return Future.value(input);
}
Future<String> secondAPI(String input){
return Future.value(input);
}
Future<String> thirdAPI(String input){
return Future.value(input);
}
```
```dart
//Callback Hell
firstAPI('test').then((value) {
secondAPI(value).then((value) {
thirdAPI(value).then((value) {
print(value);
});
});
});
```
因為`then` return `Future`
所以縮排可以不用減少
```dart
firstAPI('test').then((value) {
return secondAPI(value);
}).then((value) {
return thirdAPI(value);
}).then((value) {
print(value);
});
```
甚至一行的`then`可以寫得更簡潔
```dart
firstAPI('test')
.then((value) => secondAPI(value)
.then((value) => thirdAPI(value))
.then((value) => print(value)));
```
### Async/Await
async表達非同步,底下的都會等到await回來後才執行下一步
await必須出現在 async function裡
如果不使用Future return可以 使用async/await可以增加可讀性
```dart
apiFlow() async {
var firstString = await firstAPI('test');
var secondString = await secondAPI(firstString);
await thirdAPI(secondString).then((value) => print(value));
}
```
### Stream
也是async處理數據
```dart
Stream.fromFutures([
Future.delayed(Duration(seconds: 2), () {
return 'hello';
}),
Future.delayed(Duration(seconds: 4), () {
return ' world';
})
]).listen((data){
print(data);
}, onError: (e){
print(e.message);
},onDone: (){
print('done');
});
```
第二秒輸出 `hello`
第四秒輸出 ` world` 跟 `done`
### Tool
[DartPad](https://dartpad.dev/?)
[quicktype](https://app.quicktype.io/)
### 練習
情境:
混用 `CP`與`Viewer` List畫面,需要顯示`name` 跟 `avatar` 點擊後會 call `info()`
mockAPI 可以用 `Future delayed`
```dart
void main(List<String> arguments) {
var userList = <User>[CP(), Viewer(), Viewer()];
// for in print name and imageString
}
```
- `User`
var `String` id
func getUserInfo(`String`: id, `int`: timestamp) async return `UserInfo`
func info() return `String`
*timestamp是optional
*getUserInfo是使用`API` getUserInfo
- `CP`
`extension User`
*info return `I'm CP`
- `Viewer`
extension User
*info return `I'm Viewer`
- `UserInfo`
var `String` name
var `String` avatarURL
- `API`
func getImage(`String` url) async return `String`
func getUserInfo(`String` id) async return `UserInfo`
//以下開放 Demo code
{"metaMigratedAt":"2023-06-15T19:09:45.913Z","metaMigratedFrom":"Content","title":"Dart Flutter 基礎","breaks":true,"contributors":"[{\"id\":\"8991f674-f3e6-4bfb-91f1-4e184a839c8d\",\"add\":11524,\"del\":2303}]"}