每一个 Flutter 开发人员都应该知道的 16 个 Dart 技巧和窍门(第三节)

2021年11月24日 阅读数:4
这篇文章主要向大家介绍每一个 Flutter 开发人员都应该知道的 16 个 Dart 技巧和窍门(第三节),主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

上一周给你们带来了flutter系列的第一二期api

Flutter的安装与设置(第一节)安全

35分钟教你学会dart(第二节)
尤为经过第二节的学习,相信你们对dart基础已经有了必定的掌握。markdown

今天特地给你们带来我在开发中总结的dart相关的16个技巧app

1. 你知道吗?Dart 支持字符串乘法。

这是一个简单的程序,显示如何使用字符串乘法打印中国加油,甘肃挺住:less

个人家乡甘肃如今生病了,但我相信他会好起来的!异步

void main() {
  for (var i = 1; i <= 5; i++) {
    print('甘肃挺住,牛肉面加油!' * i);
  }
}
// Output:
甘肃挺住,牛肉面加油!
甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!
甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!
甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!
甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!甘肃挺住,牛肉面加油!

是否是很酷?😉,是的我相信甘肃能够挺住的!async

您可使用它来检查长字符串如何适合Text小部件:ide

Text('你已经屡次喊了加油:' * 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();
// 能够这样使用它:
validator('中国加油');
validator('中国加油,牛肉面挺住');
// 不须要像这样使用它:
validator.call('中国加油,牛肉面挺住');

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('欢迎你');
}

而后sayHi传递给一个welcome函数,该函数接受一个Function参数并使用它来迎接用户。

String Function(String)是一个函数类型,它接受一个String参数并返回一个String. 由于上面的匿名函数具备相同的签名,它能够直接做为参数传递,也能够经过变量传递sayHi


使用功能等运营商时,这种编码风格是常见的mapwherereduce

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

int square(int value) {
  // 只是一个简单的例子
  // 多是一个包含大量代码的复杂函数
  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.dart
class FileSystem {
  FileSystem._();
  static final instance = FileSystem._();
}

要在 Dart 中建立单例,您能够声明一个命名构造函数并使用_语法将其设为私有。

而后,您可使用它来建立类的一个静态最终实例。

所以,其余文件中的任何代码都只能经过instance变量访问此类:

//其余文件的.dart
final fs = FileSystem.instance;
// 用 fs 作点什么

注意:若是您不当心,final可能会致使许多问题。在使用它们以前,请确保您了解它们的缺点。

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

Dart 中最经常使用的集合类型是List.

可是列表能够有重复的项目,有时这不是咱们想要的:

const citiesList = [
  '上海',
  '北京',
  '广东',
  '深圳',
];

咱们能够Set在须要一组惟一值时使用 a (请注意 的使用final):

// set is final, compiles
final citiesSet = {
'深圳',
  '北京',
  '广东',
  '深圳',
};

上面的代码生成一个警告,由于深圳出现了两次。若是咱们尝试对constset执行相同的操做,则会收到错误而且咱们的代码没法编译:

// set is const, doesn't compile
const citiesSet = {
'深圳',
  '北京',
  '广东',
  '深圳', // 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;
  }
}

那么今天的分享就要和你们说再见了,咱们明天见,明天将给你们继续带来flutter的精彩内容。
你们有什么疑问或者问题,均可以在留言区告诉我,若是喜欢今天的内容,欢迎你们点赞支持!