Loading...
Loading...
Generate Flutter applications using Clean Architecture with feature-first structure, Riverpod state management, Dio + Retrofit for networking, and fpdart error handling. Use this skill when creating Flutter apps, implementing features with clean architecture patterns, setting up Riverpod providers, handling data with Either type for functional error handling, making HTTP requests with type-safe API clients, or structuring projects with domain/data/presentation layers. Triggers include "Flutter app", "clean architecture", "Riverpod", "feature-first", "state management", "API client", "Retrofit", "Dio", "REST API", or requests to build Flutter features with separation of concerns.
npx skill4agent add duckyman-ai/agent-skills flutter-clean-archNote: Riverpod 3.0+ & Freezed 3.0+ RequiredRiverpod 3.0+: Thetypes (likeXxxRef,DioRef, etc.) have been removed in favor of a unifiedUserRepositoryReftype.RefRiverpod 2.x (Legacy):dartSomeType someType(SomeTypeRef ref) { ... }Riverpod 3.x+ (Current):dartSomeType someType(Ref ref) { ... }Freezed 3.0+: Requireskeyword for union types with Dart 3.3.sealedFreezed 2.x (Legacy):dartclass Failure with _$Failure { ... }Freezed 3.x+ (Current):dartsealed 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
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// 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);
}// 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));
}
}
}// 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'),
),
);
}
}# Generate all files
dart run build_runner build --delete-conflicting-outputs
# Watch mode
dart run build_runner watch --delete-conflicting-outputssealedRefXxxRef| Issue | Solution |
|---|---|
| Build runner conflicts | |
| Provider not found | Ensure generated files are imported and run build_runner |
| Either not unwrapping | Use |
| Use unified |
| Upgrade to Dart 3.3+ and Freezed 3.0+ |
sealedRef