SKILL.md
Flutter API Integration
Full data layer: REST APIs, Firebase, Supabase, JSON serialization, authentication, offline caching, and the repository pattern that keeps all of it testable.
Guiding principle: The UI layer never talks to a network. UI → providers → repositories → datasources → network.
Workflow
- Inspect the backend before writing code — query MCP tools first:
- Firebase project: use firebase MCP (firestore/listCollections, auth/listProviders, storage/listBuckets, functions/list) - Supabase project: use supabase MCP (listtables, executesql to validate queries) - If no live backend yet: document all schema assumptions in the feature spec before starting.
- Check current package versions — query
context7for current Dio, Firebase, or Supabase SDK docs before writing integration code (APIs change across major versions). - Design the folder structure — see
@references/feature-layer-template.mdfor the canonical layout. - Implement in order: models/DTOs → datasource interface → datasource impl → repository interface → repository impl → provider/cubit.
- Map exceptions at the datasource boundary — datasources throw
AppException; repositories returnEither<Failure, T>. See@references/dart-error-mapping.md. - Add tests: mock the datasource interface, never mock concrete
Dioorhttp.Client. - Validate:
dart format .+flutter analyze+flutter test.
Integration Decision Rules
| Backend | Use when |
|---|---|
| REST + Dio | Custom backend, third-party APIs, full control over headers/interceptors |
| Firebase Auth + Firestore | Real-time data, Google ecosystem, fast MVP |
| Supabase | PostgreSQL-backed apps, open-source Firebase alternative |
| GraphQL | Complex relational data, bandwidth-critical apps |
Token storage: Always fluttersecurestorage for access/refresh tokens. Never SharedPreferences (unencrypted on Android).
Offline-first cache policy:
cache-then-network— return cache immediately, update in background (best UX)network-only— always fresh, use for mutationscache-only— for offline mode toggle
Dio Setup Checklist
BaseOptionswithbaseUrl,connectTimeout(10 s),receiveTimeout(30 s)AuthInterceptorthat attaches Bearer token and handles 401 → token refresh → retryPrettyDioLoggerin debug mode only- All
DioExceptioncaught at datasource boundary, converted withmapDioException()
JSON Serialization Rules
- Use
json_serializable+freezedfor all DTOs — never writefromJson/toJsonmanually. - Add
toDomain()extension on each DTO to convert to the domain entity (keeps domain layer free of serialization). - Run codegen:
dart run build_runner build --delete-conflicting-outputs. - DTO fields: use
@JsonKey(name: 'snakecase')when backend uses snakecase.
Mocking Strategy for Tests
- Define datasources as abstract interfaces (
abstract interface class). - Use
mocktailfor mocks of the interface; never mockDiodirectly. - Use
FakeDatasourceimplementations for complex scenarios. - Define all mocks in
test/helpers/mocks.dart.
Security Rules
- Never hardcode API keys, base URLs, or secrets in Dart source.
- Use
const String.fromEnvironment('KEY')passed via--dart-define-from-file. - Add
.env,google-services.json,GoogleService-Info.plistto.gitignore. - Provide
.env.examplewith placeholder values for onboarding.
Output Artifacts
lib/core/network/dio_client.dart— Dio factory with interceptorslib/core/network/exception_mapper.dart— DioException → AppExceptionlib/core/errors/failures.dart— Failure sealed class hierarchylib/features/<feature>/data/models/— DTOslib/features/<feature>/data/datasources/— remote + local datasource interfaces and implslib/features/<feature>/data/repositories/— repository implementationstest/features/<feature>/— datasource and repository unit tests
Cross-references
- Agent:
api-integration-engineer - See
@references/dart-error-mapping.mdfor AppException hierarchy and DioException mapping - See
@references/feature-layer-template.mdfor full folder structure - MCP tools:
firebase,supabase,context7