@cubis/foundry 0.3.71 → 0.3.72
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/CHANGELOG.md +15 -0
- package/dist/cli/core.js +4 -18
- package/dist/cli/core.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/core.ts +4 -18
- package/workflows/powers/accessibility/POWER.md +83 -94
- package/workflows/powers/accessibility/SKILL.md +82 -94
- package/workflows/powers/agent-design/POWER.md +201 -0
- package/workflows/powers/agent-design/SKILL.md +198 -0
- package/workflows/powers/agent-design/references/clarification-patterns.md +153 -0
- package/workflows/powers/agent-design/references/skill-testing.md +164 -0
- package/workflows/powers/agent-design/references/workflow-patterns.md +226 -0
- package/workflows/powers/agentic-eval/POWER.md +62 -0
- package/workflows/powers/agentic-eval/SKILL.md +59 -0
- package/workflows/powers/agentic-eval/references/rubric-and-regression-checklist.md +11 -0
- package/workflows/powers/api-designer/POWER.md +43 -71
- package/workflows/powers/api-designer/SKILL.md +43 -71
- package/workflows/powers/api-patterns/POWER.md +42 -56
- package/workflows/powers/api-patterns/SKILL.md +42 -57
- package/workflows/powers/architecture-designer/POWER.md +43 -60
- package/workflows/powers/architecture-designer/SKILL.md +43 -60
- package/workflows/powers/ask-questions-if-underspecified/POWER.md +51 -3
- package/workflows/powers/auth-architect/POWER.md +69 -0
- package/workflows/powers/auth-architect/SKILL.md +66 -0
- package/workflows/powers/auth-architect/references/session-token-policy-checklist.md +45 -0
- package/workflows/powers/behavioral-modes/POWER.md +100 -9
- package/workflows/powers/c-pro/POWER.md +105 -0
- package/workflows/powers/c-pro/SKILL.md +102 -0
- package/workflows/powers/c-pro/references/build-systems-and-toolchains.md +148 -0
- package/workflows/powers/c-pro/references/common-ub-and-portability.md +166 -0
- package/workflows/powers/c-pro/references/debugging-with-sanitizers.md +205 -0
- package/workflows/powers/c-pro/references/memory-safety-and-build-checklist.md +60 -0
- package/workflows/powers/c-pro/references/posix-and-platform-apis.md +244 -0
- package/workflows/powers/changelog-generator/POWER.md +127 -63
- package/workflows/powers/changelog-generator/SKILL.md +126 -63
- package/workflows/powers/ci-cd-pipelines/POWER.md +156 -0
- package/workflows/powers/ci-cd-pipelines/SKILL.md +153 -0
- package/workflows/powers/ci-cd-pipelines/references/github-actions-patterns.md +160 -0
- package/workflows/powers/ci-cd-pipelines/references/pipeline-security-checklist.md +57 -0
- package/workflows/powers/cli-developer/POWER.md +152 -95
- package/workflows/powers/cli-developer/SKILL.md +152 -95
- package/workflows/powers/cpp-pro/POWER.md +111 -0
- package/workflows/powers/cpp-pro/SKILL.md +108 -0
- package/workflows/powers/cpp-pro/references/concurrency-primitives.md +266 -0
- package/workflows/powers/cpp-pro/references/move-semantics-and-value-types.md +149 -0
- package/workflows/powers/cpp-pro/references/performance-and-profiling.md +191 -0
- package/workflows/powers/cpp-pro/references/raii-and-modern-cpp-checklist.md +87 -0
- package/workflows/powers/cpp-pro/references/template-and-concepts-patterns.md +205 -0
- package/workflows/powers/csharp-pro/POWER.md +47 -22
- package/workflows/powers/csharp-pro/SKILL.md +47 -22
- package/workflows/powers/dart-pro/POWER.md +68 -0
- package/workflows/powers/dart-pro/SKILL.md +65 -0
- package/workflows/powers/dart-pro/references/isolate-and-concurrency.md +180 -0
- package/workflows/powers/dart-pro/references/null-safety-and-async-patterns.md +133 -0
- package/workflows/powers/dart-pro/references/package-structure-and-linting.md +193 -0
- package/workflows/powers/dart-pro/references/sealed-records-patterns.md +173 -0
- package/workflows/powers/dart-pro/references/testing-and-mocking.md +235 -0
- package/workflows/powers/database-design/POWER.md +47 -33
- package/workflows/powers/database-design/SKILL.md +47 -33
- package/workflows/powers/database-optimizer/POWER.md +43 -64
- package/workflows/powers/database-optimizer/SKILL.md +43 -64
- package/workflows/powers/database-skills/POWER.md +59 -93
- package/workflows/powers/database-skills/SKILL.md +59 -93
- package/workflows/powers/debugging-strategies/POWER.md +69 -0
- package/workflows/powers/debugging-strategies/SKILL.md +66 -0
- package/workflows/powers/debugging-strategies/references/reproduce-isolate-verify-checklist.md +42 -0
- package/workflows/powers/deep-research/POWER.md +67 -0
- package/workflows/powers/deep-research/SKILL.md +64 -0
- package/workflows/powers/deep-research/references/multi-round-research-loop.md +80 -0
- package/workflows/powers/design-system-builder/POWER.md +130 -116
- package/workflows/powers/design-system-builder/SKILL.md +130 -116
- package/workflows/powers/devops-engineer/POWER.md +120 -57
- package/workflows/powers/devops-engineer/SKILL.md +120 -57
- package/workflows/powers/docker-kubernetes/POWER.md +94 -0
- package/workflows/powers/docker-kubernetes/SKILL.md +91 -0
- package/workflows/powers/docker-kubernetes/references/dockerfile-optimization-checklist.md +35 -0
- package/workflows/powers/docker-kubernetes/references/kubernetes-deployment-patterns.md +59 -0
- package/workflows/powers/documentation-templates/POWER.md +158 -127
- package/workflows/powers/documentation-templates/SKILL.md +158 -127
- package/workflows/powers/drizzle-expert/POWER.md +66 -0
- package/workflows/powers/drizzle-expert/SKILL.md +63 -0
- package/workflows/powers/drizzle-expert/references/runtime-pairing-matrix.md +16 -0
- package/workflows/powers/drizzle-expert/references/schema-and-migration-playbook.md +18 -0
- package/workflows/powers/error-ux-observability/POWER.md +144 -131
- package/workflows/powers/error-ux-observability/SKILL.md +143 -131
- package/workflows/powers/fastapi-expert/POWER.md +46 -60
- package/workflows/powers/fastapi-expert/SKILL.md +46 -60
- package/workflows/powers/firebase/POWER.md +65 -0
- package/workflows/powers/firebase/SKILL.md +62 -0
- package/workflows/powers/firebase/references/platform-routing.md +16 -0
- package/workflows/powers/firebase/references/rules-and-indexes-checklist.md +11 -0
- package/workflows/powers/flutter-design-system/POWER.md +63 -0
- package/workflows/powers/flutter-design-system/SKILL.md +60 -0
- package/workflows/powers/flutter-design-system/references/shared-widgets.md +29 -0
- package/workflows/powers/flutter-design-system/references/tokens-and-theme.md +34 -0
- package/workflows/powers/flutter-drift/POWER.md +65 -0
- package/workflows/powers/flutter-drift/SKILL.md +62 -0
- package/workflows/powers/flutter-drift/references/migrations.md +22 -0
- package/workflows/powers/flutter-drift/references/query-patterns.md +26 -0
- package/workflows/powers/flutter-feature/POWER.md +65 -0
- package/workflows/powers/flutter-feature/SKILL.md +62 -0
- package/workflows/powers/flutter-feature/references/architecture-rules.md +85 -0
- package/workflows/powers/flutter-feature/references/composite-provider.md +58 -0
- package/workflows/powers/flutter-feature/references/outbox-pattern.md +87 -0
- package/workflows/powers/flutter-feature/references/testing-patterns.md +218 -0
- package/workflows/powers/flutter-go-router/POWER.md +64 -0
- package/workflows/powers/flutter-go-router/SKILL.md +61 -0
- package/workflows/powers/flutter-go-router/references/guards-and-deeplinks.md +20 -0
- package/workflows/powers/flutter-go-router/references/typed-routes.md +27 -0
- package/workflows/powers/flutter-offline-sync/POWER.md +62 -0
- package/workflows/powers/flutter-offline-sync/SKILL.md +59 -0
- package/workflows/powers/flutter-offline-sync/references/outbox-full.md +44 -0
- package/workflows/powers/flutter-repository/POWER.md +64 -0
- package/workflows/powers/flutter-repository/SKILL.md +61 -0
- package/workflows/powers/flutter-repository/references/drift-patterns.md +21 -0
- package/workflows/powers/flutter-repository/references/retrofit-patterns.md +20 -0
- package/workflows/powers/flutter-riverpod/POWER.md +70 -0
- package/workflows/powers/flutter-riverpod/SKILL.md +67 -0
- package/workflows/powers/flutter-riverpod/references/async-and-mutations.md +19 -0
- package/workflows/powers/flutter-riverpod/references/async-lifecycle.md +19 -0
- package/workflows/powers/flutter-riverpod/references/provider-selection.md +20 -0
- package/workflows/powers/flutter-riverpod/references/testing.md +21 -0
- package/workflows/powers/flutter-riverpod/references/version-matrix.md +24 -0
- package/workflows/powers/flutter-state-machine/POWER.md +62 -0
- package/workflows/powers/flutter-state-machine/SKILL.md +59 -0
- package/workflows/powers/flutter-state-machine/references/app-state-contract.md +23 -0
- package/workflows/powers/flutter-state-machine/references/ui-rendering.md +14 -0
- package/workflows/powers/flutter-testing/POWER.md +64 -0
- package/workflows/powers/flutter-testing/SKILL.md +61 -0
- package/workflows/powers/flutter-testing/references/offline-sync-tests.md +16 -0
- package/workflows/powers/flutter-testing/references/test-layers.md +33 -0
- package/workflows/powers/frontend-code-review/POWER.md +137 -0
- package/workflows/powers/frontend-code-review/SKILL.md +134 -0
- package/workflows/powers/frontend-code-review/references/common-antipatterns.md +86 -0
- package/workflows/powers/frontend-code-review/references/performance-budgets.md +56 -0
- package/workflows/powers/frontend-code-review/references/review-checklists.md +47 -0
- package/workflows/powers/frontend-design/POWER.md +163 -362
- package/workflows/powers/frontend-design/SKILL.md +163 -362
- package/workflows/powers/game-development/POWER.md +57 -140
- package/workflows/powers/game-development/SKILL.md +57 -140
- package/workflows/powers/geo-fundamentals/POWER.md +64 -126
- package/workflows/powers/geo-fundamentals/SKILL.md +64 -127
- package/workflows/powers/git-workflow/POWER.md +135 -0
- package/workflows/powers/git-workflow/SKILL.md +132 -0
- package/workflows/powers/git-workflow/references/pr-review-checklist.md +63 -0
- package/workflows/powers/golang-pro/POWER.md +46 -35
- package/workflows/powers/golang-pro/SKILL.md +46 -35
- package/workflows/powers/graphql-architect/POWER.md +44 -62
- package/workflows/powers/graphql-architect/SKILL.md +44 -62
- package/workflows/powers/i18n-localization/POWER.md +118 -103
- package/workflows/powers/i18n-localization/SKILL.md +118 -103
- package/workflows/powers/java-pro/POWER.md +47 -22
- package/workflows/powers/java-pro/SKILL.md +47 -22
- package/workflows/powers/javascript-pro/POWER.md +47 -34
- package/workflows/powers/javascript-pro/SKILL.md +47 -34
- package/workflows/powers/kotlin-pro/POWER.md +46 -23
- package/workflows/powers/kotlin-pro/SKILL.md +46 -23
- package/workflows/powers/legacy-modernizer/POWER.md +43 -60
- package/workflows/powers/legacy-modernizer/SKILL.md +43 -60
- package/workflows/powers/mcp-builder/POWER.md +65 -0
- package/workflows/powers/mcp-builder/SKILL.md +62 -0
- package/workflows/powers/mcp-builder/references/testing-and-evals.md +17 -0
- package/workflows/powers/mcp-builder/references/transport-and-tool-design.md +17 -0
- package/workflows/powers/microservices-architect/POWER.md +43 -70
- package/workflows/powers/microservices-architect/SKILL.md +43 -70
- package/workflows/powers/mobile-design/POWER.md +110 -345
- package/workflows/powers/mobile-design/SKILL.md +110 -345
- package/workflows/powers/mongodb/POWER.md +67 -0
- package/workflows/powers/mongodb/SKILL.md +64 -0
- package/workflows/powers/mongodb/references/mongodb-checklist.md +20 -0
- package/workflows/powers/mysql/POWER.md +67 -0
- package/workflows/powers/mysql/SKILL.md +64 -0
- package/workflows/powers/mysql/references/mysql-checklist.md +20 -0
- package/workflows/powers/neki/POWER.md +67 -0
- package/workflows/powers/neki/SKILL.md +64 -0
- package/workflows/powers/neki/references/neki-checklist.md +18 -0
- package/workflows/powers/nestjs-expert/POWER.md +45 -91
- package/workflows/powers/nestjs-expert/SKILL.md +45 -91
- package/workflows/powers/nextjs-developer/POWER.md +51 -44
- package/workflows/powers/nextjs-developer/SKILL.md +51 -44
- package/workflows/powers/nodejs-best-practices/POWER.md +48 -29
- package/workflows/powers/nodejs-best-practices/SKILL.md +48 -29
- package/workflows/powers/observability/POWER.md +109 -0
- package/workflows/powers/observability/SKILL.md +106 -0
- package/workflows/powers/observability/references/alerting-and-slo-checklist.md +87 -0
- package/workflows/powers/observability/references/opentelemetry-setup-guide.md +121 -0
- package/workflows/powers/openai-docs/POWER.md +61 -0
- package/workflows/powers/openai-docs/SKILL.md +58 -0
- package/workflows/powers/openai-docs/references/official-source-playbook.md +10 -0
- package/workflows/powers/performance-profiling/POWER.md +61 -114
- package/workflows/powers/performance-profiling/SKILL.md +61 -114
- package/workflows/powers/php-pro/POWER.md +116 -0
- package/workflows/powers/php-pro/SKILL.md +113 -0
- package/workflows/powers/php-pro/references/architecture-and-di.md +239 -0
- package/workflows/powers/php-pro/references/modern-php-features.md +189 -0
- package/workflows/powers/php-pro/references/performance-and-deployment.md +197 -0
- package/workflows/powers/php-pro/references/php84-strict-typing-checklist.md +161 -0
- package/workflows/powers/php-pro/references/testing-and-static-analysis.md +235 -0
- package/workflows/powers/playwright-e2e/POWER.md +85 -0
- package/workflows/powers/playwright-e2e/SKILL.md +82 -0
- package/workflows/powers/playwright-e2e/references/locator-trace-flake-checklist.md +80 -0
- package/workflows/powers/postgres/POWER.md +67 -0
- package/workflows/powers/postgres/SKILL.md +64 -0
- package/workflows/powers/postgres/references/postgres-checklist.md +20 -0
- package/workflows/powers/prompt-engineer/POWER.md +47 -30
- package/workflows/powers/prompt-engineer/SKILL.md +47 -30
- package/workflows/powers/python-pro/POWER.md +47 -36
- package/workflows/powers/python-pro/SKILL.md +47 -36
- package/workflows/powers/react-best-practices/POWER.md +56 -33
- package/workflows/powers/react-best-practices/SKILL.md +56 -33
- package/workflows/powers/react-expert/POWER.md +47 -37
- package/workflows/powers/react-expert/SKILL.md +47 -37
- package/workflows/powers/redis/POWER.md +67 -0
- package/workflows/powers/redis/SKILL.md +64 -0
- package/workflows/powers/redis/references/redis-checklist.md +19 -0
- package/workflows/powers/ruby-pro/POWER.md +118 -0
- package/workflows/powers/ruby-pro/SKILL.md +115 -0
- package/workflows/powers/ruby-pro/references/modern-ruby-features.md +189 -0
- package/workflows/powers/ruby-pro/references/object-design-patterns.md +220 -0
- package/workflows/powers/ruby-pro/references/performance-and-profiling.md +224 -0
- package/workflows/powers/ruby-pro/references/ruby-concurrency-and-testing.md +190 -0
- package/workflows/powers/ruby-pro/references/testing-and-rspec.md +236 -0
- package/workflows/powers/rust-pro/POWER.md +45 -31
- package/workflows/powers/rust-pro/SKILL.md +45 -31
- package/workflows/powers/security-engineer/POWER.md +129 -0
- package/workflows/powers/security-engineer/SKILL.md +126 -0
- package/workflows/powers/seo-fundamentals/POWER.md +59 -102
- package/workflows/powers/seo-fundamentals/SKILL.md +59 -102
- package/workflows/powers/serverless-patterns/POWER.md +171 -0
- package/workflows/powers/serverless-patterns/SKILL.md +168 -0
- package/workflows/powers/skill-creator/POWER.md +90 -0
- package/workflows/powers/skill-creator/SKILL.md +87 -0
- package/workflows/powers/skill-creator/references/platform-formats.md +181 -0
- package/workflows/powers/skill-creator/references/schemas.md +430 -0
- package/workflows/powers/spec-miner/POWER.md +49 -57
- package/workflows/powers/spec-miner/SKILL.md +49 -57
- package/workflows/powers/sqlite/POWER.md +67 -0
- package/workflows/powers/sqlite/SKILL.md +64 -0
- package/workflows/powers/sqlite/references/sqlite-checklist.md +19 -0
- package/workflows/powers/sre-engineer/POWER.md +123 -64
- package/workflows/powers/sre-engineer/SKILL.md +123 -64
- package/workflows/powers/static-analysis/POWER.md +121 -77
- package/workflows/powers/static-analysis/SKILL.md +121 -77
- package/workflows/powers/stripe-best-practices/POWER.md +140 -17
- package/workflows/powers/stripe-best-practices/SKILL.md +139 -17
- package/workflows/powers/supabase/POWER.md +67 -0
- package/workflows/powers/supabase/SKILL.md +64 -0
- package/workflows/powers/supabase/references/supabase-checklist.md +19 -0
- package/workflows/powers/swift-pro/POWER.md +118 -0
- package/workflows/powers/swift-pro/SKILL.md +115 -0
- package/workflows/powers/swift-pro/references/concurrency-patterns.md +165 -0
- package/workflows/powers/swift-pro/references/protocol-and-generics.md +172 -0
- package/workflows/powers/swift-pro/references/sendable-and-isolation.md +116 -0
- package/workflows/powers/swift-pro/references/swift-concurrency-and-protocols.md +260 -0
- package/workflows/powers/swift-pro/references/testing-and-packages.md +192 -0
- package/workflows/powers/tailwind-patterns/POWER.md +71 -240
- package/workflows/powers/tailwind-patterns/SKILL.md +71 -240
- package/workflows/powers/testing-patterns/POWER.md +155 -10
- package/workflows/powers/testing-patterns/SKILL.md +155 -10
- package/workflows/powers/typescript-pro/POWER.md +47 -38
- package/workflows/powers/typescript-pro/SKILL.md +47 -38
- package/workflows/powers/vitess/POWER.md +67 -0
- package/workflows/powers/vitess/SKILL.md +64 -0
- package/workflows/powers/vitess/references/vitess-checklist.md +19 -0
- package/workflows/powers/vulnerability-scanner/POWER.md +146 -10
- package/workflows/powers/vulnerability-scanner/SKILL.md +146 -10
- package/workflows/powers/web-perf/POWER.md +43 -170
- package/workflows/powers/web-perf/SKILL.md +43 -170
- package/workflows/powers/webapp-testing/POWER.md +43 -164
- package/workflows/powers/webapp-testing/SKILL.md +43 -164
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Isolate and Concurrency Patterns
|
|
2
|
+
|
|
3
|
+
## Isolate.run — Simple Compute Offload
|
|
4
|
+
|
|
5
|
+
```dart
|
|
6
|
+
// Isolate.run for one-shot heavy computation (Dart 3.0+)
|
|
7
|
+
Future<List<SearchResult>> searchIndex(String query) async {
|
|
8
|
+
final data = await loadIndexData();
|
|
9
|
+
|
|
10
|
+
// Runs in a new isolate — no main-isolate jank
|
|
11
|
+
return await Isolate.run(() {
|
|
12
|
+
return performSearch(data, query); // data is copied to isolate
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// In Flutter, use compute() for the same purpose
|
|
17
|
+
final results = await compute(performSearch, (data, query));
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Long-Lived Isolates with Ports
|
|
21
|
+
|
|
22
|
+
```dart
|
|
23
|
+
// Long-lived worker for continuous processing
|
|
24
|
+
class ImageProcessor {
|
|
25
|
+
late final Isolate _isolate;
|
|
26
|
+
late final SendPort _sendPort;
|
|
27
|
+
final ReceivePort _receivePort = ReceivePort();
|
|
28
|
+
final Map<int, Completer<Uint8List>> _pending = {};
|
|
29
|
+
int _nextId = 0;
|
|
30
|
+
|
|
31
|
+
Future<void> start() async {
|
|
32
|
+
_isolate = await Isolate.spawn(_workerEntryPoint, _receivePort.sendPort);
|
|
33
|
+
|
|
34
|
+
// First message from worker is its SendPort
|
|
35
|
+
final completer = Completer<SendPort>();
|
|
36
|
+
_receivePort.listen((message) {
|
|
37
|
+
if (message is SendPort) {
|
|
38
|
+
completer.complete(message);
|
|
39
|
+
} else if (message is (int, Uint8List)) {
|
|
40
|
+
final (id, result) = message;
|
|
41
|
+
_pending.remove(id)?.complete(result);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
_sendPort = await completer.future;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Future<Uint8List> processImage(Uint8List imageData) {
|
|
48
|
+
final id = _nextId++;
|
|
49
|
+
final completer = Completer<Uint8List>();
|
|
50
|
+
_pending[id] = completer;
|
|
51
|
+
_sendPort.send((id, imageData));
|
|
52
|
+
return completer.future;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void dispose() {
|
|
56
|
+
_isolate.kill(priority: Isolate.immediate);
|
|
57
|
+
_receivePort.close();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static void _workerEntryPoint(SendPort mainPort) {
|
|
61
|
+
final workerPort = ReceivePort();
|
|
62
|
+
mainPort.send(workerPort.sendPort);
|
|
63
|
+
|
|
64
|
+
workerPort.listen((message) {
|
|
65
|
+
if (message is (int, Uint8List)) {
|
|
66
|
+
final (id, data) = message;
|
|
67
|
+
final result = _applyFilters(data); // heavy work
|
|
68
|
+
mainPort.send((id, result));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static Uint8List _applyFilters(Uint8List data) {
|
|
74
|
+
// ... image processing
|
|
75
|
+
return data;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Stream-Based Concurrency
|
|
81
|
+
|
|
82
|
+
```dart
|
|
83
|
+
// StreamController for event-driven architecture
|
|
84
|
+
class EventBus {
|
|
85
|
+
final _controller = StreamController<AppEvent>.broadcast();
|
|
86
|
+
|
|
87
|
+
Stream<AppEvent> get events => _controller.stream;
|
|
88
|
+
|
|
89
|
+
// Type-filtered streams
|
|
90
|
+
Stream<T> on<T extends AppEvent>() =>
|
|
91
|
+
_controller.stream.whereType<T>();
|
|
92
|
+
|
|
93
|
+
void emit(AppEvent event) => _controller.add(event);
|
|
94
|
+
|
|
95
|
+
void dispose() => _controller.close();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Usage
|
|
99
|
+
final bus = EventBus();
|
|
100
|
+
bus.on<OrderCreated>().listen((event) {
|
|
101
|
+
notifyUser(event.orderId);
|
|
102
|
+
});
|
|
103
|
+
bus.emit(OrderCreated(orderId: 42));
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Stream Transformations
|
|
107
|
+
|
|
108
|
+
```dart
|
|
109
|
+
// Debounce search input
|
|
110
|
+
Stream<String> debounce(Stream<String> input, Duration delay) async* {
|
|
111
|
+
Timer? timer;
|
|
112
|
+
String? lastValue;
|
|
113
|
+
|
|
114
|
+
await for (final value in input) {
|
|
115
|
+
lastValue = value;
|
|
116
|
+
timer?.cancel();
|
|
117
|
+
timer = Timer(delay, () {});
|
|
118
|
+
await Future.delayed(delay);
|
|
119
|
+
if (lastValue == value) yield value;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Rate limiting with Stream.asyncMap
|
|
124
|
+
Stream<Response> rateLimited(
|
|
125
|
+
Stream<Request> requests, {
|
|
126
|
+
Duration interval = const Duration(milliseconds: 100),
|
|
127
|
+
}) {
|
|
128
|
+
return requests.asyncMap((request) async {
|
|
129
|
+
await Future.delayed(interval);
|
|
130
|
+
return await processRequest(request);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Cancellation pattern
|
|
135
|
+
class DataFetcher {
|
|
136
|
+
StreamSubscription? _subscription;
|
|
137
|
+
|
|
138
|
+
void startListening(Stream<Update> updates) {
|
|
139
|
+
// Cancel previous subscription before starting new one
|
|
140
|
+
_subscription?.cancel();
|
|
141
|
+
_subscription = updates.listen(
|
|
142
|
+
(update) => handleUpdate(update),
|
|
143
|
+
onError: (e) => handleError(e),
|
|
144
|
+
onDone: () => handleDone(),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
void dispose() {
|
|
149
|
+
_subscription?.cancel(); // always cancel in dispose
|
|
150
|
+
_subscription = null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Zones for Error Handling
|
|
156
|
+
|
|
157
|
+
```dart
|
|
158
|
+
// Run code in a zone to catch uncaught async errors
|
|
159
|
+
void main() {
|
|
160
|
+
runZonedGuarded(
|
|
161
|
+
() => runApp(const MyApp()),
|
|
162
|
+
(error, stackTrace) {
|
|
163
|
+
// Catches uncaught errors from async code
|
|
164
|
+
reportError(error, stackTrace);
|
|
165
|
+
},
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Concurrency Rules
|
|
171
|
+
|
|
172
|
+
| Rule | Reason |
|
|
173
|
+
| ---------------------------------------------------------- | ---------------------------------------------- |
|
|
174
|
+
| Never do heavy compute on main isolate | Causes UI jank (>16ms blocks drop frames) |
|
|
175
|
+
| Use `Isolate.run` for one-shot work | Simpler than managing ports |
|
|
176
|
+
| Use persistent isolates for ongoing work | Avoids spawn/teardown overhead per task |
|
|
177
|
+
| Cancel stream subscriptions in `dispose()` | Prevents memory leaks and ghost listeners |
|
|
178
|
+
| Don't share mutable state across isolates | Isolates have separate heaps — send copies |
|
|
179
|
+
| Use `TransferableTypedData` for large buffers | Avoids copying overhead for byte arrays |
|
|
180
|
+
| Use `StreamController.broadcast()` only for multi-listener | Single-listener controllers are more efficient |
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Null Safety and Async Patterns
|
|
2
|
+
|
|
3
|
+
## Null safety rules
|
|
4
|
+
|
|
5
|
+
### Non-nullable by default
|
|
6
|
+
|
|
7
|
+
- `String name` means name is never null. `String? name` means null is a valid value.
|
|
8
|
+
- Use `?` only when null carries domain meaning (e.g., "no value selected"), not as a lazy default.
|
|
9
|
+
- Avoid `late` unless the variable is guaranteed to be initialized before use and you cannot restructure to avoid it.
|
|
10
|
+
|
|
11
|
+
### Null assertion (`!`) usage
|
|
12
|
+
|
|
13
|
+
- **Never** use `!` without a preceding null check or guard in the same scope in production code.
|
|
14
|
+
- In tests, `!` is acceptable after assertions: `expect(result, isNotNull); result!.doSomething();`
|
|
15
|
+
- Prefer `if-case` or null-aware operators (`?.`, `??`) over `!`.
|
|
16
|
+
|
|
17
|
+
### Null-aware operators cheatsheet
|
|
18
|
+
|
|
19
|
+
| Operator | Meaning | Example |
|
|
20
|
+
| -------- | ---------------------------- | -------------------------- |
|
|
21
|
+
| `?.` | Call method only if non-null | `user?.name` |
|
|
22
|
+
| `??` | Default if null | `name ?? 'Unknown'` |
|
|
23
|
+
| `??=` | Assign if null | `cache ??= compute()` |
|
|
24
|
+
| `?..` | Cascade only if non-null | `list?..add(item)..sort()` |
|
|
25
|
+
|
|
26
|
+
## Sealed classes and pattern matching
|
|
27
|
+
|
|
28
|
+
### Defining sealed hierarchies
|
|
29
|
+
|
|
30
|
+
```dart
|
|
31
|
+
sealed class AuthState {}
|
|
32
|
+
class Unauthenticated extends AuthState {}
|
|
33
|
+
class Authenticating extends AuthState {}
|
|
34
|
+
class Authenticated extends AuthState {
|
|
35
|
+
final User user;
|
|
36
|
+
Authenticated(this.user);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- Sealed classes enable **exhaustive switch** — the compiler errors if a subtype is missing.
|
|
41
|
+
- Adding a new subtype immediately surfaces all locations that need updating.
|
|
42
|
+
|
|
43
|
+
### Exhaustive switch
|
|
44
|
+
|
|
45
|
+
```dart
|
|
46
|
+
Widget build(AuthState state) => switch (state) {
|
|
47
|
+
Unauthenticated() => const LoginPage(),
|
|
48
|
+
Authenticating() => const LoadingSpinner(),
|
|
49
|
+
Authenticated(:final user) => HomePage(user: user),
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- Use destructuring (`:final field`) to extract values in switch arms.
|
|
54
|
+
- Prefer switch expressions over if/else chains for sealed types.
|
|
55
|
+
|
|
56
|
+
## Records
|
|
57
|
+
|
|
58
|
+
```dart
|
|
59
|
+
(int, String) parseHeader(String line) {
|
|
60
|
+
final parts = line.split(':');
|
|
61
|
+
return (int.parse(parts[0]), parts[1].trim());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
final (code, message) = parseHeader('200: OK');
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- Use records for lightweight grouped returns — no need to create single-use classes.
|
|
68
|
+
- Name fields for clarity: `({int id, String name}) getUserInfo()`.
|
|
69
|
+
- Records are value types: equality is structural, not referential.
|
|
70
|
+
|
|
71
|
+
## Async patterns
|
|
72
|
+
|
|
73
|
+
### Future best practices
|
|
74
|
+
|
|
75
|
+
- Always `await` Futures. Never fire-and-forget unless you explicitly handle errors with `.catchError` or a zone.
|
|
76
|
+
- Use `Future.wait` for concurrent independent operations, not sequential `await` calls.
|
|
77
|
+
- Handle errors with try/catch around `await`, not `.catchError` chains.
|
|
78
|
+
|
|
79
|
+
### Stream lifecycle management
|
|
80
|
+
|
|
81
|
+
```dart
|
|
82
|
+
class EventManager {
|
|
83
|
+
final _controller = StreamController<Event>.broadcast();
|
|
84
|
+
Stream<Event> get events => _controller.stream;
|
|
85
|
+
|
|
86
|
+
void emit(Event e) => _controller.add(e);
|
|
87
|
+
|
|
88
|
+
void dispose() {
|
|
89
|
+
_controller.close(); // Always close controllers
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- Use `StreamController` (not broadcast) when only one listener is expected.
|
|
95
|
+
- Use `.broadcast()` only when multiple listeners must receive the same events.
|
|
96
|
+
- Always cancel `StreamSubscription` in `dispose()` or `deactivate()`.
|
|
97
|
+
- Prefer `async*` generator functions for producing streams from iteration or polling.
|
|
98
|
+
|
|
99
|
+
### Isolate patterns
|
|
100
|
+
|
|
101
|
+
```dart
|
|
102
|
+
// Simple isolate for CPU-heavy work
|
|
103
|
+
final result = await Isolate.run(() {
|
|
104
|
+
return heavyComputation(data);
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- Use `Isolate.run()` (Dart 2.19+) for one-shot CPU work. It handles spawning and result transfer.
|
|
109
|
+
- In Flutter, use `compute()` for the same pattern with simpler API.
|
|
110
|
+
- For long-lived isolates, use `ReceivePort`/`SendPort` for bidirectional communication.
|
|
111
|
+
- Only `Sendable` objects can cross isolate boundaries. Classes with closures or raw pointers cannot be sent.
|
|
112
|
+
|
|
113
|
+
## Error handling in async code
|
|
114
|
+
|
|
115
|
+
```dart
|
|
116
|
+
Future<User> fetchUser(int id) async {
|
|
117
|
+
try {
|
|
118
|
+
final response = await http.get(Uri.parse('/users/$id'));
|
|
119
|
+
if (response.statusCode != 200) {
|
|
120
|
+
throw HttpException('Failed to fetch user: ${response.statusCode}');
|
|
121
|
+
}
|
|
122
|
+
return User.fromJson(jsonDecode(response.body));
|
|
123
|
+
} on SocketException {
|
|
124
|
+
throw NetworkException('No network connection');
|
|
125
|
+
} on FormatException {
|
|
126
|
+
throw ParseException('Invalid response format');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- Catch specific exceptions. Avoid bare `catch (e)` unless you re-throw.
|
|
132
|
+
- Transform low-level exceptions into domain-specific exceptions at service boundaries.
|
|
133
|
+
- Use `Zone.current.handleUncaughtError` or `runZonedGuarded` for global error handling in Flutter.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Package Structure and Linting
|
|
2
|
+
|
|
3
|
+
## Recommended Package Layout
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
my_package/
|
|
7
|
+
├── lib/
|
|
8
|
+
│ ├── my_package.dart # Barrel file — public API exports
|
|
9
|
+
│ └── src/
|
|
10
|
+
│ ├── models/
|
|
11
|
+
│ │ ├── user.dart
|
|
12
|
+
│ │ └── order.dart
|
|
13
|
+
│ ├── services/
|
|
14
|
+
│ │ └── auth_service.dart
|
|
15
|
+
│ └── utils/
|
|
16
|
+
│ └── validators.dart
|
|
17
|
+
├── test/
|
|
18
|
+
│ ├── models/
|
|
19
|
+
│ │ └── user_test.dart
|
|
20
|
+
│ └── services/
|
|
21
|
+
│ └── auth_service_test.dart
|
|
22
|
+
├── example/ # Usage examples (libraries)
|
|
23
|
+
├── analysis_options.yaml
|
|
24
|
+
├── pubspec.yaml
|
|
25
|
+
└── pubspec.lock # Commit for apps, .gitignore for libraries
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Barrel File Pattern
|
|
29
|
+
|
|
30
|
+
```dart
|
|
31
|
+
// lib/my_package.dart — only export public API
|
|
32
|
+
library my_package;
|
|
33
|
+
|
|
34
|
+
export 'src/models/user.dart';
|
|
35
|
+
export 'src/models/order.dart';
|
|
36
|
+
export 'src/services/auth_service.dart';
|
|
37
|
+
// DO NOT export implementation-only files
|
|
38
|
+
|
|
39
|
+
// Use show/hide for selective exports
|
|
40
|
+
export 'src/models/user.dart' show User, UserRole;
|
|
41
|
+
export 'src/models/user.dart' hide InternalUserState;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## pubspec.yaml Best Practices
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
name: my_package
|
|
48
|
+
description: A concise description of what this package does.
|
|
49
|
+
version: 1.2.3
|
|
50
|
+
publish_to: none # Remove for pub.dev publishing
|
|
51
|
+
|
|
52
|
+
environment:
|
|
53
|
+
sdk: ^3.9.0 # Pin to minimum required version
|
|
54
|
+
|
|
55
|
+
dependencies:
|
|
56
|
+
http: ^1.2.0
|
|
57
|
+
riverpod: ^2.6.0
|
|
58
|
+
|
|
59
|
+
dev_dependencies:
|
|
60
|
+
test: ^1.25.0
|
|
61
|
+
mocktail: ^1.0.0
|
|
62
|
+
very_good_analysis: ^7.0.0 # Or flutter_lints
|
|
63
|
+
build_runner: ^2.4.0
|
|
64
|
+
json_serializable: ^6.8.0
|
|
65
|
+
|
|
66
|
+
# For monorepo workspaces (Dart 3.5+)
|
|
67
|
+
workspace:
|
|
68
|
+
- packages/core
|
|
69
|
+
- packages/api
|
|
70
|
+
- packages/app
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## analysis_options.yaml
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
# Use a lint package as base
|
|
77
|
+
include: package:very_good_analysis/analysis_options.yaml
|
|
78
|
+
|
|
79
|
+
analyzer:
|
|
80
|
+
language:
|
|
81
|
+
strict-casts: true # No implicit casts from dynamic
|
|
82
|
+
strict-inference: true # No implicit dynamic types
|
|
83
|
+
strict-raw-types: true # No raw generic types
|
|
84
|
+
|
|
85
|
+
errors:
|
|
86
|
+
# Treat these as errors in CI
|
|
87
|
+
unused_import: error
|
|
88
|
+
unused_local_variable: warning
|
|
89
|
+
dead_code: warning
|
|
90
|
+
todo: ignore
|
|
91
|
+
|
|
92
|
+
exclude:
|
|
93
|
+
- "**/*.g.dart" # Generated code
|
|
94
|
+
- "**/*.freezed.dart"
|
|
95
|
+
- "build/**"
|
|
96
|
+
|
|
97
|
+
linter:
|
|
98
|
+
rules:
|
|
99
|
+
# Additional rules beyond the base package
|
|
100
|
+
- prefer_single_quotes
|
|
101
|
+
- require_trailing_commas
|
|
102
|
+
- sort_constructors_first
|
|
103
|
+
- unawaited_futures # Catch missing awaits
|
|
104
|
+
- close_sinks # Catch unclosed StreamControllers
|
|
105
|
+
- cancel_subscriptions # Catch uncancelled subscriptions
|
|
106
|
+
- avoid_dynamic_calls # Prevent calls on dynamic
|
|
107
|
+
- prefer_const_constructors
|
|
108
|
+
- prefer_const_declarations
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Dependency Audit Workflow
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Check for outdated dependencies
|
|
115
|
+
dart pub outdated
|
|
116
|
+
|
|
117
|
+
# Upgrade to latest compatible versions
|
|
118
|
+
dart pub upgrade
|
|
119
|
+
|
|
120
|
+
# Upgrade to latest major versions (breaking changes)
|
|
121
|
+
dart pub upgrade --major-versions
|
|
122
|
+
|
|
123
|
+
# Audit dependency tree
|
|
124
|
+
dart pub deps
|
|
125
|
+
|
|
126
|
+
# Check for known vulnerabilities (if publishing)
|
|
127
|
+
dart pub publish --dry-run
|
|
128
|
+
|
|
129
|
+
# Run full analysis
|
|
130
|
+
dart analyze
|
|
131
|
+
dart format --set-exit-if-changed .
|
|
132
|
+
dart test --coverage
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Part and Part-of Directives
|
|
136
|
+
|
|
137
|
+
```dart
|
|
138
|
+
// Use part files for code generation (json_serializable, freezed)
|
|
139
|
+
// user.dart
|
|
140
|
+
import 'package:json_annotation/json_annotation.dart';
|
|
141
|
+
|
|
142
|
+
part 'user.g.dart'; // generated code
|
|
143
|
+
|
|
144
|
+
@JsonSerializable()
|
|
145
|
+
class User {
|
|
146
|
+
final String name;
|
|
147
|
+
final int age;
|
|
148
|
+
|
|
149
|
+
User({required this.name, required this.age});
|
|
150
|
+
|
|
151
|
+
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
|
|
152
|
+
Map<String, dynamic> toJson() => _$UserToJson(this);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// DO NOT use part/part-of for manual code organization — use imports instead
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Library-Level Visibility
|
|
159
|
+
|
|
160
|
+
```dart
|
|
161
|
+
// src/ files are private to the package — not importable from outside
|
|
162
|
+
// Only files exported via barrel file are public API
|
|
163
|
+
|
|
164
|
+
// For package-internal but cross-file visibility, use @visibleForTesting
|
|
165
|
+
import 'package:meta/meta.dart';
|
|
166
|
+
|
|
167
|
+
class Cache {
|
|
168
|
+
@visibleForTesting
|
|
169
|
+
final Map<String, dynamic> store = {};
|
|
170
|
+
|
|
171
|
+
// ... public API
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Monorepo with Pub Workspaces
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
# Root pubspec.yaml
|
|
179
|
+
name: my_workspace
|
|
180
|
+
publish_to: none
|
|
181
|
+
environment:
|
|
182
|
+
sdk: ^3.9.0
|
|
183
|
+
workspace:
|
|
184
|
+
- packages/core
|
|
185
|
+
- packages/api_client
|
|
186
|
+
- packages/app
|
|
187
|
+
|
|
188
|
+
# packages/app/pubspec.yaml
|
|
189
|
+
dependencies:
|
|
190
|
+
core: # Resolved from workspace
|
|
191
|
+
api_client: # Resolved from workspace
|
|
192
|
+
resolution: workspace # Use workspace resolution
|
|
193
|
+
```
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Sealed Classes, Records, and Pattern Matching
|
|
2
|
+
|
|
3
|
+
## Sealed Class Hierarchies
|
|
4
|
+
|
|
5
|
+
```dart
|
|
6
|
+
// Sealed classes define closed type sets — switch must be exhaustive
|
|
7
|
+
sealed class AuthState {}
|
|
8
|
+
|
|
9
|
+
class Unauthenticated extends AuthState {}
|
|
10
|
+
class Authenticating extends AuthState {}
|
|
11
|
+
class Authenticated extends AuthState {
|
|
12
|
+
final User user;
|
|
13
|
+
Authenticated(this.user);
|
|
14
|
+
}
|
|
15
|
+
class AuthError extends AuthState {
|
|
16
|
+
final String message;
|
|
17
|
+
AuthError(this.message);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Exhaustive switch — compiler error if a variant is missing
|
|
21
|
+
Widget buildAuth(AuthState state) => switch (state) {
|
|
22
|
+
Unauthenticated() => const LoginScreen(),
|
|
23
|
+
Authenticating() => const LoadingSpinner(),
|
|
24
|
+
Authenticated(:final user) => HomeScreen(user: user),
|
|
25
|
+
AuthError(:final message) => ErrorScreen(message: message),
|
|
26
|
+
};
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Records for Lightweight Data
|
|
30
|
+
|
|
31
|
+
```dart
|
|
32
|
+
// Positional records — unnamed fields
|
|
33
|
+
(int, String) parseIdAndName(String input) {
|
|
34
|
+
final parts = input.split(':');
|
|
35
|
+
return (int.parse(parts[0]), parts[1]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Named fields
|
|
39
|
+
({double lat, double lng}) parseCoordinate(String s) {
|
|
40
|
+
final parts = s.split(',');
|
|
41
|
+
return (lat: double.parse(parts[0]), lng: double.parse(parts[1]));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Destructuring
|
|
45
|
+
final (id, name) = parseIdAndName('42:Alice');
|
|
46
|
+
final (:lat, :lng) = parseCoordinate('51.5,-0.1');
|
|
47
|
+
|
|
48
|
+
// Records have value equality
|
|
49
|
+
final a = (1, 'hello');
|
|
50
|
+
final b = (1, 'hello');
|
|
51
|
+
print(a == b); // true — structural equality
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Pattern Matching
|
|
55
|
+
|
|
56
|
+
### If-case Patterns
|
|
57
|
+
|
|
58
|
+
```dart
|
|
59
|
+
// Replace is + cast with if-case
|
|
60
|
+
// BAD
|
|
61
|
+
if (response is Map<String, dynamic>) {
|
|
62
|
+
final name = response['name'] as String?;
|
|
63
|
+
if (name != null) {
|
|
64
|
+
processName(name);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// GOOD — destructuring in if-case
|
|
69
|
+
if (response case {'name': String name}) {
|
|
70
|
+
processName(name); // name is already typed as String
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Nested patterns
|
|
74
|
+
if (json case {'user': {'name': String name, 'age': int age}}) {
|
|
75
|
+
print('$name is $age years old');
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Switch Expression Patterns
|
|
80
|
+
|
|
81
|
+
```dart
|
|
82
|
+
// Object patterns — match on properties
|
|
83
|
+
String describe(Shape shape) => switch (shape) {
|
|
84
|
+
Circle(radius: var r) when r > 100 => 'large circle',
|
|
85
|
+
Circle(radius: var r) => 'circle with radius $r',
|
|
86
|
+
Rectangle(width: var w, height: var h) when w == h => 'square ${w}x$h',
|
|
87
|
+
Rectangle(:var width, :var height) => 'rect ${width}x$height',
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Logical or patterns
|
|
91
|
+
String httpCategory(int status) => switch (status) {
|
|
92
|
+
200 || 201 || 204 => 'success',
|
|
93
|
+
301 || 302 => 'redirect',
|
|
94
|
+
400 || 422 => 'client error',
|
|
95
|
+
500 || 502 || 503 => 'server error',
|
|
96
|
+
_ => 'unknown',
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Guard clauses with when
|
|
100
|
+
String classify(num value) => switch (value) {
|
|
101
|
+
< 0 => 'negative',
|
|
102
|
+
== 0 => 'zero',
|
|
103
|
+
> 0 && < 100 => 'small positive',
|
|
104
|
+
>= 100 => 'large positive',
|
|
105
|
+
_ => 'NaN', // for double.nan
|
|
106
|
+
};
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Exhaustiveness Checking
|
|
110
|
+
|
|
111
|
+
```dart
|
|
112
|
+
// Sealed types + switch = compile-time exhaustiveness
|
|
113
|
+
sealed class Result<T> {}
|
|
114
|
+
class Success<T> extends Result<T> { final T value; Success(this.value); }
|
|
115
|
+
class Failure<T> extends Result<T> { final Exception error; Failure(this.error); }
|
|
116
|
+
|
|
117
|
+
// Compiler enforces both Success and Failure are handled
|
|
118
|
+
T unwrap<T>(Result<T> result) => switch (result) {
|
|
119
|
+
Success(:final value) => value,
|
|
120
|
+
Failure(:final error) => throw error,
|
|
121
|
+
// No default needed — sealed class makes this exhaustive
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Adding a new variant (e.g., Loading) causes compile errors everywhere
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Extension Types (Dart 3.3+)
|
|
128
|
+
|
|
129
|
+
```dart
|
|
130
|
+
// Zero-cost type wrappers — compiled away at runtime
|
|
131
|
+
extension type UserId(int value) {
|
|
132
|
+
// Only UserId-specific methods are available
|
|
133
|
+
bool get isValid => value > 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
extension type Email(String value) {
|
|
137
|
+
factory Email.parse(String input) {
|
|
138
|
+
if (!input.contains('@')) throw FormatException('Invalid email: $input');
|
|
139
|
+
return Email(input.toLowerCase());
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Type safety without runtime overhead
|
|
144
|
+
void sendEmail(Email to, UserId from) { /*...*/ }
|
|
145
|
+
|
|
146
|
+
// Can't accidentally pass raw int/String where UserId/Email is expected
|
|
147
|
+
sendEmail(Email.parse('alice@example.com'), UserId(42));
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Class Modifiers (Dart 3.0+)
|
|
151
|
+
|
|
152
|
+
```dart
|
|
153
|
+
// interface class — can implement but not extend
|
|
154
|
+
interface class Serializable {
|
|
155
|
+
Map<String, dynamic> toJson();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// base class — can extend but not implement
|
|
159
|
+
base class BaseRepository {
|
|
160
|
+
Future<void> connect() async { /*...*/ }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// final class — cannot be extended or implemented outside the library
|
|
164
|
+
final class InternalConfig {
|
|
165
|
+
final String apiKey;
|
|
166
|
+
InternalConfig(this.apiKey);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// mixin class — can be used as both mixin and class
|
|
170
|
+
mixin class Logging {
|
|
171
|
+
void log(String message) => print('[LOG] $message');
|
|
172
|
+
}
|
|
173
|
+
```
|