Skip to content

Commit a3174dd

Browse files
feat(flutter): add docs about new switch and pattern matching (#80)
* feat(flutter): add docs about new switch and pattern matching * fix: typos * refactor: incorporate PR comments * fix: missing semicolon Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * refactor: final suggestions --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
1 parent 0adac45 commit a3174dd

1 file changed

Lines changed: 254 additions & 0 deletions

File tree

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
---
2+
title: Dart features
3+
description: Smaczki Dart, które warto znać
4+
---
5+
6+
## Mechanizmy Darta, które warto znać
7+
8+
Dart jest językiem wieloparadygmatowym — pozwala pisać zarówno w stylu imperatywnym, jak i funkcyjnym. Ma wiele przydatnych mechanizmów, o których łatwo zapomnieć, a które mogą ułatwić pisanie prostszego i bardziej niezawodnego kodu. Oto krótkie podsumowanie niektórych ficzerów, o których warto pamiętać.
9+
10+
## New Switch-Case
11+
12+
Dla Darta od `3.0` w górę dostępna jest nowa wersja switch-case'a, która działa jako wyrażenie i wspiera `pattern matching` oraz `guard clauses`. W dokumentacji Dart'a można znaleźć starą składnię switch'a pod nazwą [`switch statement`](https://arc.net/l/quote/ltljrcla) a nową jako [`switch expression`](https://arc.net/l/quote/ofmreksm).
13+
14+
### Porównanie: switch statement
15+
16+
Widzimy tu ciąg imperatywnych instrukcji, który zostanie wykonany, gdy warunek opisany w `case` zostanie spełniony, np.:
17+
18+
```dart
19+
var command = 'OPEN';
20+
switch (command) {
21+
case 'CLOSED':
22+
executeClosed();
23+
case 'PENDING':
24+
executePending();
25+
case 'APPROVED':
26+
executeApproved();
27+
case 'DENIED':
28+
executeDenied();
29+
case 'OPEN':
30+
executeOpen();
31+
default:
32+
executeUnknown();
33+
}
34+
```
35+
36+
Ale co jeśli w wyniku switcha chcemy po prostu przypisać konkretną wartość do danej zmiennej? Korzystając ze `switch statement`, będzie wyglądać to tak:
37+
38+
```dart
39+
// Where slash, star, comma, semicolon, etc., are constant variables...
40+
switch (charCode) {
41+
case slash || star || plus || minus: // Logical-or pattern
42+
token = operator(charCode);
43+
case comma || semicolon: // Logical-or pattern
44+
token = punctuation(charCode);
45+
case >= digit0 && <= digit9: // Relational and logical-and patterns
46+
token = number();
47+
default:
48+
throw FormatException('Invalid');
49+
}
50+
```
51+
52+
De facto, jedyne co robimy za każdym razem, to przypisujemy zmiennej wartość, możnaby więc wykonać to szybciej - z pomocą przychodzi `switch expression`.
53+
54+
### Porównanie: switch expression
55+
56+
Tutaj zaznaczamy, że do zmiennej `token` zostanie przypisane cokolwiek zwróci nam _wyrażenie_ switch, które sprowadza się do:
57+
\[dopasowanie\] => \[zwracana wartość\].
58+
59+
```dart
60+
final token = switch (charCode) {
61+
slash || star || plus || minus => operator(charCode),
62+
comma || semicolon => punctuation(charCode),
63+
>= digit0 && <= digit9 => number(),
64+
_ => throw FormatException('Invalid'),
65+
};
66+
```
67+
68+
Krótsze, a przede wszystkim nie wymusza konieczności ręcznego przypisywania wartości do zmiennej pod każdym `case`.
69+
70+
## Pattern Matching
71+
72+
[`Pattern matching`](https://dart.dev/language/patterns) w Darcie to potężny mechanizm, który pozwala na dopasowywanie wartości według ich struktury, typu czy zawartości. Wprowadzony w Darcie `3.0`, otwiera nowe możliwości pisania czytelnego i bezpiecznego kodu. Oprócz dopasowywania wartości wewnątrz `switch statement` i `switch expression`, pozwala też na `destructuring` - wyciągnięcie konkretnych pól/atrybutów ze złożonych struktur.
73+
74+
### Zalety wykorzystywania pattern matchingu
75+
76+
- Czytelność - kod staje się bardziej deklaratywny i łatwiejszy do zrozumienia.
77+
- Bezpieczeństwo typów - kompilator sprawdza kompletność wzorców.
78+
- Mniej boilerplate - eliminuje potrzebę wielu if-else lub instanceof checks.
79+
- Destructuring - łatwe wyciąganie wartości ze złożonych struktur danych.
80+
- Kompozycja wzorców - możliwość łączenia różnych typów wzorców.
81+
82+
### Wykorzystanie: Pattern matching w switch statement
83+
84+
`Switch statement` wykonuje różne instrukcje w zależności od dopasowanego typu danych:
85+
86+
```dart
87+
switch (value) {
88+
case int i when i > 0:
89+
print('Positive number');
90+
break;
91+
case String s:
92+
print('This is a string: $s');
93+
break;
94+
default:
95+
print('Other type');
96+
}
97+
```
98+
99+
### Wykorzystanie: Pattern matching w switch expression
100+
101+
`Switch expression` zwraca różne wartości w zależności od dopasowanego typu danych:
102+
103+
```dart
104+
String message = switch (value) {
105+
int i when i > 0 => 'Positive number',
106+
String s => 'This is a string: $s',
107+
_ => 'Other type',
108+
};
109+
```
110+
111+
### Wykorzystanie: Pattern matching w destructuringu
112+
113+
`Pattern matching` można wykorzystać aby wyciągnąć konkretne dane ze struktur, np. z list, tupli, map:
114+
115+
```dart
116+
final numList = [1, 2, 3];
117+
// List pattern [a, b, c] destructures the three elements from numList...
118+
final [a, b, c] = numList;
119+
// ...and assigns them to new variables.
120+
print(a + b + c);
121+
```
122+
123+
### Wykorzystanie: Użycie wieloznacznika - wildcard
124+
125+
`Wildcard` lub też wieloznacznik oznaczany jest jako `_` i może pełnić podobną funkcję w `switch expression` co `default` w `switch statement`, np.:
126+
127+
```dart
128+
String stringOrInt(Object value) => switch (value) {
129+
String s => 'This is a string.',
130+
int i => 'This is an int.',
131+
_ => 'Neither string nor int.',
132+
};
133+
```
134+
135+
Dodatkowo, przy destructuringu może oznaczać te elementy, które nas nie interesują, np.:
136+
137+
```dart
138+
final importantList = ['boring', 'important', 'something', 'something more' ];
139+
final [_, i, _, _] = importantList;
140+
print('This one is important: $i');
141+
```
142+
143+
`Wildcard` może być również stosowany wtedy, gdy interesuje nas typ zmiennej, ale nie chcemy jej później wykorzystywać, np.:
144+
145+
```dart
146+
switch (record) {
147+
case (int _, String _):
148+
print('First field is int and second is String.');
149+
}
150+
```
151+
152+
### Rodzaje wzorców
153+
154+
#### Constant patterns
155+
156+
Dopasowują dokładną wartość:
157+
158+
```dart
159+
String describe(int value) => switch (value) {
160+
0 => 'zero',
161+
1 => 'one',
162+
42 => 'the answer',
163+
_ => 'some number',
164+
};
165+
```
166+
167+
#### Variable patterns
168+
169+
Wiążą dopasowaną wartość z nową zmienną:
170+
171+
```dart
172+
String processValue(Object value) => switch (value) {
173+
int x when x > 0 => 'positive: $x',
174+
int x when x < 0 => 'negative: $x',
175+
int x => 'zero: $x',
176+
String s => 'text: $s',
177+
_ => 'unknown type',
178+
};
179+
```
180+
181+
#### Record patterns
182+
183+
Dopasowują rekordy według struktury:
184+
185+
```dart
186+
String analyzePoint((int, int) point) => switch (point) {
187+
(0, 0) => 'origin',
188+
(final x, 0) => 'on x-axis at $x',
189+
(0, final y) => 'on y-axis at $y',
190+
(var x, var y) when x == y => 'diagonal at ($x, $y)',
191+
(var x, var y) => 'point at ($x, $y)',
192+
};
193+
```
194+
195+
:::tip
196+
Jeśli nie podajemy typu zmiennej, to do jej opisania można użyć zarówno final jak i var, w zależności od tego, czy planujemy ją modyfikować.
197+
:::
198+
199+
#### List patterns
200+
201+
Dopasowują listy według zawartości:
202+
203+
```dart
204+
String analyzeList(List<int> numbers) => switch (numbers) {
205+
[] => 'empty list',
206+
[final single] => 'single element: $single',
207+
[final first, final second] => 'two elements: $first, $second',
208+
[final first, ...final rest] => 'starts with $first, has ${rest.length} more',
209+
};
210+
```
211+
212+
#### Map patterns
213+
214+
Dopasowują mapy według kluczy i wartości:
215+
216+
```dart
217+
String analyzeUser(Map<String, dynamic> user) => switch (user) {
218+
{'name': String name, 'age': int age} when age >= 18 => 'Adult: $name ($age)',
219+
{'name': String name, 'age': int age} => 'Minor: $name ($age)',
220+
{'name': String name} => 'Name only: $name',
221+
_ => 'Invalid user data',
222+
};
223+
```
224+
225+
#### Object patterns
226+
227+
Dopasowują obiekty według ich właściwości:
228+
229+
```dart
230+
sealed class Shape {}
231+
class Circle extends Shape {
232+
final double radius;
233+
Circle(this.radius);
234+
}
235+
class Rectangle extends Shape {
236+
final double width, height;
237+
Rectangle(this.width, this.height);
238+
}
239+
240+
double calculateArea(Shape shape) => switch (shape) {
241+
Circle(radius: final r) => 3.14159 * r * r,
242+
Rectangle(width: final w, height: final h) => w * h,
243+
};
244+
```
245+
246+
Jeśli nie ma potrzeby zmieniać nazw pól obiektu w ramach dopasowania, można użyć także takiej notacji:
247+
248+
```dart
249+
double calculateArea(Shape shape) => switch (shape) {
250+
Circle(:final radius) => 3.14159 * radius * radius,
251+
Rectangle(:final width, :final height) => width * height,
252+
};
253+
254+
```

0 commit comments

Comments
 (0)