flutter-app-architecture
Skill669 starsChanged 3 months agoMITView on GitHub
Use when scaffolding a project, refactoring into layers, creating view models/repositories, configuring dependency injection, or implementing unidirectional data flow (MVVM).
1 other project uses a version of this, 1 of them edited.
Adopted by
Most recently taken up by
- kitloomlk-stack/mobile-apps-kitedited, first seen Oct 15, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 1
- kitloomlk-stack/mobile-apps-kit★ 0100% same
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository36 other files it gives its agents.
- AGENTS.mdAGENTS.md
- accessibilitySkill
- architecture-feature-firstSkill
- blocSkill
- code-reviewSkill
- dart-3-updatesSkill
- developing-genkit-dartSkill
- effective-dartSkill
- firebase-aiSkill
- firebase-analyticsSkill
- firebase-app-checkSkill
- firebase-authSkill
- firebase-cloud-firestoreSkill
- firebase-cloud-functionsSkill
- firebase-crashlyticsSkill
- firebase-databaseSkill
- firebase-data-connectSkill
- firebase-in-app-messagingSkill
- firebase-messagingSkill
- firebase-remote-configSkill
- firebase-storageSkill
- flutter-best-practicesSkill
- flutter-change-notifierSkill
- flutter-errorsSkill
- flutterfire-configureSkill
- flutter-pre-cachingSkill
- flutter-use-column-row-firstSkill
- generate-images-with-firebase-aiSkill
- inclusive-designSkill
- mockitoSkill
- mocktailSkill
- patrol-e2e-testingSkill
- providerSkill
- revenuecat-testingSkill
- riverpodSkill
- testingSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 4 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 13
Use this skill when:
- Distinctive: line 38
Only adjacent layers may communicate.
- Distinctive: line 40
Follow unidirectional data flow: state flows down (Data → UI), events flow up (UI → Data).
- Very specific: line 198
Use key-value storage (e.g., shared_preferences) for configuration and preferences.
- Very specific: line 199
Use SQL storage (e.g., drift, sqflite) for complex relational data.
- Very specific: line 207
Use StatelessWidget when possible.
- Distinctive: line 208
Keep build methods simple and focused on rendering.
- Very specific: line 209
Prefer final for fields and top-level variables.
- Very specific: line 210
Prefer explicit typing on public APIs (e.g., Command0<void> over dynamic signatures).
- Very specific: line 211
Use descriptive constant names (e.g., _todoTableName over _kTableTodo).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Flutter App Architecture Skill
This skill defines how to structure Flutter applications using layered architecture, proper data flow, and MVVM patterns for maintainability and testability.
When to Use
Use this skill when:
- Scaffolding a new Flutter project with layered architecture.
- Creating or refactoring View Models, Repositories, or Services.
- Wiring dependency injection between architectural components.
- Implementing unidirectional data flow across layers.
- Adding a Domain (Logic) Layer for complex business logic or shared use cases.
1. Layer Structure
Separate every app into a UI Layer and a Data Layer. Add a Logic (Domain) Layer only for complex apps.
┌──────────────────────────────────────────────────────────────┐
│ UI Layer │ Views + ViewModels │
├──────────────────────────────────────────────────────────────┤
│ Logic Layer │ Use Cases / Interactors (optional) │
├──────────────────────────────────────────────────────────────┤
│ Data Layer │ Repositories + Services │
└──────────────────────────────────────────────────────────────┘Rules:
- Only adjacent layers may communicate. The UI layer must never access a Service directly.
- Data changes always happen in the Data layer (SSOT = Repository). No mutation in UI or Logic layers.
- Follow unidirectional data flow: state flows down (Data → UI), events flow up (UI → Data).
2. Component Responsibilities
View
- Describes how to present data; keep logic minimal and UI-related only.
- Passes events to the ViewModel in response to user interactions.
ViewModel
- Converts app data into UI state and maintains the current state needed by the View.
- Exposes callbacks (commands) to the View and retrieves/transforms data from Repositories.
class BookingViewModel extends ChangeNotifier {
final BookingRepository _repo;
BookingViewModel(this._repo);
List<Booking> _bookings = [];
List<Booking> get bookings => List.unmodifiable(_bookings);
bool _isLoading = false;
bool get isLoading => _isLoading;
Future<void> loadBookings() async {
_isLoading = true;
notifyListeners();
_bookings = await _repo.getBookings();
_isLoading = false;
notifyListeners();
}
Future<void> cancelBooking(String id) async {
await _repo.cancelBooking(id);
_bookings = await _repo.getBookings();
notifyListeners();
}
}Repository (Single Source of Truth)
- The only class that may mutate its data; all other classes read from it.
- Handles caching, error handling, and data refresh logic.
- Transforms raw data from Services into domain models.
class BookingRepository {
final BookingApiService _apiService;
final BookingLocalService _localService;
BookingRepository(this._apiService, this._localService);
Future<List<Booking>> getBookings() async {
try {
final remote = await _apiService.fetchBookings();
await _localService.cacheBookings(remote);
return remote;
} catch (_) {
return _localService.getCachedBookings();
}
}
Future<void> cancelBooking(String id) async {
await _apiService.cancelBooking(id);
await _localService.removeCachedBooking(id);
}
}Service
- Wraps API endpoints and exposes asynchronous response objects.
- Isolates data-loading and holds no state.
class BookingApiService {
final http.Client _client;
BookingApiService(this._client);
Future<List<Booking>> fetchBookings() async {
final response = await _client.get(Uri.parse('/api/bookings'));
if (response.statusCode != 200) {
throw HttpException('Failed to load bookings');
}
final data = jsonDecode(response.body) as List;
return data.map((json) => Booking.fromJson(json)).toList();
}
}3. Dependency Injection
Supply dependencies via constructors. Define abstract interfaces so implementations can be swapped for testing.
// Abstract interface for the repository
abstract class BookingRepository {
Future<List<Booking>> getBookings();
Future<void> cancelBooking(String id);
}
// Concrete implementation
class BookingRepositoryImpl implements BookingRepository {
final BookingApiService _api;
BookingRepositoryImpl(this._api);
@override
Future<List<Booking>> getBookings() => _api.fetchBookings();
@override
Future<void> cancelBooking(String id) => _api.cancelBooking(id);
}4. Use Cases (Domain Layer)
Introduce use cases only when:
- Logic is complex or does not fit cleanly in the UI or Data layers.
- Logic is reused across multiple ViewModels or merges data from multiple Repositories.
class GetUpcomingBookingsUseCase {
final BookingRepository _bookingRepo;
final UserRepository _userRepo;
GetUpcomingBookingsUseCase(this._bookingRepo, this._userRepo);
Future<List<Booking>> call() async {
final user = await _userRepo.getCurrentUser();
final bookings = await _bookingRepo.getBookings();
return bookings
.where((b) => b.userId == user.id && b.date.isAfter(DateTime.now()))
.toList();
}
}5. Workflow: Scaffold a New Feature
- Create the Service — implement the API wrapper with typed response parsing.
- Create the Repository — inject the Service, implement caching and error-handling logic.
- Create the ViewModel — inject the Repository, expose UI state and commands.
- Create the View — bind to the ViewModel, render state, dispatch events.
- Wire DI — register all components in the dependency injection container.
- Verify — confirm the View never accesses the Service directly and data flows unidirectionally.
6. Data Storage
- Use key-value storage (e.g.,
shared_preferences) for configuration and preferences. - Use SQL storage (e.g.,
drift,sqflite) for complex relational data. - Implement optimistic updates to improve perceived responsiveness by updating UI before server confirms.
- Support offline-first by combining local and remote data sources in Repositories.
7. Coding Conventions
- Use
StatelessWidgetwhen possible; avoid unnecessaryStatefulWidgets. - Keep build methods simple and focused on rendering.
- Prefer
finalfor fields and top-level variables. Preferconstconstructors when the class supports it. - Prefer explicit typing on public APIs (e.g.,
Command0<void>over dynamic signatures). - Use descriptive constant names (e.g.,
_todoTableNameover_kTableTodo).
References
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

