flutter-clean-arch
npx skills add https://github.com/duckyman-ai/agent-skills --skill flutter-clean-arch
Agent 安装分布
Skill 文档
Flutter Clean Architecture Skill
Generate Flutter applications following Clean Architecture principles with feature-first organization, Riverpod for state management, and functional error handling using fpdart.
Includes Dio + Retrofit for type-safe REST API calls.
Core Principles
Architecture: Clean Architecture (Feature-First)
- Domain layer: Pure business logic, no dependencies
- Data layer: Data sources, repositories implementation, data models
- Presentation layer: UI, state management, view models
Dependency Rule: Presentation â Domain â Data (Domain has no external dependencies)
State Management: Riverpod 3.0+ with code generation
Note: Riverpod 3.0+ & Freezed 3.0+ Required
Riverpod 3.0+: The
XxxReftypes (likeDioRef,UserRepositoryRef, etc.) have been removed in favor of a unifiedReftype.Riverpod 2.x (Legacy):
SomeType someType(SomeTypeRef ref) { ... }Riverpod 3.x+ (Current):
SomeType someType(Ref ref) { ... }Freezed 3.0+: Requires
sealedkeyword for union types with Dart 3.3.Freezed 2.x (Legacy):
class Failure with _$Failure { ... }Freezed 3.x+ (Current):
sealed class Failure with _$Failure { ... }Required versions: This skill requires Riverpod 3.0+ and Freezed 3.0+. Check your version with
flutter pub deps | grep riverpod.
Error Handling: fpdart’s Either<Failure, T> for functional error handling
Networking: Dio + Retrofit for type-safe REST API calls
Project Structure
lib/
âââ core/
â âââ constants/
â â âââ api_constants.dart
â âââ errors/
â â âââ failures.dart
â â âââ network_exceptions.dart
â âââ network/
â â âââ dio_provider.dart
â â âââ interceptors/
â â âââ auth_interceptor.dart
â â âââ logging_interceptor.dart
â â âââ error_interceptor.dart
â âââ storage/
â âââ services/
â âââ router/
â â âââ app_router.dart
â âââ utils/
âââ shared/
âââ features/
â âââ [feature_name]/
â âââ data/
â â âââ models/
â â â âââ [entity]_model.dart
â â âââ datasources/
â â â âââ [feature]_api_service.dart
â â âââ repositories/
â â âââ [feature]_repository_impl.dart
â âââ domain/
â â âââ entities/
â â âââ repositories/
â â â âââ [feature]_repository.dart
â â âââ usecases/
â â âââ [action]_usecase.dart
â âââ presentation/
â âââ providers/
â â âââ [feature]_provider.dart
â âââ screens/
â â âââ [feature]_screen.dart
â âââ widgets/
â âââ [feature]_widget.dart
âââ main.dart
Quick Start
1. Domain Layer (Entities, Repository Interfaces, UseCases)
// Entity
sealed class User with _$User {
const factory User({
required String id,
required String name,
required String email,
}) = _User;
}
// Repository Interface
abstract class UserRepository {
Future<Either<Failure, User>> getUser(String id);
}
// UseCase
class GetUser {
final UserRepository repository;
GetUser(this.repository);
Future<Either<Failure, User>> call(String id) => repository.getUser(id);
}
2. Data Layer (Models, API Service, Repository Implementation)
// Model with JSON serialization
sealed class UserModel with _$UserModel {
const UserModel._();
const factory UserModel({
required String id,
required String name,
required String email,
}) = _UserModel;
factory UserModel.fromJson(Map<String, dynamic> json) => _$UserModelFromJson(json);
User toEntity() => User(id: id, name: name, email: email);
}
// Retrofit API Service
()
abstract class UserApiService {
factory UserApiService(Dio dio) = _UserApiService;
('/users/{id}')
Future<UserModel> getUser(('id') String id);
}
// Repository Implementation
class UserRepositoryImpl implements UserRepository {
final UserApiService apiService;
Future<Either<Failure, User>> getUser(String id) async {
try {
final userModel = await apiService.getUser(id);
return Right(userModel.toEntity());
} on DioException catch (e) {
return Left(Failure.network(NetworkExceptions.fromDioError(e).message));
}
}
}
3. Presentation Layer (Providers, Screens)
// Provider
UserApiService userApiService(Ref ref) {
return UserApiService(ref.watch(dioProvider));
}
UserRepositoryImpl userRepository(Ref ref) {
return UserRepositoryImpl(ref.watch(userApiServiceProvider));
}
class UserNotifier extends _$UserNotifier {
FutureOr<User?> build() => null;
Future<void> fetchUser(String id) async {
state = const AsyncLoading();
final result = await ref.read(userRepositoryProvider).getUser(id);
state = result.fold(
(failure) => AsyncError(failure, StackTrace.current),
(user) => AsyncData(user),
);
}
}
// Screen
class UserScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final userState = ref.watch(userNotifierProvider);
return Scaffold(
body: userState.when(
data: (user) => Text('Hello ${user?.name}'),
loading: () => const CircularProgressIndicator(),
error: (e, _) => Text('Error: $e'),
),
);
}
}
Code Generation
# Generate all files
dart run build_runner build --delete-conflicting-outputs
# Watch mode
dart run build_runner watch --delete-conflicting-outputs
Best Practices
DO:
- Keep domain entities pure (no external dependencies)
- Use freezed with
sealedkeyword for immutable data classes - Handle all error cases with Either<Failure, T>
- Use riverpod_generator with unified
Reftype - Separate models (data) from entities (domain)
- Place business logic in use cases, not in widgets
- Use Retrofit for type-safe API calls
- Handle DioException in repositories with NetworkExceptions
- Use interceptors for cross-cutting concerns (auth, logging)
DON’T:
- Import Flutter/HTTP libraries in domain layer
- Mix presentation logic with business logic
- Use try-catch directly in widgets when using Either
- Create god objects or god providers
- Skip the repository pattern
- Use legacy
XxxReftypes in new code
Common Issues
| Issue | Solution |
|---|---|
| Build runner conflicts | dart run build_runner clean && dart run build_runner build --delete-conflicting-outputs |
| Provider not found | Ensure generated files are imported and run build_runner |
| Either not unwrapping | Use fold(), match(), or getOrElse() to extract values |
XxxRef not found |
Use unified Ref type instead (Riverpod 3.x+) |
sealed keyword error |
Upgrade to Dart 3.3+ and Freezed 3.0+ |
Knowledge References
Primary Libraries (used in this skill):
- Flutter 3.19+: Latest framework features
- Dart 3.3+: Language features (patterns, records,
sealedmodifier) - Riverpod 3.0+: State management with unified
Reftype - Dio 5.9+: HTTP client with interceptors
- Retrofit 4.9+: Type-safe REST API code generation
- freezed 3.0+: Immutable data classes with code generation
- json_serializable 6.x: JSON serialization
- go_router 14.x+: Declarative routing
- fpdart: Functional error handling with Either type
References
- quick_start.md – Step-by-step feature creation workflow
- data_layer.md – Models, Retrofit API services, Repositories
- presentation_layer.md – Providers, Screens, Widgets patterns
- network_setup.md – Dio provider, Interceptors, Network exceptions
- error_handling.md – Either patterns, Failure types, Error strategies
- retrofit_patterns.md – Complete Retrofit API request patterns