From flutter-skills-33
Architects Flutter apps with layered architecture (UI, Logic, Data) using MVVM and Repository patterns. Use for new projects or refactoring for scalability.
How this skill is triggered — by the user, by Claude, or both
Slash command
/flutter-skills-33:flutter-apply-architecture-best-practicesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- [Architectural Layers](#architectural-layers)
Enforce strict Separation of Concerns by dividing the application into distinct layers. Never mix UI rendering with business logic or data fetching.
Implement the MVVM (Model-View-ViewModel) pattern to manage UI state and logic.
ChangeNotifier (or use Listenable) to expose state. Expose immutable state snapshots to the View. Inject Repositories into ViewModels via the constructor.Implement the Repository pattern to isolate data access logic and create a single source of truth.
Result wrappers.Organize the codebase using a hybrid approach: group UI components by feature, and group Data/Domain components by type.
lib/
├── data/
│ ├── models/ # API models
│ ├── repositories/ # Repository implementations
│ └── services/ # API clients, local storage wrappers
├── domain/
│ ├── models/ # Clean domain models
│ └── use_cases/ # Optional business logic classes
└── ui/
├── core/ # Shared widgets, themes, typography
└── features/
└── [feature_name]/
├── view_models/
└── views/
Follow this sequential workflow when adding a new feature to the application. Copy the checklist to track progress.
freezed or built_value.ChangeNotifier. Inject required Repositories/Use Cases. Expose immutable state and command methods.ListenableBuilder or AnimatedBuilder to listen to ViewModel changes.provider or get_it).// 1. Service (Raw API interaction)
class ApiClient {
Future<UserApiModel> fetchUser(String id) async {
// HTTP GET implementation...
}
}
// 2. Repository (Single source of truth, returns Domain Model)
class UserRepository {
UserRepository({required ApiClient apiClient}) : _apiClient = apiClient;
final ApiClient _apiClient;
User? _cachedUser;
Future<User> getUser(String id) async {
if (_cachedUser != null) return _cachedUser!;
final apiModel = await _apiClient.fetchUser(id);
_cachedUser = User(id: apiModel.id, name: apiModel.fullName); // Transform to Domain Model
return _cachedUser!;
}
}
// 3. ViewModel (State management and presentation logic)
class ProfileViewModel extends ChangeNotifier {
ProfileViewModel({required UserRepository userRepository})
: _userRepository = userRepository;
final UserRepository _userRepository;
User? _user;
User? get user => _user;
bool _isLoading = false;
bool get isLoading => _isLoading;
Future<void> loadProfile(String id) async {
_isLoading = true;
notifyListeners();
try {
_user = await _userRepository.getUser(id);
} finally {
_isLoading = false;
notifyListeners();
}
}
}
// 4. View (Dumb UI component)
class ProfileView extends StatelessWidget {
const ProfileView({super.key, required this.viewModel});
final ProfileViewModel viewModel;
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: viewModel,
builder: (context, _) {
if (viewModel.isLoading) {
return const Center(child: CircularProgressIndicator());
}
final user = viewModel.user;
if (user == null) {
return const Center(child: Text('User not found'));
}
return Column(
children: [
Text(user.name),
ElevatedButton(
onPressed: () => viewModel.loadProfile(user.id),
child: const Text('Refresh'),
),
],
);
},
);
}
}
npx claudepluginhub flutter/skillsGuides structuring Flutter apps as four-layer monorepos (Data, Repository, Business Logic, Presentation) with strict unidirectional dependencies and path-based local packages.
Provides expert Flutter/Dart patterns for cross-platform mobile apps including feature-first project structure, const widget best practices, and Riverpod/Bloc state management.
Provides Flutter/Dart guidance on architecture (BLoC, Riverpod), state management, widgets, navigation (GoRouter), data (Dio, Hive), performance, and testing for cross-platform mobile apps.