# 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}]"}
    614 views