domains/build-agent-dart/SKILL.md
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.
npx skillsauth add agile-v/agile_v_skills build-agent-dartInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
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):
This skill participates in 4 of 6 SCOPE-V phases (see agile-v-core for full framework):
Not participating: Specify (Requirement Architect), Verify (Red Team Verifier)
Flutter App 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:
Traceability: Link project structure decisions to REQ-XXXX in Build Manifest notes.
Null Safety:
! (null assertion); prefer null-aware operators// Parent: REQ-0001
// Good: Null-aware operators
String getUserName(User? user) {
return user?.name ?? 'Guest';
}
Const Constructors:
const for immutable widgets and objects (performance)// 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.
pubspec.yaml Structure:
# 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:
^) for compatible updates: ^1.2.3 allows >=1.2.3 <2.0.01.2.3Lock Files:
pubspec.lock for apps (reproducible builds)pubspec.lock for packages (allow version flexibility)Traceability: Link dependency choices to REQ-XXXX in Build Manifest notes.
Stateless vs Stateful:
Widget Composition:
// 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:
// 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.
BLoC (Business Logic Component) - PRIMARY:
// 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):
Traceability: Document state management choice in Build Manifest notes with REQ justification.
go_router (Declarative Routing):
// 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.
MethodChannel (Request/Response):
// 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:
Halt Condition: Halt if platform channel handles sensitive data without documented security review.
Clean Architecture:
Feature-First Architecture:
Traceability: Document architecture choice in Build Manifest notes with REQ justification.
Secure Storage:
// 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:
// 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:
// 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:
Secure Coding (inherited from build-agent + Dart-specific):
Halt Condition: Halt if hardcoded secrets detected in code.
Unit Tests:
// 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: '[email protected]', name: 'Test');
when(mockRepository.login(
email: '[email protected]',
password: 'password',
)).thenAnswer((_) async => user);
expectLater(
authBloc.stream,
emitsInOrder([
isA<AuthLoading>(),
isA<AuthAuthenticated>(),
]),
);
authBloc.add(LoginRequested(
email: '[email protected]',
password: 'password',
));
});
});
}
Widget Tests:
// 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:
// 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')),
'[email protected]',
);
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:
flutter test --coverageBug Fixes:
Alignment: Test Designer (TC-XXXX) defines tests; Build Agent structures code for testability.
Build Flavors:
# Parent: REQ-0026
flutter build apk --flavor dev -t lib/main_dev.dart
flutter build apk --flavor prod -t lib/main_prod.dart
Code Generation:
flutter pub run build_runner buildPlatform-Specific Configuration:
Halt Condition: Halt if platform permissions added without documentation in Build Manifest notes.
Inherits R0-R3 framework from agile-v-compliance. Dart/Flutter-specific additions below.
Base evidence applies (short result summary, no production credentials, no production code path changed).
Dart/Flutter-Specific: No additions.
Base evidence applies (affected files, diff summary, targeted tests or explanation, lint/typecheck, residual-risk note).
Dart/Flutter-Specific Additions:
dart analyze output (no errors)flutter test output for affected modulesBase 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:
flutter test integration_test/)flutter build apk and flutter build ios successfulBase evidence applies (all R2 evidence + independent verification agent review, traceability matrix, explicit human sign-off, audit artifact, release decision rationale).
Dart/Flutter-Specific Additions:
Halt and do not emit when:
Inherited from build-agent:
Dart/Flutter-Specific:
dart analyze fails for R2+ tasks without documented exceptions)Halt Protocol:
Inherited from build-agent + these Dart/Flutter considerations:
Pre-Execution Validation (inherited from build-agent): Before synthesis, validate:
Halt if any validation fails.
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
Project Types:
Auto-Trigger Hints (for agent routing):
pubspec.yaml dependencies:
flutterflutter_blocproviderriverpodgetdiohttpgo_routershelfdart_frogFile patterns:
**/*.dart**/pubspec.yaml**/analysis_options.yaml**/lib/**/*.dart**/test/**/*.dart**/integration_test/**/*.dartTask keywords:
development
The Verification Agent — challenges Build Agent artifacts via independent verification. Executes tests against artifacts. Use to audit code, schematics, or firmware against requirements.
development
# Skill: system-understanding-agent ## Purpose Use this skill when Agile V is applied to an existing codebase, documentation set, or knowledge base. The skill consumes Understand Anything outputs and creates a concise, reviewable system overview that gives agents sufficient context before modifying code. This is **Gate 0** of the integrated Agile V lifecycle. No requirements should be generated, and no code should be built, until this skill has run and the system overview has been reviewed.
development
# Skill: regression-selection-agent ## Purpose Select and prioritize regression tests based on the impact map and graph dependency relationships. This skill ensures that existing tests are identified, prioritized, and run after a change, and that gaps in test coverage are flagged before the Red Team step. --- ## Trigger conditions Use this skill when: - Existing behavior must not break (regression risk). - An impact map is available. - The change affects shared modules, services, or APIs.
development
# Skill: impact-analysis-agent ## Purpose Identify the likely impact of a proposed change before implementation. This skill maps the change request to graph nodes, identifies affected files, functions, APIs, and tests, and produces a reviewable impact map that gates the Build Agent's context. --- ## Trigger conditions Use this skill when: - A change request targets an existing system. - The change could affect multiple files or modules. - Regression risk exists (the change touches shared c