A Flutter mobile application built using a Feature-First Clean Architecture structure. This approach organizes code by feature rather than by layer, keeping related logic (UI, business logic, and data) close together while still respecting clean architecture boundaries (presentation, domain, data).
- Platform: Android (Flutter)
- Architecture: Feature-First Clean Architecture
- Login — authentication against the SAPOT admin API
- Dashboard — live monitoring data view
- Settings — profile view and logout
- Alert Push Notifications — FCM push alerts when a monitored parameter needs attention, tapping opens the SAPOT admin website in the device's external browser
flutter_riverpod— state managementdio— HTTP client (with certificate pinning against the API's self-signed cert)go_router— routing/navigationflutter_secure_storage— secure token storagefirebase_messaging— FCM push notificationsurl_launcher— opening the SAPOT admin website from a notification tap
lib/
├── core/ # Shared code used across features
│ ├── constants/ # App-wide constants (colors, strings, sizes)
│ ├── errors/ # Failure & exception classes
│ ├── network/ # Network client, interceptors, connectivity checks
│ ├── theme/ # App theming, text styles
│ ├── usecases/ # Base UseCase abstractions
│ ├── utils/ # Helpers, extensions, formatters
│ └── widgets/ # Shared/reusable widgets
│
├── features/ # Each feature is self-contained
│ └── feature_name/
│ ├── data/
│ │ ├── datasources/ # Remote & local data sources
│ │ ├── models/ # DTOs / JSON-serializable models
│ │ └── repositories/ # Repository implementations
│ ├── domain/
│ │ ├── entities/ # Core business objects
│ │ ├── repositories/ # Abstract repository contracts
│ │ └── usecases/ # Business logic / use cases
│ └── presentation/
│ ├── providers/ # State management
│ ├── pages/ # Screens
│ └── widgets/ # Feature-specific widgets
│
└── main.dart # App entry point
| Layer | Responsibility |
|---|---|
| Domain | Pure Dart business logic. Contains entities, repository interfaces, and use cases. No Flutter or external package dependencies. |
| Data | Implements domain repository interfaces. Handles API calls, local storage, and mapping models to/from entities. |
| Presentation | UI layer. Contains widgets, pages, and state management (Bloc/Cubit/Provider/Riverpod) that consume use cases. |
Dependency rule: Presentation → Domain ← Data. The domain layer never depends on data or presentation.
- Flutter SDK (>= 3.x)
- Dart SDK (>= 3.x)
- Android Studio / Xcode (for emulators/simulators)
# Clone the repository
git clone https://github.com/sapot-admin-mobile/sapot_admin_mobile.git
cd sapot_admin_mobile
# Install dependencies
flutter pub get
# Run code generation (if using freezed/json_serializable/build_runner)
flutter pub run build_runner build --delete-conflicting-outputs
# Run the app
flutter run- Create a new folder under
lib/features/<feature_name>/. - Add the three layers:
data/,domain/,presentation/. - Define entities and repository contracts in
domain/. - Implement the repository and data sources in
data/. - Build the UI and state management in
presentation/. - Add routes/navigation entries as needed.
# Run all tests
flutter test
# Run tests with coverage
flutter test --coverageTesting structure mirrors the lib/ structure:
test/
└── features/
└── feature_name/
├── data/
├── domain/
└── presentation/
- Domain layer: Unit tests for use cases and entities.
- Data layer: Unit tests for repositories and data sources (mocking APIs/local storage).
- Presentation layer: Widget tests and Bloc/Cubit/state tests.
- Follow Effective Dart guidelines.
- Use
flutter analyzeanddart format .before committing. - File naming:
snake_case.dart - Class naming:
PascalCase - One class/widget per file where practical.
See CONTRIBUTING.md for guidelines on branching, commit conventions, and the pull request process.