特意给大家带来我在开发中总结的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 classclass 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  }  @override  Widget 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


应用性能等运营商时,这种编码格调是常见的mapwherereduce

例如,这是一个计算数字平方的简略函数:

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作为参数传递,因为它的签名正是 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,只增加avgRatingnumRatings键值对,如果addRatingstrue。因为咱们要增加多个键值对,所以咱们须要应用扩大运算符 ( ...)。

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 values  print('${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 {  @override  Widget 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.dartclass FileSystem {  FileSystem._();  static final instance = FileSystem._();}

要在 Dart 中创立单例,您能够申明一个命名构造函数并应用_语法将其设为公有。

而后,您能够应用它来创立类的一个动态最终实例。

因而,其余文件中的任何代码都只能通过instance变量拜访此类:

// some_other_file.dartfinal fs = FileSystem.instance;// do something with fs

留神:如果您不小心,final可能会导致许多问题。在应用它们之前,请确保您理解它们的毛病。

12. 须要收集独特的set?应用汇合而不是列表。

Dart 中最罕用的汇合类型是List.

然而列表能够有反复的我的项目,有时这不是咱们想要的:

const citiesList = [  'London',  'Paris',  'Rome',  'London',];

咱们能够Set在须要一组惟一值时应用 a (请留神 的应用final):

// set is final, compilesfinal citiesSet = {  'London',  'Paris',  'Rome',  'London', // Two elements in a set literal shouldn't be equal};

下面的代码生成一个正告,因为London被蕴含了两次。如果咱们尝试对constset执行雷同的操作,则会收到谬误并且咱们的代码无奈编译:

// set is const, doesn't compileconst citiesSet = {  'London',  'Paris',  'Rome',  'London', // Two elements in a constant set literal can't be equal};

当咱们与台单干,咱们可能取得有用的API,如uniondifferenceintersection

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语句将以后异样向上抛出调用堆栈,同时保留堆栈跟踪
  • 您能够应用finallyFuture实现后运行一些代码,无论它是胜利还是失败。

如果您正在应用或设计一些基于 Future 的 API,请确保依据须要解决异样。

14. 常见的 Future 构造函数

DartFuture类带有一些不便的工厂构造函数:Future.delayed,Future.valueFuture.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 request    await Future.delayed(Duration(seconds: 1));    yield i;  }}