知道这些tips,可以写出更简洁的代码

1.string的乘法
1
2
3
4
5
6
7
8
9
10
11
void main() {
for (var i = 1; i <= 5; i++) {
print('🎄' * i);
}
}
// Output:
// 🎄
// 🎄🎄
// 🎄🎄🎄
// 🎄🎄🎄🎄
// 🎄🎄🎄🎄🎄
2.同步调用Future函数
1
2
3
4
5
6
//演示数据
class CovidAPI {
Future<int> getCases() => Future.value(1000);
Future<int> getRecovered() => Future.value(100);
Future<int> getDeaths() => Future.value(10);
}

使用Future.wait

1
2
3
4
5
6
7
final api = CovidAPI();
final values = await Future.wait([
api.getCases(),
api.getRecovered(),
api.getDeaths(),
]);
print(values); // [1000, 100, 10]
3.类里的call方法
1
2
3
4
5
class PasswordValidator {
bool call(String password) {
return password.length > 10;
}
}

可以像方法一样直接通过实例对象调用,

两种调用方式

1
2
3
4
5
6
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()”调用回调函数避免为空
1
2
3
4
5
6
7
8
9
10
11
//demo类
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) {/*...*/}
}

这是传统写法

1
2
3
4
5
void _dragComplete() {
if (onDragCompleted != null) {
onDragCompleted();
}
}

另一种简便写法,为null就不会调用了,和上面等效

1
2
3
Future<void> _dragComplete() async {
onDragCompleted?.call();
}
5.函数作为参数传递

dart里函数也是一等公民,具有和其他变量同等待遇

1
2
3
4
5
6
7
8
9
10
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');
}
6.容器判断和容器展开

…可以把容器的元素展开,也可以在容器里添加if判断语法

1
2
3
4
5
6
7
8
9
const addRatings = true;
const restaurant = {
'name' : 'Pizza Mario',
'cuisine': 'Italian',
if (addRatings) ...{
'avgRating': 4.3,
'numRatings': 5,
}
};
7.安全遍历map”.entries”
1
2
3
4
for (var entry in timeSpent.entries) {
// do something with keys and values
print('${entry.key}: ${entry.value}');
}

比遍历key安全

1
2
3
4
for (var key in timeSpent.keys) {
final value = timeSpent[key];
print('$key: $value');
}

这个实现如果key对应的value是null,就会报错

8.使用命名构造函数,更具有维护性,可读性
1
2
3
4
5
6
class Temperature {
Temperature.celsius(this.celsius);
Temperature.fahrenheit(double fahrenheit)
: celsius = (fahrenheit - 32) / 1.8;
double celsius;
}
9.Getter和Setter

dart里的getter和setter的语法

1
2
3
4
5
6
7
8
9
10
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;
}
1
2
3
4
final temp1 = Temperature.celsius(30);
print(temp1.fahrenheit);
final temp2 = Temperature.fahrenheit(90);
temp2.celsius = 28;

10.用_下划线代替未使用的参数

1
2
3
4
5
6
7
8
9
10
11
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,可以写成如下

1
2
3
4
5
6
istView.builder(
itemBuilder: (_, __) => ListTile(
title: Text('all the same'),
),
itemCount: 10,
)

_和__代表不同的参数

11.单例实现

dart的单例实现方式

1
2
3
4
5
// file_system.dart
class FileSystem {
FileSystem._();
static final instance = FileSystem._();
}

无法实例化,仅能通过以下方式访问

1
2
3
// some_other_file.dart
final fs = FileSystem.instance;
// do something with fs

12.Set

如果需要一个不包含重复元素的集合,那么使用Set

一个细节,如果使用final 修饰set,有重复元素只会提示warning

1
2
3
4
5
6
7
// set is final, compiles
final citiesSet = {
'London',
'Paris',
'Rome',
'London', // Two elements in a set literal shouldn't be equal
};

如果用const修复,会提示无法编译

1
2
3
4
5
6
7
// set is const, doesn't compile
const citiesSet = {
'London',
'Paris',
'Rome',
'London', // Two elements in a constant set literal can't be equal
};

TODO

15. Common Stream constructors

The Stream class also comes with some handy constructors. Here are the most common ones:

1
2
3
4
5
6
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);
  • use Stream.fromIterable to create a Stream from a list of values.
  • use Stream.value if you have just one value.
  • use Stream.empty to create an empty stream.
  • use Stream.error to create a stream that contains an error value.
  • use Stream.fromFuture to create a stream that will contain only one value, and that value will be available when the future completes.
  • use Stream.periodic to create a periodic stream of events. You can specify a Duration as the time interval between events, and an anonymous function to generate each value given its index in the stream.

16. Sync and Async Generators

In Dart we can define a synchronous generator as a function that returns an Iterable:

1
2
3
4
5
Iterable<int> count(int n) sync* {
for (var i = 1; i <= n; i++) {
yield i;
}
}

This uses the sync* syntax. Inside the function we can “generate” or yield multiple values. These will be returned as an Iterable when the function completes.


On the other hand, an asynchronous generator is a function that returns a Stream:

1
2
3
4
5
Stream<int> countStream(int n) async* {
for (var i = 1; i <= n; i++) {
yield i;
}
}

This uses this async* syntax. Inside the function we can yield values just like in the synchronous case.

But if we want we can await on Future-based APIs, because this is an asynchronous generator:

1
2
3
4
5
6
7
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;
}
}

转自

https://codewithandrea.com/videos/2020-11-16-top-dart-tips-and-tricks-for-flutter-devs/#8-use-named-constructors-and-initializer-lists-for-more-ergonomic-apis