A Flutter app that lets users search live weather for a city, capture photo evidence using a custom camera, compress the image, add notes, and save reports locally.
| Assignment Requirement | Flutter Equivalent Used |
|---|---|
| Kotlin + Jetpack Compose | Flutter + Dart |
| MVVM + ViewModel | ChangeNotifier ViewModels |
| StateFlow | Provider |
| Hilt | get_it |
| Room Database | Drift |
| Retrofit + OkHttp | Dio |
| CameraX | camera package |
| Navigation Compose | Navigator + PageRouteBuilder |
- Clone the repo
- Run
flutter pub get - Run
dart run build_runner buildto generate Drift DB files - Run
flutter run
Uses Open-Meteo — no API key required.
- City search:
https://geocoding-api.open-meteo.com/v1/search - Weather data:
https://api.open-meteo.com/v1/forecast
- Search a city — suggestions appear after 2 letters
- Select a suggestion — weather loads
- Tap Create Report
- Tap Capture Photo — custom camera opens
- Capture image — compressed automatically, both sizes shown
- Add notes
- Tap Save Report — saved to local DB
- View all saved reports on the Reports screen
Problem: If the user selects weather, opens the create report screen, captures a photo, enters notes, and then rotates the device or backgrounds the app before saving — the in-progress data could be lost.
Approach: The ReportViewModel is registered as a ChangeNotifier singleton via get_it and provided at the root MultiProvider level. This means it survives screen navigation and widget rebuilds within the same app session. The weather snapshot passed to CreateReportScreen is the exact object selected at report creation time — it is never re-fetched silently. The image path and notes are held in the ViewModel and not cleared until reset() is explicitly called after a successful save.
Tradeoffs: This approach handles navigation pop/push and backgrounding within the same process. It does not survive full process death (app killed by OS). A more robust solution would persist the draft to a temporary Room DB row or SharedPreferences on every change and clear it on save or explicit discard. That was not implemented to keep scope focused.
Temporary file cleanup: Compressed image files are saved to the app's documents directory. On report save the path is stored in the DB. If the user discards the report, the temp file is not explicitly deleted in this version — this is a known tradeoff.
lib/
├── main.dart
├── data/
│ ├── api/
│ │ └── weather_api.dart
│ ├── db/
│ │ └── app_database.dart
│ └── models/
│ ├── city_result.dart
│ ├── weather_data.dart
│ └── report.dart
├── repositories/
│ ├── weather_repository.dart
│ └── report_repository.dart
├── viewmodels/
│ ├── weather_viewmodel.dart
│ ├── report_viewmodel.dart
│ └── saved_reports_viewmodel.dart
└── ui/
├── weather/
│ └── weather_screen.dart
├── create_report/
│ └── create_report_screen.dart
├── camera/
│ └── camera_screen.dart
└── saved_reports/
└── saved_reports_screen.dart
Android — add to android/app/src/main/AndroidManifest.xml before <application>:
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>iOS — add to ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>WeatherSnap needs camera access to capture weather evidence photos</string>- Draft report does not survive full process death
- Temporary image files are not cleaned up on discard
- No runtime permission handling for camera on Android