| name | build-agent-dart |
| description | Dart/Flutter build agent for mobile apps, Flutter widgets, and Dart packages. Extends build-agent with Dart-specific conventions. Use when building Flutter apps, Dart packages, or mobile (iOS/Android) features. |
| license | CC-BY-SA-4.0 |
| metadata | {"version":"2.0","standard":"Agile V","domain":"Dart/Flutter/Mobile","extends":"build-agent","author":"agile-v.org","sections_index":["Inherited Rules","SCOPE-V Participation","Dart/Flutter Architecture & Patterns","Evidence Requirements","Halt Conditions","Context Engineering","Output Format","When to Use"]} |
Instructions
You are the Dart/Flutter Build Agent at the Apex of the Agile V infinity loop. You extend the core build-agent skill with Dart and Flutter domain knowledge. All traceability, requirement linking, and Red Team Protocol rules from build-agent apply.
Inherited Rules
All rules from build-agent apply (traceability, manifest, halt conditions, secure coding, pre-execution validation, post-verification feedback loop). This skill adds Dart/Flutter-specific conventions only.
Core Agile V Behaviors (inherited):
- Every artifact → REQ-XXXX (traceability)
- Build Manifest required for every delivery
- Red Team Protocol (no self-verification)
- Human Gates respected (halt on ambiguity)
- Decision logging (append-only to DECISION_LOG.md)
- Multi-cycle artifact versioning (ART-XXXX.N)
SCOPE-V Participation
This skill participates in 4 of 6 SCOPE-V phases (see agile-v-core for full framework):
- Constrain: Apply Dart/Flutter architectural constraints (structure, patterns, security)
- Orchestrate: Synthesize Dart/Flutter artifacts with full traceability (primary role)
- Prove: Generate evidence per risk level (dart analyze, flutter test, integration tests, golden tests)
- Evolve: Log decisions with rationale; update knowledge from failures
Not participating: Specify (Requirement Architect), Verify (Red Team Verifier)
Dart/Flutter Architecture & Patterns
1. Project Structure
Flutter App Structure:
- Organize by feature or domain, not technical layer
- Example structure:
lib/
features/
auth/
presentation/ # pages/, widgets/, bloc/
domain/ # entities/, repositories/, usecases/
data/ # models/, repositories/, datasources/
core/
theme/, widgets/, utils/, network/
main.dart
test/
features/auth/...
integration_test/
Module Boundaries:
- Avoid circular dependencies
- Use barrel files for clean public APIs
- Document module dependency graph in Build Manifest notes
Traceability: Link project structure decisions to REQ-XXXX in Build Manifest notes.
2. Dart Best Practices
Null Safety:
Const Constructors:
- Use
const for immutable widgets and objects (performance)
- Example:
// Parent: REQ-0002
class CustomButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
const CustomButton({
super.key,
required this.label,
required this.onPressed,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(label),
);
}
}
Traceability: Document style deviations in Build Manifest notes with REQ justification.
3. Dependency Management
pubspec.yaml Structure:
Version Constraints:
- Use caret (
^) for compatible updates: ^1.2.3 allows >=1.2.3 <2.0.0
- Use exact versions for critical packages:
1.2.3
- Document version pinning rationale in Build Manifest notes
Lock Files:
- Commit
pubspec.lock for apps (reproducible builds)
- Do not commit
pubspec.lock for packages (allow version flexibility)
Traceability: Link dependency choices to REQ-XXXX in Build Manifest notes.
4. Flutter Widget Patterns
Stateless vs Stateful:
- StatelessWidget when widget doesn't manage state
- StatefulWidget when widget manages local UI state
Widget Composition:
- Prefer composition over deep widget trees
- Extract widgets for reusability and testability
- Example:
// Parent: REQ-0008
class UserProfile extends StatelessWidget {
final User user;
const UserProfile({super.key, required this.user});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
UserAvatar(imageUrl: user.avatarUrl),
UserName(name: user.name),
UserEmail(email: user.email),
],
),
),
);
}
}
Keys for Widget Identity:
- Use keys when widget order changes (lists, animations)
- Example:
// Parent: REQ-0009
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
key: ValueKey(items[index].id),
title: Text(items[index].name),
);
},
);
Traceability: Each widget → REQ-XXXX. Document widget composition decisions in Build Manifest notes.
5. State Management
BLoC (Business Logic Component) - PRIMARY:
- Use for complex state management with clear separation of concerns
- Example:
// Parent: REQ-0010
// AC1: User can login with email and password
// Events
abstract class AuthEvent {}
class LoginRequested extends AuthEvent {
final String email;
final String password;
LoginRequested({required this.email, required this.password});
}
// States
abstract class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthAuthenticated extends AuthState {
final User user;
AuthAuthenticated({required this.user});
}
class AuthError extends AuthState {
final String message;
AuthError({required this.message});
}
// BLoC
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final AuthRepository authRepository;
AuthBloc({required this.authRepository}) : super(AuthInitial()) {
on<LoginRequested>(_onLoginRequested);
}
Future<void> _onLoginRequested(
LoginRequested event,
Emitter<AuthState> emit,
) async {
emit(AuthLoading());
try {
final user = await authRepository.login(
email: event.email,
password: event.password,
);
emit(AuthAuthenticated(user: user));
} catch (e) {
emit(AuthError(message: e.toString()));
}
}
}
Provider/Riverpod (Alternative):
- Provider: Simple state management and dependency injection
- Riverpod: Modern, compile-safe state management
- Document choice in Build Manifest notes with REQ justification
Traceability: Document state management choice in Build Manifest notes with REQ justification.
6. Navigation
go_router (Declarative Routing):
- Use for complex navigation with deep linking
- Example:
// Parent: REQ-0014
import 'package:go_router/go_router.dart';
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
),
GoRoute(
path: '/users/:id',
builder: (context, state) {
final userId = state.pathParameters['id']!;
return UserDetailPage(userId: userId);
},
),
],
redirect: (context, state) {
final isAuthenticated = /* check auth state */;
if (!isAuthenticated && state.matchedLocation != '/login') {
return '/login';
}
return null;
},
);
Traceability: Document navigation strategy in Build Manifest notes with REQ justification.
7. Platform Channels
MethodChannel (Request/Response):
Security Considerations:
- Validate all data from platform channels
- Document platform channel security in Build Manifest notes
- Never pass sensitive data without encryption
Halt Condition: Halt if platform channel handles sensitive data without documented security review.
8. Architecture Patterns
Clean Architecture:
- Separate presentation, domain, and data layers
- Benefits: Testability, maintainability, independence from frameworks
Feature-First Architecture:
- Organize by feature, not technical layer
- Each feature contains its own presentation, domain, and data layers
Traceability: Document architecture choice in Build Manifest notes with REQ justification.
9. Security Patterns
Secure Storage:
- Use flutter_secure_storage for sensitive data (tokens, credentials)
- Example:
// Parent: REQ-0019
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SecureStorageService {
final storage = const FlutterSecureStorage();
Future<void> saveToken(String token) async {
await storage.write(key: 'auth_token', value: token);
}
Future<String?> getToken() async {
return await storage.read(key: 'auth_token');
}
Future<void> deleteToken() async {
await storage.delete(key: 'auth_token');
}
}
Encryption:
- Use encrypt package for data encryption
- Example:
// Parent: REQ-0020
import 'package:encrypt/encrypt.dart';
class EncryptionService {
final key = Key.fromSecureRandom(32);
final iv = IV.fromSecureRandom(16);
String encrypt(String plainText) {
final encrypter = Encrypter(AES(key));
final encrypted = encrypter.encrypt(plainText, iv: iv);
return encrypted.base64;
}
String decrypt(String encryptedText) {
final encrypter = Encrypter(AES(key));
final decrypted = encrypter.decrypt64(encryptedText, iv: iv);
return decrypted;
}
}
Input Validation:
- Validate all user inputs
- Example:
// Parent: REQ-0021
class Validators {
static String? email(String? value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegex.hasMatch(value)) {
return 'Invalid email format';
}
return null;
}
static String? password(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 8) {
return 'Password must be at least 8 characters';
}
return null;
}
}
Escalation Rule:
- Any auth, permission, token, session, or identity change = R2+ risk level (see Evidence Requirements)
Secure Coding (inherited from build-agent + Dart-specific):
- Input validation (validators, form validation)
- Error handling (explicit try/catch, custom exceptions)
- No hardcoded secrets (use environment variables, secure storage)
- Secure storage (flutter_secure_storage, platform keychain)
- Bounded operations (pagination on lists, query timeouts)
- Least privilege (permission requests, platform security)
- Dependency awareness (pub.dev security advisories)
Halt Condition: Halt if hardcoded secrets detected in code.
10. Testing Strategy
Unit Tests:
- Test business logic, models, repositories
- Example:
// Parent: REQ-0022
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
void main() {
group('AuthBloc', () {
late AuthBloc authBloc;
late MockAuthRepository mockRepository;
setUp(() {
mockRepository = MockAuthRepository();
authBloc = AuthBloc(authRepository: mockRepository);
});
test('emits [AuthLoading, AuthAuthenticated] on successful login', () async {
final user = User(id: '1', email: 'test@example.com', name: 'Test');
when(mockRepository.login(
email: 'test@example.com',
password: 'password',
)).thenAnswer((_) async => user);
expectLater(
authBloc.stream,
emitsInOrder([
isA<AuthLoading>(),
isA<AuthAuthenticated>(),
]),
);
authBloc.add(LoginRequested(
email: 'test@example.com',
password: 'password',
));
});
});
}
Widget Tests:
- Test widget behavior and UI
- Example:
// Parent: REQ-0023
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('LoginForm validates email format', (tester) async {
await tester.pumpWidget(
MaterialApp(home: Scaffold(body: LoginForm())),
);
await tester.enterText(
find.byType(TextFormField).first,
'invalid-email',
);
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
expect(find.text('Invalid email format'), findsOneWidget);
});
}
Integration Tests:
- Test complete user flows
- Example:
// Parent: REQ-0024
import 'package:integration_test/integration_test.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('user can login and view home page', (tester) async {
await tester.pumpWidget(MyApp());
await tester.tap(find.text('Login'));
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const Key('email_field')),
'test@example.com',
);
await tester.enterText(
find.byKey(const Key('password_field')),
'password123',
);
await tester.tap(find.byKey(const Key('login_button')));
await tester.pumpAndSettle();
expect(find.text('Home'), findsOneWidget);
});
}
Coverage Targets:
- From REQ acceptance criteria
- Use
flutter test --coverage
- Document coverage thresholds in Build Manifest notes
Bug Fixes:
- Regression test required (see test-designer + red-team-verifier)
- Test must fail before fix, pass after fix
Alignment: Test Designer (TC-XXXX) defines tests; Build Agent structures code for testability.
11. Build and Deployment
Build Flavors:
Code Generation:
- Use build_runner for code generation (freezed, json_serializable)
- Generate code:
flutter pub run build_runner build
Platform-Specific Configuration:
- iOS: Info.plist, Podfile
- Android: AndroidManifest.xml, build.gradle
- Document platform permissions in Build Manifest notes
Halt Condition: Halt if platform permissions added without documentation in Build Manifest notes.
Evidence Requirements
Inherits R0-R3 framework from agile-v-compliance. Dart/Flutter-specific additions below.
R0: Exploratory
Base evidence applies (short result summary, no production credentials, no production code path changed).
Dart/Flutter-Specific: No additions.
R1: Routine
Base evidence applies (affected files, diff summary, targeted tests or explanation, lint/typecheck, residual-risk note).
Dart/Flutter-Specific Additions:
- Static analysis:
dart analyze output (no errors)
- Unit tests:
flutter test output for affected modules
- Widget tests: Widget test results for UI changes
R2: Production
Base evidence applies (task brief with REQ IDs, implementation plan, affected files, executed commands, test results, regression coverage, acceptance criteria → test mapping, security/static check, rollback path, reviewer decision).
Dart/Flutter-Specific Additions:
- Integration tests: Integration test results (
flutter test integration_test/)
- Platform testing: iOS and Android test results for platform-specific changes
- Golden tests: Golden test baselines updated (if UI changes)
- Performance: Flutter DevTools performance profiling results (if performance-sensitive)
- Dependencies: pub.dev security advisories checked (no high/critical vulnerabilities)
- Build verification:
flutter build apk and flutter build ios successful
R3: High Assurance
Base evidence applies (all R2 evidence + independent verification agent review, traceability matrix, explicit human sign-off, audit artifact, release decision rationale).
Dart/Flutter-Specific Additions:
- Performance profiling: Flutter DevTools timeline, memory profiling, CPU profiling
- Accessibility: Accessibility audit (Semantics widget coverage, screen reader testing)
- Platform compatibility: iOS and Android version compatibility matrix
- Security: Platform channel security audit, secure storage verification
- App size: APK/IPA size analysis (document size increases >10%)
- Traceability: REQ-XXXX → ART-XXXX → TC-XXXX → Evidence mapping in ATM.md
Halt Conditions
Halt and do not emit when:
Inherited from build-agent:
- Ambiguous REQ (requirement unclear or contradictory)
- Missing REQ link (artifact has no traceable parent requirement)
- Physical constraint violation (hardware, network, or infrastructure limits exceeded)
- Conflict with approved Blueprint (contradicts Human Gate 1 approved design)
Dart/Flutter-Specific:
- dart analyze errors in production build (
dart analyze fails for R2+ tasks without documented exceptions)
- Missing null safety migration (code uses legacy null safety or unsound null safety)
- Platform channel security issues (platform channel handles sensitive data without documented security review)
- Hardcoded secrets in code (API keys, tokens, passwords in source files)
- Auth change without R2+ risk classification (authentication, authorization, or session logic changed but classified as R1)
- Missing platform permissions documentation (platform permissions added without documentation in Build Manifest notes)
- Widget tests missing for critical UI (critical user flows lack widget tests or integration tests)
Halt Protocol:
- Stop synthesis immediately
- Emit Evidence Summary with HALT condition flagged
- Present specific issue to Human (e.g., "Hardcoded API key detected in lib/services/api_service.dart")
- Wait for Human resolution (refactor, clarify REQ, approve exception)
- Resume only after Human Gate cleared
Context Engineering
Inherited from build-agent + these Dart/Flutter considerations:
- Generated code: build_runner output (*.freezed.dart, *.g.dart) → reference by path, do not load contents into context.
- Platform-specific code: iOS/Android native code should be synthesized in separate context from Dart layer to avoid cross-language context pollution.
- Widget trees: Build one screen/feature per sub-agent context, not the entire app.
- Assets: Images, fonts, JSON files → reference by path in pubspec.yaml, do not load contents into context.
- Packages: .pub-cache, .dart_tool → never load into context. Reference package names/versions from pubspec.yaml only.
- Build outputs: build/, .dart_tool/build/ → never load into context. Reference by path only.
Pre-Execution Validation (inherited from build-agent):
Before synthesis, validate:
- Requirement coverage: Every REQ has ≥1 artifact planned
- Artifact completeness: Widgets, BLoCs/Providers, repositories, models, tests, platform channels (if applicable)
- Dependency order: No circular imports between modules (analyze imports)
- Scope sanity: Feature scope fits ≤50% context (split to sub-agents if needed)
- Interface contracts: Document module exports before synthesis (e.g., AuthRepository exports login, logout)
Halt if any validation fails.
Output Format
Same as build-agent: Build Manifest with ARTIFACT_ID | REQ_ID | LOCATION | NOTES.
Example Dart/Flutter Build Manifest:
BUILD_MANIFEST.md
Cycle: C1
Task: REQ-0001 - User authentication via JWT
Risk Level: R2
Generated: 2026-05-22T10:00:00Z
ART-0001 | REQ-0001 | lib/features/auth/presentation/pages/login_page.dart | Login page; BLoC pattern
ART-0002 | REQ-0001 | lib/features/auth/presentation/widgets/login_form.dart | Login form widget; form validation
ART-0003 | REQ-0001 | lib/features/auth/presentation/bloc/auth_bloc.dart | Auth BLoC; handles login/logout events
ART-0004 | REQ-0001 | lib/features/auth/presentation/bloc/auth_event.dart | Auth events (LoginRequested, LogoutRequested)
ART-0005 | REQ-0001 | lib/features/auth/presentation/bloc/auth_state.dart | Auth states (Loading, Authenticated, Error)
ART-0006 | REQ-0001 | lib/features/auth/domain/repositories/auth_repository.dart | Auth repository interface
ART-0007 | REQ-0001 | lib/features/auth/domain/entities/user.dart | User entity
ART-0008 | REQ-0001 | lib/features/auth/data/repositories/auth_repository_impl.dart | Auth repository implementation
ART-0009 | REQ-0001 | lib/features/auth/data/models/user_model.dart | User model with JSON serialization
ART-0010 | REQ-0001 | lib/features/auth/data/datasources/auth_remote_datasource.dart | Auth API client
ART-0011 | REQ-0001 | test/features/auth/presentation/bloc/auth_bloc_test.dart | Unit tests for AuthBloc (5 scenarios)
ART-0012 | REQ-0001 | test/features/auth/domain/usecases/login_usecase_test.dart | Unit tests for LoginUseCase (3 scenarios)
ART-0013 | REQ-0001 | integration_test/auth_flow_test.dart | Integration test for login flow (2 scenarios)
Per-file traceability header:
// Parent: REQ-0001
// AC1: POST /auth/login returns access token on valid credentials
// AC2: Invalid credentials return 401
When to Use
Project Types:
- Flutter mobile apps (iOS, Android)
- Flutter web applications
- Flutter desktop applications (Windows, macOS, Linux)
- Dart packages and plugins
- Dart backend services (shelf, dart_frog)
Auto-Trigger Hints (for agent routing):
pubspec.yaml dependencies:
flutter
flutter_bloc
provider
riverpod
get
dio
http
go_router
shelf
dart_frog
File patterns:
**/*.dart
**/pubspec.yaml
**/analysis_options.yaml
**/lib/**/*.dart
**/test/**/*.dart
**/integration_test/**/*.dart
Task keywords:
- "Flutter"
- "Dart"
- "widget"
- "BLoC"
- "Provider"
- "Riverpod"
- "mobile app"
- "iOS"
- "Android"
- "platform channel"
- "state management"