Dart avanzado
Extensiones
Las extensiones son una forma de agregar funcionalidad a una clase existente. Es una forma de extender una clase sin tener que modificarla. Las extensiones son una forma de agregar métodos a una clase que no controlamos.
class Person {
String name;
String firstName;
Person(this.name, this.firstName);
}
extension PersonExtension on Person {
String get fullName => '$firstName $name';
}
void main() {
Person person = Person('Foo', 'Bar');
print(person.fullName); // Bar Foo
}
class Person {
String name;
String firstName;
Person(this.name, this.firstName);
}
extension PersonExtension on Person {
String get fullName => '$firstName $name';
}
void main() {
Person person = Person('Foo', 'Bar');
print(person.fullName); // Bar Foo
}
Otro ejemplo:
extension StringExtension on String {
String capitalize() {
return "${this[0].toUpperCase()}${this.substring(1)}";
}
}
void main() {
String name = 'foo';
print(name.capitalize()); // Foo
}
extension StringExtension on String {
String capitalize() {
return "${this[0].toUpperCase()}${this.substring(1)}";
}
}
void main() {
String name = 'foo';
print(name.capitalize()); // Foo
}
Future
Un Future
es un objeto que representa un valor potencial, o error, que estará disponible en algún momento en el futuro. Cuando se crea un Future
, se inicia un proceso asíncrono. El proceso puede producir un valor o un error. Cuando el proceso finaliza, el Future
se completa con este valor o error.
Future<String> fetchUserOrder() {
return Future.delayed(Duration(seconds: 2), () => 'Cafe');
}
void main() {
print('Program started');
fetchUserOrder().then((value) => print(value));
print('Program ended');
}
Future<String> fetchUserOrder() {
return Future.delayed(Duration(seconds: 2), () => 'Cafe');
}
void main() {
print('Program started');
fetchUserOrder().then((value) => print(value));
print('Program ended');
}
Program started
Program ended
Cafe
Program started
Program ended
Cafe
async/await
async
y await
son palabras clave que se usan para trabajar con Future
. async
se usa para marcar una función como asíncrona. await
se usa para esperar a que se complete un Future
.
Future<String> fetchUserOrder() {
return Future.delayed(Duration(seconds: 2), () => 'Cafe');
}
Future<String> fetchUserOrder2() {
return Future.delayed(Duration(seconds: 2), () => 'Tea');
}
Future<String> fetchUserOrder3() {
return Future.delayed(Duration(seconds: 2), () => 'Juice');
}
Future<void> main() async {
print('Program started');
print(await fetchUserOrder());
print(await fetchUserOrder2());
print(await fetchUserOrder3());
print('Program ended');
}
Future<String> fetchUserOrder() {
return Future.delayed(Duration(seconds: 2), () => 'Cafe');
}
Future<String> fetchUserOrder2() {
return Future.delayed(Duration(seconds: 2), () => 'Tea');
}
Future<String> fetchUserOrder3() {
return Future.delayed(Duration(seconds: 2), () => 'Juice');
}
Future<void> main() async {
print('Program started');
print(await fetchUserOrder());
print(await fetchUserOrder2());
print(await fetchUserOrder3());
print('Program ended');
}
Program started
Cafe
Tea
Juice
Program ended
Program started
Cafe
Tea
Juice
Program ended
Stream
Un Stream
es una secuencia de eventos asíncronos. Los eventos pueden ser valores o errores. Puede escuchar eventos de un Stream
usando el método listen
. Nunca se puede saber cuántos eventos se emitirán, ni cuándo se emitirán.
En javascript se parece a un Observable
.
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
void main() {
countStream(5).listen((event) {
print(event);
});
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
void main() {
countStream(5).listen((event) {
print(event);
});
}
1
2
3
4
5
1
2
3
4
5
Podemos hacer que un Stream
se emita solo una vez usando el método first
:
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
void main() {
countStream(5).first.then((value) => print(value));
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
void main() {
countStream(5).first.then((value) => print(value));
}
1
1
También que se emita cada "x" tiempo:
Stream<int> countStream() {
return Stream.periodic(const Duration(seconds: 1), (value) {
return 55;
});
}
void main() async {
await for (final value in countStream()){
print(value);
}
print("hi");
}
Stream<int> countStream() {
return Stream.periodic(const Duration(seconds: 1), (value) {
return 55;
});
}
void main() async {
await for (final value in countStream()){
print(value);
}
print("hi");
}
55
55
55
....
55
55
55
....
Nunca imprime "hi" porque el Stream
nunca se completa.
Generadores
Los generadores son funciones que producen una secuencia de valores dinámicamente. Se pueden usar para simplificar el código que usa iteradores.
La diferencia entre sync* y async* es que el primero es síncrono y el segundo asíncrono.
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++; // yield is like return
}
void main() {
print(naturalsTo(5)); // (0, 1, 2, 3, 4)
}
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++; // yield is like return
}
void main() {
print(naturalsTo(5)); // (0, 1, 2, 3, 4)
}
Podemos pensar en un restaurante donde podemos tener 50 clientes, no estará la comida de todos los clientes al mismo tiempo, sino que sé irá preparando la comida de cada cliente a medida que se vaya pidiendo.
Generics
Los genéricos son una forma de hacer que los tipos sean más reutilizables. Los genéricos nos permiten escribir código que se puede reutilizar en diferentes tipos en lugar de escribir el mismo código para cada tipo.
Por ejemplo para evitar:
class PairOfStrings {
String first;
String second;
PairOfStrings(this.first, this.second);
}
class PairOfInts {
int first;
int second;
PairOfInts(this.first, this.second);
}
void main() {
PairOfStrings pairOfStrings = PairOfStrings('Foo', 'Bar');
PairOfInts pairOfInts = PairOfInts(1, 2);
}
class PairOfStrings {
String first;
String second;
PairOfStrings(this.first, this.second);
}
class PairOfInts {
int first;
int second;
PairOfInts(this.first, this.second);
}
void main() {
PairOfStrings pairOfStrings = PairOfStrings('Foo', 'Bar');
PairOfInts pairOfInts = PairOfInts(1, 2);
}
class Pair<A, B> {
A first;
B second;
Pair(this.first, this.second);
}
void main() {
Pair<String, String> names = Pair('Foo', 'Bar');
Pair<int, int> ints = Pair(1, 2);
}
class Pair<A, B> {
A first;
B second;
Pair(this.first, this.second);
}
void main() {
Pair<String, String> names = Pair('Foo', 'Bar');
Pair<int, int> ints = Pair(1, 2);
}