- 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.3","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):**
- Synthesis artifacts → `implements` → baselined REQ revision (typed lineage)
- 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:**
- Mandatory sound null safety
- Avoid `!` (null assertion); prefer null-aware operators
- Example:
```dart
// Parent: REQ-0001
// Good: Null-aware operators
String getUserName(User? user) {
return user?.name ?? 'Guest';
}
```
**Const Constructors:**
- Use `const` for immutable widgets and objects (performance)
- Example:
```dart
// 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:**
- Separate dependencies from dev_dependencies
- Use version constraints for stability
- Example:
```yaml
# Parent: REQ-0006
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^8.1.3
dio: ^5.3.2
go_router: ^12.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
mockito: ^5.4.2
```
**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:
```dart
// 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:
```dart
// 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:
```dart
// 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:
```dart
// 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):**
- Use for calling platform-specific code (iOS/Android)
- Example:
```dart
// Parent: REQ-0016
// AC1: Get battery level from platform
import 'package:flutter/services.dart';
class BatteryService {
static const platform = MethodChannel('com.example.app/battery');
Future<int?> getBatteryLevel() async {
try {
final int result = await platform.invokeMethod('getBatteryLevel');
return result;
} on PlatformException catch (e) {
debugPrint('Failed to get battery level: ${e.message}');
return null;
}
}
}
```
**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:
```dart
// 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:
```dart
// 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:
```dart
// 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 = L2+ risk level (see `docs/agile-v-runtime/04_RISK_CLASSIFICATION.md`)
**Secure Coding (inherited from build-agent + Dart-specific):**
1. Input validation (validators, form validation)
2. Error handling (explicit try/catch, custom exceptions)
3. No hardcoded secrets (use environment variables, secure storage)
4. Secure storage (flutter_secure_storage, platform keychain)
5. Bounded operations (pagination on lists, query timeouts)
6. Least privilege (permission requests, platform security)
7. 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:
```dart
// 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',
));
Ver no GitHub