opencode-skills-collection 3.0.49 → 3.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled-skills/.antigravity-install-manifest.json +27 -1
- package/bundled-skills/ab-test-setup/SKILL.md +14 -0
- package/bundled-skills/apple-notes-search/SKILL.md +122 -0
- package/bundled-skills/astropy/references/coordinates.md +9 -0
- package/bundled-skills/astropy/references/cosmology.md +8 -0
- package/bundled-skills/astropy/references/fits.md +8 -0
- package/bundled-skills/astropy/references/tables.md +8 -0
- package/bundled-skills/astropy/references/time.md +7 -0
- package/bundled-skills/astropy/references/units.md +9 -0
- package/bundled-skills/astropy/references/wcs_and_other_modules.md +9 -0
- package/bundled-skills/browser-extension-builder/SKILL.md +1 -1
- package/bundled-skills/ckw-design/SKILL.md +129 -0
- package/bundled-skills/deterministic-design/SKILL.md +56 -0
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/users/bundles.md +1 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/getting-started.md +1 -1
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +4 -4
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/bundled-skills/lint-and-validate/SKILL.md +3 -1
- package/bundled-skills/lookdev/SKILL.md +229 -0
- package/bundled-skills/lookdev-auto/SKILL.md +102 -0
- package/bundled-skills/macos-screen-recorder/SKILL.md +59 -0
- package/bundled-skills/pr-merge-champion/SKILL.md +116 -0
- package/bundled-skills/programmatic-seo/SKILL.md +11 -0
- package/bundled-skills/schema-markup/SKILL.md +11 -0
- package/bundled-skills/screenstudio-alt/SKILL.md +91 -0
- package/bundled-skills/super-code/SKILL.md +209 -0
- package/bundled-skills/super-code/bash/SKILL.md +292 -0
- package/bundled-skills/super-code/c/SKILL.md +263 -0
- package/bundled-skills/super-code/cpp/SKILL.md +271 -0
- package/bundled-skills/super-code/csharp/SKILL.md +276 -0
- package/bundled-skills/super-code/dart/SKILL.md +327 -0
- package/bundled-skills/super-code/elixir/SKILL.md +366 -0
- package/bundled-skills/super-code/go/SKILL.md +234 -0
- package/bundled-skills/super-code/java/SKILL.md +230 -0
- package/bundled-skills/super-code/kotlin/SKILL.md +281 -0
- package/bundled-skills/super-code/php/SKILL.md +316 -0
- package/bundled-skills/super-code/python/SKILL.md +315 -0
- package/bundled-skills/super-code/ruby/SKILL.md +306 -0
- package/bundled-skills/super-code/rust/SKILL.md +289 -0
- package/bundled-skills/super-code/scala/SKILL.md +302 -0
- package/bundled-skills/super-code/swift/SKILL.md +299 -0
- package/bundled-skills/super-code/typescript/SKILL.md +286 -0
- package/bundled-skills/web-media-getter/SKILL.md +119 -0
- package/bundled-skills/youtube-seo-optimizer/SKILL.md +9 -9
- package/package.json +1 -1
- package/skills_index.json +574 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: csharp
|
|
3
|
+
description: "Language-specific super-code guidelines for csharp."
|
|
4
|
+
risk: safe
|
|
5
|
+
source: community
|
|
6
|
+
date_added: "2026-06-16"
|
|
7
|
+
---
|
|
8
|
+
# C#: Idiomatic Efficiency Reference
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
1. [LINQ & Collections](#linq)
|
|
12
|
+
2. [Null Handling](#nulls)
|
|
13
|
+
3. [Async/Await](#async)
|
|
14
|
+
4. [Records & Pattern Matching](#records)
|
|
15
|
+
5. [Error Handling](#errors)
|
|
16
|
+
6. [Resource Management](#resources)
|
|
17
|
+
7. [Anti-patterns specific to C#](#antipatterns)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. LINQ & Collections {#linq}
|
|
22
|
+
|
|
23
|
+
```csharp
|
|
24
|
+
// ❌ Imperative accumulation
|
|
25
|
+
var result = new List<string>();
|
|
26
|
+
foreach (var item in items) {
|
|
27
|
+
if (item.IsActive) result.Add(item.Name.ToUpper());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ✅
|
|
31
|
+
var result = items
|
|
32
|
+
.Where(i => i.IsActive)
|
|
33
|
+
.Select(i => i.Name.ToUpper())
|
|
34
|
+
.ToList();
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```csharp
|
|
38
|
+
// ❌ Manual grouping
|
|
39
|
+
var grouped = new Dictionary<string, List<Item>>();
|
|
40
|
+
foreach (var item in items) {
|
|
41
|
+
if (!grouped.ContainsKey(item.Category))
|
|
42
|
+
grouped[item.Category] = new List<Item>();
|
|
43
|
+
grouped[item.Category].Add(item);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ✅
|
|
47
|
+
var grouped = items.GroupBy(i => i.Category)
|
|
48
|
+
.ToDictionary(g => g.Key, g => g.ToList());
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```csharp
|
|
52
|
+
// ❌ Checking Any() then First()
|
|
53
|
+
if (items.Any(i => i.IsValid)) {
|
|
54
|
+
var first = items.First(i => i.IsValid);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ✅
|
|
58
|
+
var first = items.FirstOrDefault(i => i.IsValid);
|
|
59
|
+
if (first is not null) { ... }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Prefer method syntax for chains of 2+ operations. Query syntax is fine for complex joins.**
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 2. Null Handling {#nulls}
|
|
67
|
+
|
|
68
|
+
```csharp
|
|
69
|
+
// ❌ Nested null checks
|
|
70
|
+
string city = null;
|
|
71
|
+
if (user != null && user.Address != null) {
|
|
72
|
+
city = user.Address.City;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ✅
|
|
76
|
+
var city = user?.Address?.City;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```csharp
|
|
80
|
+
// ❌ Ternary for null fallback
|
|
81
|
+
var name = user != null ? user.Name : "Unknown";
|
|
82
|
+
|
|
83
|
+
// ✅
|
|
84
|
+
var name = user?.Name ?? "Unknown";
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```csharp
|
|
88
|
+
// ❌ Null check before event invocation
|
|
89
|
+
if (OnChanged != null) OnChanged(this, args);
|
|
90
|
+
|
|
91
|
+
// ✅
|
|
92
|
+
OnChanged?.Invoke(this, args);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```csharp
|
|
96
|
+
// ❌ Throwing ArgumentNullException manually
|
|
97
|
+
if (name == null) throw new ArgumentNullException(nameof(name));
|
|
98
|
+
|
|
99
|
+
// ✅ (C# 10+)
|
|
100
|
+
ArgumentNullException.ThrowIfNull(name);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Enable nullable reference types (`<Nullable>enable</Nullable>`) project-wide.**
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 3. Async/Await {#async}
|
|
108
|
+
|
|
109
|
+
```csharp
|
|
110
|
+
// ❌ Blocking on async code
|
|
111
|
+
var result = GetDataAsync().Result; // deadlock risk
|
|
112
|
+
|
|
113
|
+
// ✅
|
|
114
|
+
var result = await GetDataAsync();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```csharp
|
|
118
|
+
// ❌ async void (exceptions are unobservable)
|
|
119
|
+
async void OnButtonClick() { await DoWork(); }
|
|
120
|
+
|
|
121
|
+
// ✅ — async Task; only async void for event handlers that truly require it
|
|
122
|
+
async Task OnButtonClick() { await DoWork(); }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```csharp
|
|
126
|
+
// ❌ Sequential awaits for independent work
|
|
127
|
+
var a = await FetchA();
|
|
128
|
+
var b = await FetchB();
|
|
129
|
+
|
|
130
|
+
// ✅
|
|
131
|
+
var (a, b) = (await Task.WhenAll(FetchA(), FetchB())) switch
|
|
132
|
+
{
|
|
133
|
+
var r => (r[0], r[1])
|
|
134
|
+
};
|
|
135
|
+
// or cleaner with ValueTuple:
|
|
136
|
+
var taskA = FetchA();
|
|
137
|
+
var taskB = FetchB();
|
|
138
|
+
var a = await taskA;
|
|
139
|
+
var b = await taskB;
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```csharp
|
|
143
|
+
// ❌ Wrapping synchronous code in Task.Run inside a library
|
|
144
|
+
public Task<int> GetValue() => Task.Run(() => ComputeSync());
|
|
145
|
+
|
|
146
|
+
// ✅ — let the caller decide; expose sync method
|
|
147
|
+
public int GetValue() => ComputeSync();
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Add `ConfigureAwait(false)` in library code. Omit in app/UI code.**
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 4. Records & Pattern Matching {#records}
|
|
155
|
+
|
|
156
|
+
```csharp
|
|
157
|
+
// ❌ Manual equality, ToString, Deconstruct for data types
|
|
158
|
+
class Point {
|
|
159
|
+
public int X { get; init; }
|
|
160
|
+
public int Y { get; init; }
|
|
161
|
+
// + Equals, GetHashCode, ToString...
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ✅ (C# 9+)
|
|
165
|
+
record Point(int X, int Y);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```csharp
|
|
169
|
+
// ❌ if-else chain for type checking
|
|
170
|
+
if (shape is Circle) {
|
|
171
|
+
var c = (Circle)shape;
|
|
172
|
+
return c.Radius * c.Radius * Math.PI;
|
|
173
|
+
} else if (shape is Rectangle) { ... }
|
|
174
|
+
|
|
175
|
+
// ✅
|
|
176
|
+
return shape switch {
|
|
177
|
+
Circle { Radius: var r } => r * r * Math.PI,
|
|
178
|
+
Rectangle { Width: var w, Height: var h } => w * h,
|
|
179
|
+
_ => throw new ArgumentException($"Unknown shape: {shape}")
|
|
180
|
+
};
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```csharp
|
|
184
|
+
// ❌ Range checking with &&
|
|
185
|
+
if (score >= 0 && score <= 100) { ... }
|
|
186
|
+
|
|
187
|
+
// ✅ (C# 9+)
|
|
188
|
+
if (score is >= 0 and <= 100) { ... }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 5. Error Handling {#errors}
|
|
194
|
+
|
|
195
|
+
```csharp
|
|
196
|
+
// ❌ Catching Exception to log and swallow
|
|
197
|
+
try { Process(); }
|
|
198
|
+
catch (Exception ex) { logger.LogError(ex, "error"); }
|
|
199
|
+
|
|
200
|
+
// ✅ — catch specific, rethrow if you can't handle
|
|
201
|
+
try { Process(); }
|
|
202
|
+
catch (HttpRequestException ex) {
|
|
203
|
+
throw new ServiceException("upstream failure", ex);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```csharp
|
|
208
|
+
// ❌ throw ex (resets stack trace)
|
|
209
|
+
catch (Exception ex) { throw ex; }
|
|
210
|
+
|
|
211
|
+
// ✅
|
|
212
|
+
catch (Exception ex) { throw; } // preserves stack trace
|
|
213
|
+
// or wrap: throw new AppException("context", ex);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
```csharp
|
|
217
|
+
// ❌ Exceptions for flow control
|
|
218
|
+
try { return dict[key]; }
|
|
219
|
+
catch (KeyNotFoundException) { return defaultValue; }
|
|
220
|
+
|
|
221
|
+
// ✅
|
|
222
|
+
return dict.TryGetValue(key, out var value) ? value : defaultValue;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 6. Resource Management {#resources}
|
|
228
|
+
|
|
229
|
+
```csharp
|
|
230
|
+
// ❌ Manual Dispose
|
|
231
|
+
var conn = new SqlConnection(cs);
|
|
232
|
+
conn.Open();
|
|
233
|
+
// ... use conn ...
|
|
234
|
+
conn.Dispose(); // missed on exception
|
|
235
|
+
|
|
236
|
+
// ✅
|
|
237
|
+
using var conn = new SqlConnection(cs);
|
|
238
|
+
conn.Open();
|
|
239
|
+
// disposed at end of scope
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
```csharp
|
|
243
|
+
// ❌ Verbose using block
|
|
244
|
+
using (var reader = new StreamReader(path)) {
|
|
245
|
+
return reader.ReadToEnd();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ✅ (C# 8+)
|
|
249
|
+
using var reader = new StreamReader(path);
|
|
250
|
+
return reader.ReadToEnd();
|
|
251
|
+
// or just: return File.ReadAllText(path);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 7. Anti-patterns specific to C# {#antipatterns}
|
|
257
|
+
|
|
258
|
+
| Anti-pattern | Preferred |
|
|
259
|
+
|---|---|
|
|
260
|
+
| `string.Format("{0}", x)` | `$"{x}"` string interpolation |
|
|
261
|
+
| `List<T>` as public API return | `IReadOnlyList<T>` or `IEnumerable<T>` |
|
|
262
|
+
| `async void` | `async Task` |
|
|
263
|
+
| `.Result` / `.Wait()` on Task | `await` |
|
|
264
|
+
| `throw ex` | `throw` (preserves stack trace) |
|
|
265
|
+
| Manual `IEquatable` on data types | `record` |
|
|
266
|
+
| `object` parameters | generics with constraints |
|
|
267
|
+
| `DateTime.Now` | `DateTime.UtcNow` or `DateTimeOffset` |
|
|
268
|
+
| Mutable public fields | properties with `{ get; set; }` or `{ get; init; }` |
|
|
269
|
+
| `catch (Exception) { }` (swallow all) | catch specific exceptions, rethrow unknown |
|
|
270
|
+
| `IDisposable` without `using` | `using` declaration |
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
## Limitations
|
|
275
|
+
- These are language-specific guidelines and do not cover overall architectural decisions.
|
|
276
|
+
- Over-compression might reduce readability; apply judgement.
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dart
|
|
3
|
+
description: "Language-specific super-code guidelines for dart."
|
|
4
|
+
risk: safe
|
|
5
|
+
source: community
|
|
6
|
+
date_added: "2026-06-16"
|
|
7
|
+
---
|
|
8
|
+
# Dart: Idiomatic Efficiency Reference
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
1. [Null Safety](#nulls)
|
|
12
|
+
2. [Collections & Iteration](#collections)
|
|
13
|
+
3. [Classes & Records](#classes)
|
|
14
|
+
4. [Async/Await & Streams](#async)
|
|
15
|
+
5. [Error Handling](#errors)
|
|
16
|
+
6. [Flutter-Specific Patterns](#flutter)
|
|
17
|
+
7. [Anti-patterns specific to Dart](#antipatterns)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Null Safety {#nulls}
|
|
22
|
+
|
|
23
|
+
```dart
|
|
24
|
+
// ❌ Manual null check
|
|
25
|
+
String display;
|
|
26
|
+
if (user.name != null) {
|
|
27
|
+
display = user.name!;
|
|
28
|
+
} else {
|
|
29
|
+
display = 'Unknown';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ✅
|
|
33
|
+
final display = user.name ?? 'Unknown';
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```dart
|
|
37
|
+
// ❌ Nested null checks
|
|
38
|
+
if (user != null && user.address != null && user.address!.city != null) {
|
|
39
|
+
print(user.address!.city!);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ✅
|
|
43
|
+
final city = user?.address?.city;
|
|
44
|
+
if (city != null) print(city);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```dart
|
|
48
|
+
// ❌ Late field when nullable is correct
|
|
49
|
+
late String name; // crashes if accessed before assignment
|
|
50
|
+
|
|
51
|
+
// ✅ — use late only when you guarantee initialization before access
|
|
52
|
+
String? name; // honestly nullable
|
|
53
|
+
// late is fine for: late final _controller = TextEditingController();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```dart
|
|
57
|
+
// ❌ Bang operator (!) everywhere
|
|
58
|
+
final name = user.name!;
|
|
59
|
+
final city = user.address!.city!;
|
|
60
|
+
|
|
61
|
+
// ✅ — promote through null checks
|
|
62
|
+
final name = user.name;
|
|
63
|
+
if (name == null) return;
|
|
64
|
+
// name is now non-null (promoted)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 2. Collections & Iteration {#collections}
|
|
70
|
+
|
|
71
|
+
```dart
|
|
72
|
+
// ❌ Imperative accumulation
|
|
73
|
+
final result = <String>[];
|
|
74
|
+
for (final item in items) {
|
|
75
|
+
if (item.isActive) result.add(item.name.toUpperCase());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ✅
|
|
79
|
+
final result = items
|
|
80
|
+
.where((i) => i.isActive)
|
|
81
|
+
.map((i) => i.name.toUpperCase())
|
|
82
|
+
.toList();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```dart
|
|
86
|
+
// ❌ Manual map construction
|
|
87
|
+
final map = <String, List<Item>>{};
|
|
88
|
+
for (final item in items) {
|
|
89
|
+
map.putIfAbsent(item.category, () => []).add(item);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ✅ (using collection-if/for in a different way — but groupBy isn't built-in)
|
|
93
|
+
// The loop above is actually idiomatic Dart. Use package:collection for groupBy:
|
|
94
|
+
import 'package:collection/collection.dart';
|
|
95
|
+
final map = groupBy(items, (Item i) => i.category);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```dart
|
|
99
|
+
// ❌ Building list with add() calls
|
|
100
|
+
final widgets = <Widget>[];
|
|
101
|
+
widgets.add(Header());
|
|
102
|
+
if (showSubtitle) widgets.add(Subtitle());
|
|
103
|
+
widgets.add(Body());
|
|
104
|
+
|
|
105
|
+
// ✅ — collection-if
|
|
106
|
+
final widgets = [
|
|
107
|
+
Header(),
|
|
108
|
+
if (showSubtitle) Subtitle(),
|
|
109
|
+
Body(),
|
|
110
|
+
];
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```dart
|
|
114
|
+
// ❌ Spreading manually
|
|
115
|
+
final all = <int>[];
|
|
116
|
+
all.addAll(listA);
|
|
117
|
+
all.addAll(listB);
|
|
118
|
+
|
|
119
|
+
// ✅
|
|
120
|
+
final all = [...listA, ...listB];
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 3. Classes & Records {#classes}
|
|
126
|
+
|
|
127
|
+
```dart
|
|
128
|
+
// ❌ Manual data class boilerplate
|
|
129
|
+
class Point {
|
|
130
|
+
final int x, y;
|
|
131
|
+
const Point(this.x, this.y);
|
|
132
|
+
@override bool operator ==(Object other) => ...
|
|
133
|
+
@override int get hashCode => ...
|
|
134
|
+
@override String toString() => 'Point($x, $y)';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ✅ (Dart 3.0+)
|
|
138
|
+
typedef Point = ({int x, int y});
|
|
139
|
+
// or for named class semantics:
|
|
140
|
+
class Point {
|
|
141
|
+
final int x, y;
|
|
142
|
+
const Point(this.x, this.y);
|
|
143
|
+
}
|
|
144
|
+
// Use package:equatable or Dart records for equality
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```dart
|
|
148
|
+
// ❌ Verbose constructor
|
|
149
|
+
class User {
|
|
150
|
+
final String name;
|
|
151
|
+
final int age;
|
|
152
|
+
User(String name, int age) : name = name, age = age;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ✅ — initializing formals
|
|
156
|
+
class User {
|
|
157
|
+
final String name;
|
|
158
|
+
final int age;
|
|
159
|
+
const User(this.name, this.age);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
```dart
|
|
164
|
+
// ❌ Mutable fields on an immutable object
|
|
165
|
+
class Config {
|
|
166
|
+
String host;
|
|
167
|
+
int port;
|
|
168
|
+
Config(this.host, this.port);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ✅
|
|
172
|
+
class Config {
|
|
173
|
+
final String host;
|
|
174
|
+
final int port;
|
|
175
|
+
const Config(this.host, this.port);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```dart
|
|
180
|
+
// ❌ Switch on type with if-else chain
|
|
181
|
+
if (shape is Circle) {
|
|
182
|
+
return (shape as Circle).radius * pi;
|
|
183
|
+
} else if (shape is Rectangle) { ... }
|
|
184
|
+
|
|
185
|
+
// ✅ (Dart 3.0+)
|
|
186
|
+
return switch (shape) {
|
|
187
|
+
Circle(:final radius) => radius * radius * pi,
|
|
188
|
+
Rectangle(:final width, :final height) => width * height,
|
|
189
|
+
};
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 4. Async/Await & Streams {#async}
|
|
195
|
+
|
|
196
|
+
```dart
|
|
197
|
+
// ❌ .then() chains
|
|
198
|
+
fetchUser()
|
|
199
|
+
.then((user) => fetchPosts(user))
|
|
200
|
+
.then((posts) => display(posts))
|
|
201
|
+
.catchError((e) => log(e));
|
|
202
|
+
|
|
203
|
+
// ✅
|
|
204
|
+
try {
|
|
205
|
+
final user = await fetchUser();
|
|
206
|
+
final posts = await fetchPosts(user);
|
|
207
|
+
display(posts);
|
|
208
|
+
} catch (e) {
|
|
209
|
+
log(e);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```dart
|
|
214
|
+
// ❌ Sequential await for independent work
|
|
215
|
+
final a = await fetchA();
|
|
216
|
+
final b = await fetchB();
|
|
217
|
+
|
|
218
|
+
// ✅
|
|
219
|
+
final results = await Future.wait([fetchA(), fetchB()]);
|
|
220
|
+
// or with typed destructuring:
|
|
221
|
+
final (a, b) = await (fetchA(), fetchB()).wait; // Dart 3.0+ record
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```dart
|
|
225
|
+
// ❌ StreamBuilder doing too much in build
|
|
226
|
+
StreamBuilder(
|
|
227
|
+
stream: stream,
|
|
228
|
+
builder: (ctx, snap) {
|
|
229
|
+
if (snap.hasError) return Error();
|
|
230
|
+
if (!snap.hasData) return Loading();
|
|
231
|
+
final data = snap.data!;
|
|
232
|
+
// 50 lines of widget tree...
|
|
233
|
+
},
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
// ✅ — extract widget, or use listen + setState for simple cases
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 5. Error Handling {#errors}
|
|
242
|
+
|
|
243
|
+
```dart
|
|
244
|
+
// ❌ Catching Exception (too broad)
|
|
245
|
+
try { process(); }
|
|
246
|
+
on Exception catch (e) { print(e); }
|
|
247
|
+
|
|
248
|
+
// ✅ — catch specific types
|
|
249
|
+
try {
|
|
250
|
+
process();
|
|
251
|
+
} on FormatException catch (e) {
|
|
252
|
+
throw AppException('Invalid format', cause: e);
|
|
253
|
+
} on HttpException catch (e) {
|
|
254
|
+
throw AppException('Network error', cause: e);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
```dart
|
|
259
|
+
// ❌ Returning null for errors
|
|
260
|
+
Future<User?> fetchUser() async {
|
|
261
|
+
try { return await api.getUser(); }
|
|
262
|
+
catch (_) { return null; } // caller doesn't know why
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ✅ — let exceptions propagate, or use sealed Result type
|
|
266
|
+
sealed class Result<T> {}
|
|
267
|
+
class Success<T> extends Result<T> { final T value; Success(this.value); }
|
|
268
|
+
class Failure<T> extends Result<T> { final Object error; Failure(this.error); }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 6. Flutter-Specific Patterns {#flutter}
|
|
274
|
+
|
|
275
|
+
```dart
|
|
276
|
+
// ❌ Rebuilding entire tree on state change
|
|
277
|
+
setState(() {
|
|
278
|
+
// changes a single value, but the build() method builds 200 widgets
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// ✅ — extract subtrees into separate widgets or use ValueListenableBuilder
|
|
282
|
+
ValueListenableBuilder<int>(
|
|
283
|
+
valueListenable: counter,
|
|
284
|
+
builder: (_, value, __) => Text('$value'),
|
|
285
|
+
)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
```dart
|
|
289
|
+
// ❌ const-able widget without const
|
|
290
|
+
Container(color: Colors.blue)
|
|
291
|
+
|
|
292
|
+
// ✅
|
|
293
|
+
const ColoredBox(color: Colors.blue)
|
|
294
|
+
// Mark constructors const when possible; use `const` keyword at call site
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
```dart
|
|
298
|
+
// ❌ Navigator.push with MaterialPageRoute everywhere
|
|
299
|
+
Navigator.push(context, MaterialPageRoute(builder: (_) => DetailPage()));
|
|
300
|
+
|
|
301
|
+
// ✅ — named routes or GoRouter
|
|
302
|
+
context.go('/detail');
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## 7. Anti-patterns specific to Dart {#antipatterns}
|
|
308
|
+
|
|
309
|
+
| Anti-pattern | Preferred |
|
|
310
|
+
|---|---|
|
|
311
|
+
| `!` (bang) operator liberally | null checks and promotion |
|
|
312
|
+
| `dynamic` everywhere | proper types |
|
|
313
|
+
| `as` cast without check | pattern matching or `is` check |
|
|
314
|
+
| Mutable fields on value objects | `final` fields |
|
|
315
|
+
| `print()` for logging | `package:logging` or structured logger |
|
|
316
|
+
| Manual `==`/`hashCode` | records, equatable, or code generation |
|
|
317
|
+
| `setState` for complex state | Riverpod / Bloc / Provider |
|
|
318
|
+
| Deep widget nesting | extract widgets into classes |
|
|
319
|
+
| String-based routing | typed routing (GoRouter) |
|
|
320
|
+
| `late` as escape hatch | nullable types or proper initialization |
|
|
321
|
+
| `Future.delayed` for debounce | `Timer` or proper debounce utility |
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
## Limitations
|
|
326
|
+
- These are language-specific guidelines and do not cover overall architectural decisions.
|
|
327
|
+
- Over-compression might reduce readability; apply judgement.
|