特意給大家?guī)砦以陂_發(fā)中總結(jié)的dart相關(guān)的技巧
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.需要同時執(zhí)行多個Future嗎炫加?使用 Future.wait。
考慮這個模擬 API 類铺然,它告訴我們最新的 COVID 病例數(shù):
// Mock API class
class CovidAPI {
Future<int> getCases() => Future.value(1000);
Future<int> getRecovered() => Future.value(100);
Future<int> getDeaths() => Future.value(10);
}
要同時執(zhí)行所有這些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 類中實現(xiàn)“調(diào)用”方法,使它們像函數(shù)一樣可調(diào)用魄健。
這是一個示例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. 需要調(diào)用回調(diào)但前提是它不為空?使用“?.call()”語法沽瘦。
假設(shè)我們有一個自定義小部件類革骨,它應(yīng)該onDragCompleted
在發(fā)生特定事件時調(diào)用回調(diào):
class CustomDraggable extends StatelessWidget {
const CustomDraggable({Key key, this.onDragCompleted}) : super(key: key);
final VoidCallback? onDragCompleted;
void _dragComplete() {
// TODO: Implement me
}
@override
Widget build(BuildContext context) {/*...*/}
}
要調(diào)用回調(diào),我們可以編寫以下代碼:
void _dragComplete() {
if (onDragCompleted != null) {
onDragCompleted();
}
}
但是有一個更簡單的方法(注意使用?.
):
Future<void> _dragComplete() async {
onDragCompleted?.call();
}
5. 使用匿名函數(shù)和函數(shù)作為參數(shù)
在 Dart 中其垄,函數(shù)是一等公民,可以作為參數(shù)傳遞給其他函數(shù)卤橄。
下面是一些定義匿名函數(shù)并將其分配給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
函數(shù)寝受,該函數(shù)接受一個Function
參數(shù)并使用它來迎接用戶侦副。
String Function(String)
是一個函數(shù)類型,它接受一個String
參數(shù)并返回一個String
. 因為上面的匿名函數(shù)具有相同的簽名寸齐,它可以直接作為參數(shù)傳遞,也可以通過變量傳遞sayHi
婉刀。
使用功能等運營商時,這種編碼風格是常見的map
,where
和reduce
蔫浆。
例如,這是一個計算數(shù)字平方的簡單函數(shù):
int square(int value) {
// just a simple example
// could be a complex function with a lot of code
return value * value;
}
給定一個值列表姐叁,我們可以映射它們以獲得平方:
const values = [1, 2, 3];
values.map(square).toList();
這里我們square
作為參數(shù)傳遞瓦盛,因為它的簽名正是 map 操作符所期望的。這意味著我們不需要用匿名函數(shù)擴展它:
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,
}
};
這里我們聲明一個restaurant
maps处窥,只添加avgRating
和numRatings
鍵值對嘱吗,如果addRatings
是true
。因為我們要添加多個鍵值對滔驾,所以我們需要使用擴展運算符 ( ...
)谒麦。
7. 需要以空安全的方式遍歷map嗎?使用.entries
:
假設(shè)你有map:
const timeSpent = <String, double>{
'Blogging': 10.5,
'YouTube': 30.5,
'Courses': 75.2,
};
以下是如何編寫循環(huán)以使用所有鍵值對運行一些代碼:
for (var entry in timeSpent.entries) {
// do something with keys and values
print('${entry.key}: ${entry.value}');
}
通過迭代entries
變量哆致,您可以以空安全的方式訪問所有鍵值對绕德。
這比這更簡潔,更不容易出錯:
for (var key in timeSpent.keys) {
final value = timeSpent[key]!;
print('$key: $value');
}
上面的代碼!
在讀取值時需要使用斷言運算符 ( )沽瞭,因為 Dart 不能保證給定鍵的值存在迁匠。
8. 使用命名構(gòu)造函數(shù)和初始化列表以獲得更符合人體工程學的 API。
假設(shè)您要聲明一個表示溫度值的類驹溃。
你可以讓你的類API明確支持兩個攝氏和華氏兩種命名的構(gòu)造函數(shù):
class Temperature {
Temperature.celsius(this.celsius);
Temperature.fahrenheit(double fahrenheit)
: celsius = (fahrenheit - 32) / 1.8;
double celsius;
}
這個類只需要一個存儲變量來表示溫度城丧,并使用初始化列表將華氏溫度轉(zhuǎn)換為攝氏溫度。
這意味著您可以像這樣聲明溫度值:
final temp1 = Temperature.celsius(30);
final temp2 = Temperature.fahrenheit(90);
9. getter 和 setter
在Temperature
上面的類中豌鹤,celsius
被聲明為存儲變量亡哄。
但是用戶可能更喜歡以華氏度獲取或設(shè)置溫度。
這可以使用 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;
}
這使得使用華氏度或攝氏度輕松獲取或設(shè)置溫度:
final temp1 = Temperature.celsius(30);
print(temp1.fahrenheit);
final temp2 = Temperature.fahrenheit(90);
temp2.celsius = 28;
底線:使用命名構(gòu)造函數(shù)、getter 和 setter 來改進類的設(shè)計灵临。
10. 對未使用的函數(shù)參數(shù)使用下劃線
在 Flutter 中截型,我們經(jīng)常使用帶有函數(shù)參數(shù)的小部件。一個常見的例子是ListView.builder
:
class MyListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) => ListTile(
title: Text('all the same'),
),
itemCount: 10,
);
}
}
在這種情況下儒溉,我們不使用(context, index)
的參數(shù)itemBuilder
宦焦。所以我們可以用下劃線代替它們:
ListView.builder(
itemBuilder: (_, __) => ListTile(
title: Text('all the same'),
),
itemCount: 10,
)
注意:這兩個參數(shù)是不同的 (_
和__
),因為它們是單獨的標識符。
11. 需要一個只能實例化一次的類(又名單例)波闹?使用帶有私有構(gòu)造函數(shù)的靜態(tài)實例變量酝豪。
單例最重要的特性是整個程序中只能有一個它的實例。這對于建模文件系統(tǒng)之類的東西很有用精堕。
// file_system.dart
class FileSystem {
FileSystem._();
static final instance = FileSystem._();
}
要在 Dart 中創(chuàng)建單例孵淘,您可以聲明一個命名構(gòu)造函數(shù)并使用_
語法將其設(shè)為私有。
然后歹篓,您可以使用它來創(chuàng)建類的一個靜態(tài)最終實例瘫证。
因此,其他文件中的任何代碼都只能通過instance
變量訪問此類:
// some_other_file.dart
final fs = FileSystem.instance;
// do something with fs
注意:如果您不小心滋捶,final可能會導(dǎo)致許多問題痛悯。在使用它們之前,請確保您了解它們的缺點重窟。
12. 需要收集獨特的set载萌?使用集合而不是列表。
Dart 中最常用的集合類型是List
.
但是列表可以有重復(fù)的項目巡扇,有時這不是我們想要的:
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
被包含了兩次。如果我們嘗試對const
set執(zhí)行相同的操作厅翔,則會收到錯誤并且我們的代碼無法編譯:
// 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'});
底線:當你創(chuàng)建一個集合時熊泵,問問自己你是否希望它的項目是獨一無二的,并考慮使用一個集合甸昏。
13.如何使用try顽分、on、catch施蜜、rethrow卒蘸、finally
try
并且catch
在使用基于 Future 的 API 時非常理想,如果出現(xiàn)問題翻默,這些 API 可能會引發(fā)異常缸沃。
這是一個完整的示例,展示了如何充分利用它們:
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
語句將當前異常向上拋出調(diào)用堆棧,同時保留堆棧跟蹤肯污。 - 您可以使用
finally
在Future
完成后運行一些代碼翘单,無論它是成功還是失敗梯皿。
如果您正在使用或設(shè)計一些基于 Future 的 API,請確保根據(jù)需要處理異常。
14. 常見的 Future 構(gòu)造函數(shù)
DartFuture
類帶有一些方便的工廠構(gòu)造函數(shù):Future.delayed
,Future.value
和Future.error
剂桥。
我們可以Future.delayed
用來創(chuàng)建一個Future
等待一定延遲的忠烛。第二個參數(shù)是一個(可選的)匿名函數(shù),你可以用它來完成一個值或拋出一個錯誤:
await Future.delayed(Duration(seconds: 2), () => 'Latte');
但有時我們想創(chuàng)建一個Future
立即完成的:
await Future.value('Cappuccino');
await Future.error(Exception('Out of milk'));
我們可以用Future.value
一個值來成功完成美尸,或者Future.error
用一個錯誤來完成师坎。
您可以使用這些構(gòu)造函數(shù)來模擬來自基于 Future 的 API 的響應(yīng)。這在您的測試代碼中編寫模擬類時很有用堪滨。
15. 通用流構(gòu)造器
Stream 類還帶有一些方便的構(gòu)造函數(shù)胯陋。以下是最常見的:
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
創(chuàng)建一個Stream
。 - 使用
Stream.value
袱箱,如果你只有一個值遏乔。 - 用于
Stream.empty
創(chuàng)建空流。 - 用于
Stream.error
創(chuàng)建包含錯誤值的流发笔。 - 用于
Stream.fromFuture
創(chuàng)建僅包含一個值的流盟萨,該值將在未來完成時可用。 - 用于
Stream.periodic
創(chuàng)建周期性的事件流了讨。您可以將 a 指定Duration
為事件之間的時間間隔捻激,并指定一個匿名函數(shù)來生成給定其在流中的索引的每個值。
16. 同步和異步生成器
在 Dart 中前计,我們可以將同步生成器定義為一個返回 的函數(shù)Iterable
:
Iterable<int> count(int n) sync* {
for (var i = 1; i <= n; i++) {
yield i;
}
}
這使用sync*
語法胞谭。在函數(shù)內(nèi)部,我們可以“生成”或yield
多個值残炮。這些將Iterable
在函數(shù)完成時返回韭赘。
另一方面,異步生成器是一個返回 a 的函數(shù)Stream
:
Stream<int> countStream(int n) async* {
for (var i = 1; i <= n; i++) {
yield i;
}
}
這使用此async*
語法势就。在函數(shù)內(nèi)部泉瞻,我們可以yield
像在同步情況下一樣取值。
但是如果我們愿意苞冯,我們可以使用await
基于 Future 的 API袖牙,因為這是一個異步生成器:
Stream<int> countStream(int n) async* {
for (var i = 1; i <= n; i++) {
// dummy delay - this could be a network request
await Future.delayed(Duration(seconds: 1));
yield i;
}
}