A Flutter app that uses on-device machine learning to predict cat breeds from images and cat gender from audio recordings.
- Cat Breed Prediction: Upload or capture a photo of a cat to identify its breed using TensorFlow Lite
- Gender Prediction: Record cat audio to predict whether the cat is male or female
- Prediction History: View past predictions stored locally
- On-Device ML: All predictions run locally on the device - no internet required
- Flutter SDK 3.11+
- Dart SDK 3.11+
- Android SDK (minSdk 21+) / iOS 13.0+
git clone <repository-url>
cd fursureflutter pub getThe app downloads remote TFLite models on first launch. Model URLs are provided at build time via --dart-define-from-file. Config files live in config/:
| File | Purpose |
|---|---|
config/dev.json |
Placeholder mode — no model downloads, instant responses |
config/prod.json |
Real models — fill in your actual model URLs before building |
Build variables:
| Variable | Required | Description |
|---|---|---|
USE_PLACEHOLDERS |
No | Set "true" to force placeholder mode for both models |
BREED_MODEL_URL |
No | Download URL for the breed TFLite model |
BREED_MODEL_SHA256 |
No | Expected SHA-256 hex digest of the breed model file |
GENDER_MODEL_URL |
No | Download URL for the gender TFLite model |
GENDER_MODEL_SHA256 |
No | Expected SHA-256 hex digest of the gender model file |
When a URL is omitted, that model falls back to placeholder mode automatically — no flag required.
When a SHA-256 hash is provided, the app verifies the downloaded file (and any previously cached copy) against it. A mismatch throws an error — the model will not load. Leave the hash empty to skip verification.
Run in dev (placeholder mode):
flutter run --dart-define-from-file=config/dev.jsonRun with real models:
Edit config/prod.json with your actual model URLs and (optionally) SHA-256 hashes, then:
flutter run --dart-define-from-file=config/prod.jsonExample config/prod.json:
{
"USE_PLACEHOLDERS": "false",
"BREED_MODEL_URL": "https://your-host.example/breed.tflite",
"BREED_MODEL_SHA256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"GENDER_MODEL_URL": "https://your-host.example/gender.tflite",
"GENDER_MODEL_SHA256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}Omit the *_SHA256 keys (or leave them as "") to skip integrity verification for that model.
Ad-hoc overrides still work if needed:
flutter run \
--dart-define=BREED_MODEL_URL=https://your-host.example/breed.tflite \
--dart-define=BREED_MODEL_SHA256=<hex-digest>flutter run --dart-define-from-file=config/dev.jsonOn first launch, the app opens a startup screen and downloads any required real models before routing to onboarding or home. Downloaded models are cached in the app documents directory.
flutter build apk --debug --dart-define-from-file=config/dev.jsonflutter build apk --release --dart-define-from-file=config/prod.jsonOutput: build/app/outputs/flutter-apk/app-release.apk
flutter build apk --release --split-per-abi --dart-define-from-file=config/prod.jsonProduces separate APKs for arm64-v8a, armeabi-v7a, and x86_64.
flutter build appbundle --release --dart-define-from-file=config/prod.jsonOutput: build/app/outputs/bundle/release/app-release.aab
flutter build ios --simulator --dart-define-from-file=config/dev.jsonflutter build ios --release --dart-define-from-file=config/prod.jsonRequires a valid signing certificate and provisioning profile configured in Xcode.
Version management is handled by a single script:
# Bump version and commit
dart scripts/bump_version.dart {major|minor|patch|build}
# Bump version only (no git commit)
dart scripts/bump_version.dart patch --no-commit
# Bump with a custom commit message
dart scripts/bump_version.dart minor "chore: release v1.1.0 for QA"Other scripts:
# Run the app (wraps flutter run with model flag helpers)
dart scripts/run.dart --use-placeholders
dart scripts/run.dart --breed-model-url https://example.com/breed.tflite
# Clean
dart scripts/clean.dartRun all tests:
flutter testRun tests with coverage:
flutter test --coveragelib/
├── core/ # App-wide utilities and config
│ ├── config/ # Runtime build-variable configuration
│ ├── constants/ # Shared layout constants
│ ├── error/ # Typed app exceptions
│ ├── router/ # GoRouter setup
│ ├── services/ # Core onboarding service
│ ├── theme/ # App theming
│ └── widgets/ # Shared widgets
├── features/ # Feature modules
│ ├── home/ # Home screen
│ ├── onboarding/ # Onboarding flow
│ ├── prediction/ # Breed and gender prediction
│ ├── results/ # Prediction history
│ ├── settings/ # Settings
│ └── startup/ # First-launch bootstrap and model preload
├── providers/ # Riverpod providers
├── services/ # Model download, TFLite, audio, camera, database
└── main.dart # App entry point
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License