开发中总结的dart相关的技巧
特意給大家帶來我在開發中總結的dart相關的技巧
1. 你知道嗎?Dart 支持字符串乘法。
這是一個簡單的程序,顯示如何使用字符串乘法打印圣誕樹:
void main() {for (var i = 1; i <= 5; i++) {print('🎄' * i);} } // Output: // 🎄 // 🎄🎄 // 🎄🎄🎄 // 🎄🎄🎄🎄 // 🎄🎄🎄🎄🎄是不是很酷?😉
您可以使用它來檢查長字符串如何適合Text小部件:
Text('You have pushed the button this many times:' * 5)2.需要同時執行多個Future嗎?使用 Future.wait。
考慮這個模擬 API 類,它告訴我們最新的 COVID 病例數:
// Mock API class class CovidAPI {Future<int> getCases() => Future.value(1000);Future<int> getRecovered() => Future.value(100);Future<int> getDeaths() => Future.value(10); }要同時執行所有這些futures,請使用Future.wait. 這需要一個列表或 **futures and returns a future of lists:
final api = CovidAPI(); final values = await Future.wait([api.getCases(),api.getRecovered(),api.getDeaths(), ]); print(values); // [1000, 100, 10]This is ideal when the futures are independent, and they don't need to execute sequentially.
3. 在 Dart 類中實現“調用”方法,使它們像函數一樣可調用。
這是一個示例PasswordValidator類:
class PasswordValidator {bool call(String password) {return password.length > 10;} }因為該方法名為call,我們可以聲明一個類實例并將其用作方法:
final validator = PasswordValidator(); // can use it like this: validator('test'); validator('test1234'); // no need to use it like this: validator.call('not-so-frozen-arctic');4. 需要調用回調但前提是它不為空?使用“?.call()”語法。
假設我們有一個自定義小部件類,它應該onDragCompleted在發生特定事件時調用回調:
class CustomDraggable extends StatelessWidget {const CustomDraggable({Key key, this.onDragCompleted}) : super(key: key);final VoidCallback? onDragCompleted;void _dragComplete() {// TODO: Implement me}@overrideWidget build(BuildContext context) {/*...*/} }要調用回調,我們可以編寫以下代碼:
void _dragComplete() {if (onDragCompleted != null) {onDragCompleted();}}但是有一個更簡單的方法(注意使用?.):
Future<void> _dragComplete() async {onDragCompleted?.call();}5. 使用匿名函數和函數作為參數
在 Dart 中,函數是一等公民,可以作為參數傳遞給其他函數。
下面是一些定義匿名函數并將其分配給sayHi變量的代碼:
void main() {final sayHi = (name) => 'Hi, $name';welcome(sayHi, 'Andrea'); }void welcome(String Function(String) greet,String name) {print(greet(name));print('Welcome to this course'); }然后sayHi傳遞給一個welcome函數,該函數接受一個Function參數并使用它來迎接用戶。
String Function(String)是一個函數類型,它接受一個String參數并返回一個String. 因為上面的匿名函數具有相同的簽名,它可以直接作為參數傳遞,也可以通過變量傳遞sayHi。
使用功能等運營商時,這種編碼風格是常見的map,where和reduce。
例如,這是一個計算數字平方的簡單函數:
int square(int value) {// just a simple example// could be a complex function with a lot of codereturn value * value; }給定一個值列表,我們可以映射它們以獲得平方:
const values = [1, 2, 3];values.map(square).toList();這里我們square作為參數傳遞,因為它的簽名正是 map 操作符所期望的。這意味著我們不需要用匿名函數擴展它:
values.map((value) => square(value)).toList();6. 您可以使用 collection-if 和 spreads 與lists, sets AND maps
當您將 UI 作為代碼編寫時,Collection-if 和 spreads 非常有用。
但是您知道您也可以將它們與maps一起使用嗎?
考慮這個例子:
const addRatings = true; const restaurant = {'name' : 'Pizza Mario','cuisine': 'Italian',if (addRatings) ...{'avgRating': 4.3,'numRatings': 5,} };這里我們聲明一個restaurantmaps,只添加avgRating和numRatings鍵值對,如果addRatings是true。因為我們要添加多個鍵值對,所以我們需要使用擴展運算符 ( ...)。
7. 需要以空安全的方式遍歷map嗎?使用.entries:
假設你有map:
const timeSpent = <String, double>{'Blogging': 10.5,'YouTube': 30.5,'Courses': 75.2, };以下是如何編寫循環以使用所有鍵值對運行一些代碼:
for (var entry in timeSpent.entries) {// do something with keys and valuesprint('${entry.key}: ${entry.value}'); }通過迭代entries變量,您可以以空安全的方式訪問所有鍵值對。
這比這更簡潔,更不容易出錯:
for (var key in timeSpent.keys) {final value = timeSpent[key]!;print('$key: $value'); }上面的代碼!在讀取值時需要使用斷言運算符 ( ),因為 Dart 不能保證給定鍵的值存在。
8. 使用命名構造函數和初始化列表以獲得更符合人體工程學的 API。
假設您要聲明一個表示溫度值的類。
你可以讓你的類API明確支持兩個攝氏和華氏兩種命名的構造函數:
class Temperature {Temperature.celsius(this.celsius);Temperature.fahrenheit(double fahrenheit): celsius = (fahrenheit - 32) / 1.8;double celsius; }這個類只需要一個存儲變量來表示溫度,并使用初始化列表將華氏溫度轉換為攝氏溫度。
這意味著您可以像這樣聲明溫度值:
final temp1 = Temperature.celsius(30); final temp2 = Temperature.fahrenheit(90);9. getter 和 setter
在Temperature上面的類中,celsius被聲明為存儲變量。
但是用戶可能更喜歡以華氏度獲取或設置溫度。
這可以使用 getter 和 setter 輕松完成,它們允許您定義計算變量。這是更新的課程:
class Temperature {Temperature.celsius(this.celsius);Temperature.fahrenheit(double fahrenheit): celsius = (fahrenheit - 32) / 1.8;double celsius;double get fahrenheit=> celsius * 1.8 + 32;set fahrenheit(double fahrenheit)=> celsius = (fahrenheit - 32) / 1.8; }這使得使用華氏度或攝氏度輕松獲取或設置溫度:
final temp1 = Temperature.celsius(30); print(temp1.fahrenheit); final temp2 = Temperature.fahrenheit(90); temp2.celsius = 28;底線:使用命名構造函數、getter 和 setter 來改進類的設計。
10. 對未使用的函數參數使用下劃線
在 Flutter 中,我們經常使用帶有函數參數的小部件。一個常見的例子是ListView.builder:
class MyListView extends StatelessWidget {@overrideWidget build(BuildContext context) {return ListView.builder(itemBuilder: (context, index) => ListTile(title: Text('all the same'),),itemCount: 10,);} }在這種情況下,我們不使用(context, index)的參數itemBuilder。所以我們可以用下劃線代替它們:
ListView.builder(itemBuilder: (_, __) => ListTile(title: Text('all the same'),),itemCount: 10, )注意:這兩個參數是不同的 (_和__),因為它們是單獨的標識符。
11. 需要一個只能實例化一次的類(又名單例)?使用帶有私有構造函數的靜態實例變量。
單例最重要的特性是整個程序中只能有一個它的實例。這對于建模文件系統之類的東西很有用。
// file_system.dart class FileSystem {FileSystem._();static final instance = FileSystem._(); }要在 Dart 中創建單例,您可以聲明一個命名構造函數并使用_語法將其設為私有。
然后,您可以使用它來創建類的一個靜態最終實例。
因此,其他文件中的任何代碼都只能通過instance變量訪問此類:
// some_other_file.dart final fs = FileSystem.instance; // do something with fs注意:如果您不小心,final可能會導致許多問題。在使用它們之前,請確保您了解它們的缺點。
12. 需要收集獨特的set?使用集合而不是列表。
Dart 中最常用的集合類型是List.
但是列表可以有重復的項目,有時這不是我們想要的:
const citiesList = ['London','Paris','Rome','London', ];我們可以Set在需要一組唯一值時使用 a (請注意 的使用final):
// set is final, compiles final citiesSet = {'London','Paris','Rome','London', // Two elements in a set literal shouldn't be equal };上面的代碼生成一個警告,因為London被包含了兩次。如果我們嘗試對constset執行相同的操作,則會收到錯誤并且我們的代碼無法編譯:
// set is const, doesn't compile const citiesSet = {'London','Paris','Rome','London', // Two elements in a constant set literal can't be equal };當我們與臺合作,我們能夠獲得有用的API,如union,difference和intersection:
citiesSet.union({'Delhi', 'Moscow'}); citiesSet.difference({'London', 'Madrid'}); citiesSet.intersection({'London', 'Berlin'});底線:當你創建一個集合時,問問自己你是否希望它的項目是獨一無二的,并考慮使用一個集合。
13.如何使用try、on、catch、rethrow、finally
try并且catch在使用基于 Future 的 API 時非常理想,如果出現問題,這些 API 可能會引發異常。
這是一個完整的示例,展示了如何充分利用它們:
Future<void> printWeather() async {try {final api = WeatherApiClient();final weather = await api.getWeather('London');print(weather);} on SocketException catch (_) {print('Could not fetch data. Check your connection.');} on WeatherApiException catch (e) {print(e.message);} catch (e, st) {print('Error: $e\nStack trace: $st');rethrow;} finally {print('Done');} }一些注意事項:
- 您可以添加多個on子句來處理不同類型的異常。
- 您可以使用回退catch子句來處理與上述任何類型都不匹配的所有異常。
- 您可以使用rethrow語句將當前異常向上拋出調用堆棧,同時保留堆棧跟蹤。
- 您可以使用finally在Future完成后運行一些代碼,無論它是成功還是失敗。
如果您正在使用或設計一些基于 Future 的 API,請確保根據需要處理異常。
14. 常見的 Future 構造函數
DartFuture類帶有一些方便的工廠構造函數:Future.delayed,Future.value和Future.error。
我們可以Future.delayed用來創建一個Future等待一定延遲的。第二個參數是一個(可選的)匿名函數,你可以用它來完成一個值或拋出一個錯誤:
await Future.delayed(Duration(seconds: 2), () => 'Latte');但有時我們想創建一個Future立即完成的:
await Future.value('Cappuccino'); await Future.error(Exception('Out of milk'));我們可以用Future.value一個值來成功完成,或者Future.error用一個錯誤來完成。
您可以使用這些構造函數來模擬來自基于 Future 的 API 的響應。這在您的測試代碼中編寫模擬類時很有用。
15. 通用流構造器
Stream 類還帶有一些方便的構造函數。以下是最常見的:
Stream.fromIterable([1, 2, 3]); Stream.value(10); Stream.empty(); Stream.error(Exception('something went wrong')); Stream.fromFuture(Future.delayed(Duration(seconds: 1), () => 42)); Stream.periodic(Duration(seconds: 1), (index) => index);- 用于從值列表Stream.fromIterable創建一個Stream。
- 使用Stream.value,如果你只有一個值。
- 用于Stream.empty創建空流。
- 用于Stream.error創建包含錯誤值的流。
- 用于Stream.fromFuture創建僅包含一個值的流,該值將在未來完成時可用。
- 用于Stream.periodic創建周期性的事件流。您可以將 a 指定Duration為事件之間的時間間隔,并指定一個匿名函數來生成給定其在流中的索引的每個值。
16. 同步和異步生成器
在 Dart 中,我們可以將同步生成器定義為一個返回 的函數Iterable:
Iterable<int> count(int n) sync* {for (var i = 1; i <= n; i++) {yield i;} }這使用sync*語法。在函數內部,我們可以“生成”或yield多個值。這些將Iterable在函數完成時返回。
另一方面,異步生成器是一個返回 a 的函數Stream:
Stream<int> countStream(int n) async* {for (var i = 1; i <= n; i++) {yield i;} }這使用此async*語法。在函數內部,我們可以yield像在同步情況下一樣取值。
但是如果我們愿意,我們可以使用await基于 Future 的 API,因為這是一個異步生成器:
Stream<int> countStream(int n) async* {for (var i = 1; i <= n; i++) {// dummy delay - this could be a network requestawait Future.delayed(Duration(seconds: 1));yield i;} }總結
以上是生活随笔為你收集整理的开发中总结的dart相关的技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android - FlutterAct
- 下一篇: Flutter全局悬浮按钮