@aicgen/aicgen 1.0.0-beta.1 → 1.0.0
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/{.claude/guidelines → .agent/rules}/api-design.md +5 -1
- package/{.claude/guidelines → .agent/rules}/architecture.md +5 -1
- package/{.claude/guidelines → .agent/rules}/best-practices.md +5 -1
- package/{.claude/guidelines → .agent/rules}/code-style.md +5 -1
- package/{.claude/guidelines → .agent/rules}/design-patterns.md +5 -1
- package/{.claude/guidelines → .agent/rules}/devops.md +5 -1
- package/{.claude/guidelines → .agent/rules}/error-handling.md +5 -1
- package/.agent/rules/instructions.md +28 -0
- package/{.claude/guidelines → .agent/rules}/language.md +5 -1
- package/{.claude/guidelines → .agent/rules}/performance.md +5 -1
- package/{.claude/guidelines → .agent/rules}/security.md +5 -1
- package/{.claude/guidelines → .agent/rules}/testing.md +5 -1
- package/.agent/workflows/add-documentation.md +10 -0
- package/.agent/workflows/generate-integration-tests.md +10 -0
- package/.agent/workflows/generate-unit-tests.md +11 -0
- package/.agent/workflows/performance-audit.md +11 -0
- package/.agent/workflows/refactor-extract-module.md +12 -0
- package/.agent/workflows/security-audit.md +12 -0
- package/.gemini/instructions.md +4843 -0
- package/.vs/ProjectSettings.json +2 -2
- package/.vs/VSWorkspaceState.json +15 -15
- package/.vs/aicgen.slnx/v18/DocumentLayout.json +53 -53
- package/AGENTS.md +9 -11
- package/assets/icon.svg +33 -33
- package/bun.lock +734 -26
- package/{CLAUDE.md → claude.md} +2 -2
- package/config.example.yml +129 -0
- package/config.yml +38 -0
- package/data/architecture/microservices/api-gateway.md +56 -56
- package/data/devops/observability.md +73 -73
- package/data/guideline-mappings.yml +128 -0
- package/data/language/dart/async.md +289 -0
- package/data/language/dart/basics.md +280 -0
- package/data/language/dart/error-handling.md +355 -0
- package/data/language/dart/index.md +10 -0
- package/data/language/dart/testing.md +352 -0
- package/data/language/swift/basics.md +477 -0
- package/data/language/swift/concurrency.md +654 -0
- package/data/language/swift/error-handling.md +679 -0
- package/data/language/swift/swiftui-mvvm.md +795 -0
- package/data/language/swift/testing.md +708 -0
- package/data/version.json +10 -8
- package/dist/index.js +50153 -28959
- package/jest.config.js +46 -0
- package/package.json +14 -3
- package/.claude/agents/architecture-reviewer.md +0 -88
- package/.claude/agents/guideline-checker.md +0 -73
- package/.claude/agents/security-auditor.md +0 -108
- package/.claude/settings.json +0 -98
- package/.claude/settings.local.json +0 -8
- package/.eslintrc.json +0 -28
- package/.github/workflows/release.yml +0 -180
- package/.github/workflows/test.yml +0 -81
- package/CONTRIBUTING.md +0 -821
- package/dist/commands/init.d.ts +0 -8
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -46
- package/dist/commands/init.js.map +0 -1
- package/dist/config/profiles.d.ts +0 -4
- package/dist/config/profiles.d.ts.map +0 -1
- package/dist/config/profiles.js +0 -30
- package/dist/config/profiles.js.map +0 -1
- package/dist/config/settings.d.ts +0 -7
- package/dist/config/settings.d.ts.map +0 -1
- package/dist/config/settings.js +0 -7
- package/dist/config/settings.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/models/guideline.d.ts +0 -15
- package/dist/models/guideline.d.ts.map +0 -1
- package/dist/models/guideline.js +0 -2
- package/dist/models/guideline.js.map +0 -1
- package/dist/models/preference.d.ts +0 -9
- package/dist/models/preference.d.ts.map +0 -1
- package/dist/models/preference.js +0 -2
- package/dist/models/preference.js.map +0 -1
- package/dist/models/profile.d.ts +0 -9
- package/dist/models/profile.d.ts.map +0 -1
- package/dist/models/profile.js +0 -2
- package/dist/models/profile.js.map +0 -1
- package/dist/models/project.d.ts +0 -13
- package/dist/models/project.d.ts.map +0 -1
- package/dist/models/project.js +0 -2
- package/dist/models/project.js.map +0 -1
- package/dist/services/ai/anthropic.d.ts +0 -7
- package/dist/services/ai/anthropic.d.ts.map +0 -1
- package/dist/services/ai/anthropic.js +0 -39
- package/dist/services/ai/anthropic.js.map +0 -1
- package/dist/services/generator.d.ts +0 -2
- package/dist/services/generator.d.ts.map +0 -1
- package/dist/services/generator.js +0 -4
- package/dist/services/generator.js.map +0 -1
- package/dist/services/learner.d.ts +0 -2
- package/dist/services/learner.d.ts.map +0 -1
- package/dist/services/learner.js +0 -4
- package/dist/services/learner.js.map +0 -1
- package/dist/services/scanner.d.ts +0 -3
- package/dist/services/scanner.d.ts.map +0 -1
- package/dist/services/scanner.js +0 -54
- package/dist/services/scanner.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -15
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -27
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/file.d.ts +0 -7
- package/dist/utils/file.d.ts.map +0 -1
- package/dist/utils/file.js +0 -32
- package/dist/utils/file.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -6
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -17
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/path.d.ts +0 -6
- package/dist/utils/path.d.ts.map +0 -1
- package/dist/utils/path.js +0 -14
- package/dist/utils/path.js.map +0 -1
- package/docs/planning/memory-lane.md +0 -83
- package/packaging/linux/aicgen.spec +0 -23
- package/packaging/linux/control +0 -9
- package/packaging/macos/scripts/postinstall +0 -12
- package/packaging/windows/setup.nsi +0 -92
- package/scripts/add-categories.ts +0 -87
- package/scripts/build-binary.ts +0 -46
- package/scripts/embed-data.ts +0 -105
- package/scripts/generate-version.ts +0 -150
- package/scripts/test-decompress.ts +0 -27
- package/scripts/test-extract.ts +0 -31
- package/src/__tests__/services/assistant-file-writer.test.ts +0 -400
- package/src/__tests__/services/guideline-loader.test.ts +0 -281
- package/src/__tests__/services/tarball-extraction.test.ts +0 -125
- package/src/commands/add-guideline.ts +0 -296
- package/src/commands/clear.ts +0 -61
- package/src/commands/guideline-selector.ts +0 -123
- package/src/commands/init.ts +0 -645
- package/src/commands/quick-add.ts +0 -586
- package/src/commands/remove-guideline.ts +0 -152
- package/src/commands/stats.ts +0 -49
- package/src/commands/update.ts +0 -240
- package/src/config.ts +0 -82
- package/src/embedded-data.ts +0 -1492
- package/src/index.ts +0 -67
- package/src/models/profile.ts +0 -24
- package/src/models/project.ts +0 -43
- package/src/services/assistant-file-writer.ts +0 -612
- package/src/services/config-generator.ts +0 -150
- package/src/services/config-manager.ts +0 -70
- package/src/services/data-source.ts +0 -248
- package/src/services/first-run-init.ts +0 -148
- package/src/services/guideline-loader.ts +0 -311
- package/src/services/hook-generator.ts +0 -178
- package/src/services/subagent-generator.ts +0 -310
- package/src/utils/banner.ts +0 -66
- package/src/utils/errors.ts +0 -27
- package/src/utils/file.ts +0 -67
- package/src/utils/formatting.ts +0 -172
- package/src/utils/logger.ts +0 -89
- package/src/utils/path.ts +0 -17
- package/src/utils/wizard-state.ts +0 -132
- package/tsconfig.json +0 -25
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# Async Programming in Dart
|
|
2
|
+
|
|
3
|
+
## Futures and async/await
|
|
4
|
+
|
|
5
|
+
```dart
|
|
6
|
+
// ✅ Async function returns Future
|
|
7
|
+
Future<User> fetchUser(String id) async {
|
|
8
|
+
final response = await http.get('/api/users/$id');
|
|
9
|
+
return User.fromJson(response.data);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// ✅ Handle errors with try-catch
|
|
13
|
+
Future<User> fetchUserSafely(String id) async {
|
|
14
|
+
try {
|
|
15
|
+
final response = await http.get('/api/users/$id');
|
|
16
|
+
return User.fromJson(response.data);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
print('Failed to fetch user: $e');
|
|
19
|
+
rethrow;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ❌ Don't forget await
|
|
24
|
+
Future<void> badExample() async {
|
|
25
|
+
final user = fetchUser('123'); // Missing await!
|
|
26
|
+
print(user); // Prints: Instance of 'Future<User>'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ✅ Correct
|
|
30
|
+
Future<void> goodExample() async {
|
|
31
|
+
final user = await fetchUser('123');
|
|
32
|
+
print(user.name); // Prints: Alice
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Parallel Execution
|
|
37
|
+
|
|
38
|
+
```dart
|
|
39
|
+
// ❌ Sequential - slow
|
|
40
|
+
Future<void> sequential() async {
|
|
41
|
+
final user = await fetchUser('1'); // 100ms
|
|
42
|
+
final posts = await fetchPosts('1'); // 150ms
|
|
43
|
+
final comments = await fetchComments('1'); // 120ms
|
|
44
|
+
// Total: 370ms
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ✅ Parallel - fast
|
|
48
|
+
Future<void> parallel() async {
|
|
49
|
+
final results = await Future.wait([
|
|
50
|
+
fetchUser('1'),
|
|
51
|
+
fetchPosts('1'),
|
|
52
|
+
fetchComments('1'),
|
|
53
|
+
]);
|
|
54
|
+
// Total: 150ms (longest operation)
|
|
55
|
+
|
|
56
|
+
final user = results[0] as User;
|
|
57
|
+
final posts = results[1] as List<Post>;
|
|
58
|
+
final comments = results[2] as List<Comment>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ✅ With better typing using records (Dart 3.0+)
|
|
62
|
+
Future<(User, List<Post>, List<Comment>)> fetchUserData(String id) async {
|
|
63
|
+
final (user, posts, comments) = await (
|
|
64
|
+
fetchUser(id),
|
|
65
|
+
fetchPosts(id),
|
|
66
|
+
fetchComments(id),
|
|
67
|
+
).wait;
|
|
68
|
+
|
|
69
|
+
return (user, posts, comments);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Future Methods
|
|
74
|
+
|
|
75
|
+
```dart
|
|
76
|
+
// timeout - fail if takes too long
|
|
77
|
+
Future<User> fetchWithTimeout(String id) async {
|
|
78
|
+
return fetchUser(id).timeout(
|
|
79
|
+
const Duration(seconds: 5),
|
|
80
|
+
onTimeout: () => throw TimeoutException('Request timed out'),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// catchError - handle errors without try-catch
|
|
85
|
+
final user = await fetchUser('123').catchError((error) {
|
|
86
|
+
print('Error: $error');
|
|
87
|
+
return User.guest(); // Fallback value
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// then - chain operations (prefer async/await)
|
|
91
|
+
fetchUser('123')
|
|
92
|
+
.then((user) => user.name)
|
|
93
|
+
.then((name) => print(name));
|
|
94
|
+
|
|
95
|
+
// whenComplete - always runs (like finally)
|
|
96
|
+
await fetchUser('123').whenComplete(() {
|
|
97
|
+
print('Request completed');
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Streams
|
|
102
|
+
|
|
103
|
+
```dart
|
|
104
|
+
// Create stream
|
|
105
|
+
Stream<int> countStream(int max) async* {
|
|
106
|
+
for (int i = 1; i <= max; i++) {
|
|
107
|
+
await Future.delayed(const Duration(seconds: 1));
|
|
108
|
+
yield i;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ✅ Listen to stream
|
|
113
|
+
final subscription = countStream(5).listen(
|
|
114
|
+
(count) {
|
|
115
|
+
print('Count: $count');
|
|
116
|
+
},
|
|
117
|
+
onError: (error) {
|
|
118
|
+
print('Error: $error');
|
|
119
|
+
},
|
|
120
|
+
onDone: () {
|
|
121
|
+
print('Stream completed');
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Cancel subscription
|
|
126
|
+
await subscription.cancel();
|
|
127
|
+
|
|
128
|
+
// ✅ Async for loop
|
|
129
|
+
Future<void> processStream() async {
|
|
130
|
+
await for (final count in countStream(5)) {
|
|
131
|
+
print('Count: $count');
|
|
132
|
+
}
|
|
133
|
+
print('Done');
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Stream Transformations
|
|
138
|
+
|
|
139
|
+
```dart
|
|
140
|
+
// Transform stream data
|
|
141
|
+
Stream<String> getUserNames() {
|
|
142
|
+
return fetchUsersStream()
|
|
143
|
+
.map((user) => user.name)
|
|
144
|
+
.where((name) => name.isNotEmpty);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Async map
|
|
148
|
+
Stream<User> enrichUsers(Stream<String> userIds) {
|
|
149
|
+
return userIds.asyncMap((id) async {
|
|
150
|
+
return await fetchUser(id);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Combine streams
|
|
155
|
+
final combined = StreamGroup.merge([stream1, stream2, stream3]);
|
|
156
|
+
|
|
157
|
+
// Buffer events
|
|
158
|
+
final buffered = stream.transform(
|
|
159
|
+
StreamTransformer.fromHandlers(
|
|
160
|
+
handleData: (data, sink) {
|
|
161
|
+
sink.add(data);
|
|
162
|
+
},
|
|
163
|
+
),
|
|
164
|
+
);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## StreamController
|
|
168
|
+
|
|
169
|
+
```dart
|
|
170
|
+
class ChatService {
|
|
171
|
+
final _messagesController = StreamController<Message>.broadcast();
|
|
172
|
+
|
|
173
|
+
Stream<Message> get messages => _messagesController.stream;
|
|
174
|
+
|
|
175
|
+
void sendMessage(Message message) {
|
|
176
|
+
_messagesController.add(message);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
void dispose() {
|
|
180
|
+
_messagesController.close();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Usage
|
|
185
|
+
final chat = ChatService();
|
|
186
|
+
|
|
187
|
+
final subscription = chat.messages.listen((message) {
|
|
188
|
+
print('New message: ${message.text}');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
chat.sendMessage(Message(text: 'Hello'));
|
|
192
|
+
|
|
193
|
+
// Clean up
|
|
194
|
+
await subscription.cancel();
|
|
195
|
+
chat.dispose();
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Isolates (Heavy Computation)
|
|
199
|
+
|
|
200
|
+
```dart
|
|
201
|
+
// Run expensive computation in isolate
|
|
202
|
+
Future<int> calculateInIsolate(int n) async {
|
|
203
|
+
return await Isolate.run(() {
|
|
204
|
+
int result = 0;
|
|
205
|
+
for (int i = 0; i < n; i++) {
|
|
206
|
+
result += i;
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ✅ For CPU-intensive tasks
|
|
213
|
+
Future<Image> processImage(File imageFile) async {
|
|
214
|
+
final bytes = await imageFile.readAsBytes();
|
|
215
|
+
|
|
216
|
+
// Run in isolate to avoid blocking UI
|
|
217
|
+
final processed = await Isolate.run(() {
|
|
218
|
+
return applyFilters(bytes);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return Image.memory(processed);
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Completer
|
|
226
|
+
|
|
227
|
+
```dart
|
|
228
|
+
// Create Future manually
|
|
229
|
+
class DatabaseConnection {
|
|
230
|
+
final Completer<void> _readyCompleter = Completer();
|
|
231
|
+
|
|
232
|
+
Future<void> get ready => _readyCompleter.future;
|
|
233
|
+
|
|
234
|
+
void connect() async {
|
|
235
|
+
try {
|
|
236
|
+
await _performConnection();
|
|
237
|
+
_readyCompleter.complete();
|
|
238
|
+
} catch (e) {
|
|
239
|
+
_readyCompleter.completeError(e);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Usage
|
|
245
|
+
final db = DatabaseConnection();
|
|
246
|
+
db.connect();
|
|
247
|
+
await db.ready; // Wait until connected
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Error Handling
|
|
251
|
+
|
|
252
|
+
```dart
|
|
253
|
+
// ✅ Use try-catch with async/await
|
|
254
|
+
Future<void> safeOperation() async {
|
|
255
|
+
try {
|
|
256
|
+
await riskyOperation();
|
|
257
|
+
} on NetworkException catch (e) {
|
|
258
|
+
print('Network error: ${e.message}');
|
|
259
|
+
} on TimeoutException {
|
|
260
|
+
print('Operation timed out');
|
|
261
|
+
} catch (e, stackTrace) {
|
|
262
|
+
print('Unexpected error: $e');
|
|
263
|
+
print('Stack trace: $stackTrace');
|
|
264
|
+
rethrow;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ✅ Handle stream errors
|
|
269
|
+
stream.listen(
|
|
270
|
+
(data) => process(data),
|
|
271
|
+
onError: (error) {
|
|
272
|
+
if (error is NetworkException) {
|
|
273
|
+
reconnect();
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Best Practices
|
|
280
|
+
|
|
281
|
+
- Always use `async`/`await` for readability
|
|
282
|
+
- Run independent operations in parallel with `Future.wait`
|
|
283
|
+
- Set timeouts for network operations
|
|
284
|
+
- Cancel subscriptions when done
|
|
285
|
+
- Use isolates for CPU-intensive work
|
|
286
|
+
- Handle errors explicitly
|
|
287
|
+
- Close StreamControllers when finished
|
|
288
|
+
- Use `async*` and `yield` for generating stream data
|
|
289
|
+
- Prefer `await for` over `listen` for sequential processing
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Dart Fundamentals
|
|
2
|
+
|
|
3
|
+
## Null Safety
|
|
4
|
+
|
|
5
|
+
Dart has sound null safety - non-nullable by default:
|
|
6
|
+
|
|
7
|
+
```dart
|
|
8
|
+
// Non-nullable types
|
|
9
|
+
String name = 'Alice'; // Cannot be null
|
|
10
|
+
int age = 30; // Cannot be null
|
|
11
|
+
|
|
12
|
+
// Nullable types - add ?
|
|
13
|
+
String? middleName; // Can be null
|
|
14
|
+
int? optionalAge; // Can be null
|
|
15
|
+
|
|
16
|
+
// ❌ Compile error
|
|
17
|
+
String lastName = null; // Error: Can't assign null to non-nullable
|
|
18
|
+
|
|
19
|
+
// ✅ Correct
|
|
20
|
+
String? lastName = null; // OK: Explicitly nullable
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Type Annotations
|
|
24
|
+
|
|
25
|
+
Always use explicit types for clarity:
|
|
26
|
+
|
|
27
|
+
```dart
|
|
28
|
+
// ✅ Explicit types
|
|
29
|
+
String getUserName(int userId) {
|
|
30
|
+
final User user = fetchUser(userId);
|
|
31
|
+
return user.name;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Variables
|
|
35
|
+
final String message = 'Hello';
|
|
36
|
+
const int maxRetries = 3;
|
|
37
|
+
List<String> names = ['Alice', 'Bob'];
|
|
38
|
+
|
|
39
|
+
// ❌ Avoid dynamic
|
|
40
|
+
dynamic data = fetchData(); // No type safety
|
|
41
|
+
|
|
42
|
+
// ✅ Use specific types
|
|
43
|
+
Map<String, dynamic> data = fetchData();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Null-Aware Operators
|
|
47
|
+
|
|
48
|
+
```dart
|
|
49
|
+
// ?. - Null-aware access
|
|
50
|
+
String? name = user?.name; // null if user is null
|
|
51
|
+
|
|
52
|
+
// ?? - Null coalescing
|
|
53
|
+
String displayName = user?.name ?? 'Guest';
|
|
54
|
+
|
|
55
|
+
// ??= - Null-aware assignment
|
|
56
|
+
name ??= 'Unknown'; // Assign only if null
|
|
57
|
+
|
|
58
|
+
// ! - Null assertion (use sparingly)
|
|
59
|
+
String name = user!.name; // Assert user is not null
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Collections
|
|
63
|
+
|
|
64
|
+
```dart
|
|
65
|
+
// Lists
|
|
66
|
+
final List<String> fruits = ['apple', 'banana'];
|
|
67
|
+
fruits.add('orange');
|
|
68
|
+
|
|
69
|
+
// Sets (unique values)
|
|
70
|
+
final Set<int> uniqueIds = {1, 2, 3};
|
|
71
|
+
|
|
72
|
+
// Maps
|
|
73
|
+
final Map<String, int> scores = {
|
|
74
|
+
'Alice': 100,
|
|
75
|
+
'Bob': 95,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// ✅ Use collection if for conditional elements
|
|
79
|
+
final List<String> items = [
|
|
80
|
+
'required',
|
|
81
|
+
if (showOptional) 'optional',
|
|
82
|
+
if (user != null) user.name,
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
// ✅ Use spread operator
|
|
86
|
+
final List<int> combined = [...list1, ...list2];
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Functions
|
|
90
|
+
|
|
91
|
+
```dart
|
|
92
|
+
// Named parameters (recommended for multiple params)
|
|
93
|
+
void createUser({
|
|
94
|
+
required String email,
|
|
95
|
+
required String name,
|
|
96
|
+
int? age,
|
|
97
|
+
}) {
|
|
98
|
+
// email and name are required
|
|
99
|
+
// age is optional
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
createUser(email: 'test@example.com', name: 'Alice');
|
|
103
|
+
|
|
104
|
+
// Positional parameters
|
|
105
|
+
int add(int a, int b) => a + b;
|
|
106
|
+
|
|
107
|
+
// Optional positional
|
|
108
|
+
String greet(String name, [String? title]) {
|
|
109
|
+
return title != null ? '$title $name' : name;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Arrow syntax for single expressions
|
|
113
|
+
bool isAdult(int age) => age >= 18;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Classes
|
|
117
|
+
|
|
118
|
+
```dart
|
|
119
|
+
// ✅ Use final for immutable fields
|
|
120
|
+
class User {
|
|
121
|
+
final String id;
|
|
122
|
+
final String email;
|
|
123
|
+
String name; // Mutable
|
|
124
|
+
|
|
125
|
+
User({
|
|
126
|
+
required this.id,
|
|
127
|
+
required this.email,
|
|
128
|
+
required this.name,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Named constructors
|
|
132
|
+
User.guest() : id = '', email = '', name = 'Guest';
|
|
133
|
+
|
|
134
|
+
// Factory constructors
|
|
135
|
+
factory User.fromJson(Map<String, dynamic> json) {
|
|
136
|
+
return User(
|
|
137
|
+
id: json['id'] as String,
|
|
138
|
+
email: json['email'] as String,
|
|
139
|
+
name: json['name'] as String,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ✅ Immutable classes with const
|
|
145
|
+
class Point {
|
|
146
|
+
final double x;
|
|
147
|
+
final double y;
|
|
148
|
+
|
|
149
|
+
const Point(this.x, this.y);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const origin = Point(0, 0); // Compile-time constant
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Enums
|
|
156
|
+
|
|
157
|
+
```dart
|
|
158
|
+
// Simple enum
|
|
159
|
+
enum Status {
|
|
160
|
+
pending,
|
|
161
|
+
processing,
|
|
162
|
+
completed,
|
|
163
|
+
failed,
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Enhanced enums (Dart 2.17+)
|
|
167
|
+
enum UserRole {
|
|
168
|
+
admin('Administrator', level: 3),
|
|
169
|
+
editor('Editor', level: 2),
|
|
170
|
+
viewer('Viewer', level: 1);
|
|
171
|
+
|
|
172
|
+
const UserRole(this.displayName, {required this.level});
|
|
173
|
+
|
|
174
|
+
final String displayName;
|
|
175
|
+
final int level;
|
|
176
|
+
|
|
177
|
+
bool canEdit() => level >= 2;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Usage
|
|
181
|
+
final role = UserRole.admin;
|
|
182
|
+
print(role.displayName); // 'Administrator'
|
|
183
|
+
print(role.canEdit()); // true
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Extension Methods
|
|
187
|
+
|
|
188
|
+
```dart
|
|
189
|
+
// Add methods to existing types
|
|
190
|
+
extension StringExtensions on String {
|
|
191
|
+
bool get isEmail => contains('@');
|
|
192
|
+
|
|
193
|
+
String capitalize() {
|
|
194
|
+
if (isEmpty) return this;
|
|
195
|
+
return '${this[0].toUpperCase()}${substring(1)}';
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Usage
|
|
200
|
+
print('test@example.com'.isEmail); // true
|
|
201
|
+
print('hello'.capitalize()); // 'Hello'
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Cascade Notation
|
|
205
|
+
|
|
206
|
+
```dart
|
|
207
|
+
// ✅ Use cascades for fluent method chaining
|
|
208
|
+
final user = User(id: '1', email: 'test@example.com', name: 'Alice')
|
|
209
|
+
..setRole(UserRole.admin)
|
|
210
|
+
..setActive(true)
|
|
211
|
+
..save();
|
|
212
|
+
|
|
213
|
+
// ✅ Building objects
|
|
214
|
+
final button = Button()
|
|
215
|
+
..text = 'Submit'
|
|
216
|
+
..onPressed = handleSubmit
|
|
217
|
+
..enabled = true;
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Late Variables
|
|
221
|
+
|
|
222
|
+
```dart
|
|
223
|
+
// late - initialize later, but before use
|
|
224
|
+
class UserService {
|
|
225
|
+
late final Database db;
|
|
226
|
+
|
|
227
|
+
Future<void> init() async {
|
|
228
|
+
db = await Database.connect();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
Future<User> getUser(String id) async {
|
|
232
|
+
return db.query('SELECT * FROM users WHERE id = ?', [id]);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ⚠️ Runtime error if accessed before initialization
|
|
237
|
+
late String config;
|
|
238
|
+
print(config); // Error!
|
|
239
|
+
|
|
240
|
+
// ✅ Initialize before use
|
|
241
|
+
config = loadConfig();
|
|
242
|
+
print(config); // OK
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Naming Conventions
|
|
246
|
+
|
|
247
|
+
```dart
|
|
248
|
+
// ✓ Classes/Enums/Typedefs: PascalCase
|
|
249
|
+
class UserAccount { }
|
|
250
|
+
enum OrderStatus { }
|
|
251
|
+
typedef IntCallback = void Function(int);
|
|
252
|
+
|
|
253
|
+
// ✓ Variables/Functions/Parameters: camelCase
|
|
254
|
+
String userName = 'Alice';
|
|
255
|
+
void processOrder() { }
|
|
256
|
+
|
|
257
|
+
// ✓ Constants: lowerCamelCase
|
|
258
|
+
const maxRetries = 3;
|
|
259
|
+
const apiBaseUrl = 'https://api.example.com';
|
|
260
|
+
|
|
261
|
+
// ✓ Private members: prefix with _
|
|
262
|
+
class User {
|
|
263
|
+
String _password; // Private field
|
|
264
|
+
void _hashPassword() { } // Private method
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ✓ Files: snake_case
|
|
268
|
+
// user_service.dart
|
|
269
|
+
// order_repository.dart
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Best Practices
|
|
273
|
+
|
|
274
|
+
- Prefer `final` over `var` for immutability
|
|
275
|
+
- Use `const` for compile-time constants
|
|
276
|
+
- Leverage null safety - avoid `!` assertion
|
|
277
|
+
- Use named parameters for functions with multiple parameters
|
|
278
|
+
- Make classes immutable when possible
|
|
279
|
+
- Use extension methods to add functionality to existing types
|
|
280
|
+
- Follow the official Dart style guide
|