AgroSentry AI is a high-performance, offline-capable mobile application developed in Flutter. The platform enables farmers, agronomists, and agricultural researchers to perform instantaneous, on-device diagnostic analysis of crop leaf diseases. By leveraging localized TensorFlow Lite deep learning inference, the application operates entirely without internet access or cloud dependency, ensuring utility in remote fields and low-connectivity environments.
AgroSentry AI employs a decoupled architectural pattern, segregating stateful UI components, data persistency layers, and deep learning execution services.
graph TD
UI[Flutter UI Layer] --> Provider[Scan Provider State Management]
Provider --> ModelSvc[Model Service - TFLite Inference]
Provider --> DBSvc[Database Service - SQLite]
ModelSvc --> ImageProc[Image Processing Pipeline]
ModelSvc --> DBSvc
DBSvc --> SQLite[(Local SQLite Database)]
ImageProc --> FileSys[(On-Device File System)]
- Inference Engine (TensorFlow Lite): Encapsulated in the
ModelServicesingleton, this module interfaces with the MobileNet-based convolutional neural network (plant_disease.tflite). It executes on-device inference utilizing two execution threads to balance classification latency with system thermals. - Local Data Persistence (SQLite): Controlled by
DatabaseService, this module maintains a dual-table database schema (scansandremedies) using thesqfliteengine. It facilitates automatic schema migrations across app updates and manages historical scan tracking. - State Management (ChangeNotifier): The
ScanProviderstate manager acts as the controller, coordinating async camera inputs, image pre-processing, classification execution, and storage of output payloads into the relational database.
The sequence diagram below displays the end-to-end communication lifecycle from physical input capture to diagnostic logging and dashboard render.
sequenceDiagram
autonumber
actor User as Field Agronomist
participant UI as Flutter UI Screen
participant Provider as ScanProvider (State)
participant ModelSvc as ModelService (Interpreter)
participant DBSvc as DatabaseService (SQLite)
participant Model as TFLite Binary
User->>UI: Capture leaf photo in Camera Viewfinder
UI->>Provider: Invoke analyzeImage(imagePath)
activate Provider
Provider->>Provider: Set isAnalyzing = true & notify UI
UI->>UI: Display analytical loading progress bar
Provider->>ModelSvc: Invoke predictImage(imageFile)
activate ModelSvc
ModelSvc->>ModelSvc: Decode image bytes & center-crop to square
ModelSvc->>ModelSvc: Resize image to 224x224 & extract normalized Float32 values
ModelSvc->>Model: Run model.run(input, output)
activate Model
Model-->>ModelSvc: Return 38 classification logits
deactivate Model
ModelSvc->>ModelSvc: Sort logits & extract Top 5 predictions
ModelSvc->>DBSvc: Query remedies table for top class label
activate DBSvc
DBSvc-->>ModelSvc: Return symptoms & remedy JSON structures
deactivate DBSvc
ModelSvc-->>Provider: Return hydrated CropDisease payload
deactivate ModelSvc
Provider->>DBSvc: Save scan metadata to scans table (insertScan)
activate DBSvc
DBSvc-->>Provider: Return autogenerated database ID
deactivate DBSvc
Provider->>Provider: Update currentScan, set isAnalyzing = false & notify UI
deactivate Provider
UI->>UI: Render DetailScreen with diagnostic data
UI-->>User: Present diagnostic details, confidence, and treatment plans
The deep learning classifier is optimized for mobile deployment. The application uses a custom MobileNet model trained on crop leaf datasets, covering 38 distinct classes (comprising healthy crops and diverse bacterial, fungal, and viral pathological states).
- Input Tensor:
- Identifier:
input_2 - Data Type:
Float32 - Shape:
[1, 224, 224, 3](representing batch size, width, height, and RGB channels) - Normalization: Raw pixel values (0-255) are normalized to
[0.0, 1.0]prior to input injection.
- Identifier:
- Output Tensor:
- Identifier:
Identity - Data Type:
Float32 - Shape:
[1, 38] - Activation Function: Softmax (representing probability distribution across the 38 classes)
- Identifier:
The snippet below from ModelService demonstrates the exact normalization and center-square cropping applied to images before executing the TensorFlow Lite interpreter:
// Extracting, cropping, and normalizing pixel channels in ModelService.dart
final int minDimension = decoded.width < decoded.height ? decoded.width : decoded.height;
final int startX = (decoded.width - minDimension) ~/ 2;
final int startY = (decoded.height - minDimension) ~/ 2;
final cropped = img.copyCrop(
decoded,
x: startX,
y: startY,
width: minDimension,
height: minDimension,
);
final resized = img.copyResize(cropped, width: 224, height: 224);
final inputBuffer = Float32List(224 * 224 * 3);
int index = 0;
for (int y = 0; y < 224; y++) {
for (int x = 0; x < 224; x++) {
final pixel = resized.getPixel(x, y);
// Normalize color channels to [0.0, 1.0] range
inputBuffer[index++] = pixel.r / 255.0;
inputBuffer[index++] = pixel.g / 255.0;
inputBuffer[index++] = pixel.b / 255.0;
}
}The local SQLite relational engine is managed by DatabaseService. It maintains structural database tables and seeds remedy lookups automatically on the first initialization.
CREATE TABLE scans (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
scientificName TEXT NOT NULL,
description TEXT NOT NULL,
symptoms TEXT NOT NULL,
treatment TEXT NOT NULL,
confidence REAL NOT NULL,
imagePath TEXT NOT NULL,
scanDate TEXT NOT NULL,
top5 TEXT,
isConfident INTEGER,
cropName TEXT,
diseaseName TEXT
);CREATE TABLE remedies (
className TEXT PRIMARY KEY,
description TEXT NOT NULL,
symptoms TEXT NOT NULL,
organicRemedy TEXT NOT NULL,
chemicalRemedy TEXT NOT NULL,
prevention TEXT NOT NULL
);The service implements a structured, incremental migration mechanism utilizing database versioning:
- Version 1: Initial release, creates
scansschema. - Version 2: Adds
top5JSON string metadata toscansfor model analytical telemetry. - Version 3: Adds confidence status (
isConfident),cropName, anddiseaseNameclassifications to support advanced filtering. - Version 4: Creates and populates the
remedieslookup index to store agricultural solution guidelines offline.
Future _upgradeDB(Database db, int oldVersion, int newVersion) async {
if (oldVersion < 2) {
await db.execute('ALTER TABLE scans ADD COLUMN top5 TEXT');
}
if (oldVersion < 3) {
await db.execute('ALTER TABLE scans ADD COLUMN isConfident INTEGER');
await db.execute('ALTER TABLE scans ADD COLUMN cropName TEXT');
await db.execute('ALTER TABLE scans ADD COLUMN diseaseName TEXT');
}
if (oldVersion < 4) {
await db.execute('''
CREATE TABLE IF NOT EXISTS remedies (
className TEXT PRIMARY KEY,
description TEXT NOT NULL,
symptoms TEXT NOT NULL,
organicRemedy TEXT NOT NULL,
chemicalRemedy TEXT NOT NULL,
prevention TEXT NOT NULL
)
''');
}
}State mutations are coordinated by the ScanProvider class, implementing the ChangeNotifier pattern. The state updates are structured around an internal state model:
stateDiagram-v2
[*] --> Idle
Idle --> Analyzing : User triggers analyzeImage()
Analyzing --> Success : Inference complete, saved to DB
Analyzing --> Failure : Inference failed or out-of-memory
Success --> Idle : clear() called
Failure --> Idle : clear() called
_currentScan: Stores the active hydratedCropDiseaseinstance._isAnalyzing: Boolean flag indicating if image preprocessing or TFLite inference is running._error: Captures error states for UI rendering (e.g. file access errors, model issues).
// Core state transition logic in scan_provider.dart
Future<CropDisease> analyzeImage(String imagePath) async {
_isAnalyzing = true;
_error = '';
notifyListeners();
try {
final imageFile = File(imagePath);
final result = await ModelService.instance.predictImage(imageFile);
await DatabaseService.instance.insertScan(result);
_currentScan = result;
_isAnalyzing = false;
notifyListeners();
return result;
} catch (e) {
_isAnalyzing = false;
_error = e.toString();
notifyListeners();
rethrow;
}
}agro_sentry_ai/
├── assets/
│ ├── data/
│ │ ├── labels.txt # Mapping of index to the 38 classification classes
│ │ └── remedies.json # Seed data containing detailed disease descriptions and remedies
│ ├── images/
│ │ ├── app_icon.png # Launcher icon assets
│ │ └── splash_brand.png # Branding asset for native splash screens
│ ├── models/
│ │ └── plant_disease.tflite # Compact compiled deep learning model binary
│ └── test_images/
│ └── leaf_test.png # Test image for validation
├── lib/
│ ├── main.dart # Bootstrapper initializing SQLite and TFLite
│ ├── models/
│ │ └── crop_disease.dart # Data modeling schemas for CropDisease and Prediction objects
│ ├── screens/
│ │ ├── camera_screen.dart # Native camera controller with viewfinder guide overlay
│ │ ├── detail_screen.dart # Detailed display for treatments, symptoms, and analytics
│ │ ├── history_screen.dart # Paginated list of historical crop diagnoses
│ │ ├── home_screen.dart # Dashboard displaying recent scans and quick-action triggers
│ │ ├── image_preview_screen.dart # Image verification step before processing
│ │ └── scan_screen.dart # Entry screen coordinating photo upload and scan execution
│ ├── services/
│ │ ├── database_service.dart # SQLite management, tables creation, upgrade and seed handlers
│ │ ├── model_service.dart # TFLite loading, pre-processing, and inference execution
│ │ └── scan_provider.dart # Global state manager for active scan sessions
│ └── widgets/
│ └── disease_card.dart # Reusable UI card for history and dashboard listings
└── pubspec.yaml # Flutter project dependency configuration
- Renders current diagnostics metrics (total plants scanned, healthy rates, and pathogen detection logs).
- Displays a horizontal sliding carousel showing recent scans.
- Offers floating shortcut triggers to start a new leaf diagnostic session immediately.
- Integrates a live camera feed.
- Displays a physical guide box overlay on the camera preview, directing the user to place the leaf in the center to optimize cropping precision.
- Provides real-time controls for camera flash settings and camera switching (rear/front).
- Restricts access to standard system photo galleries if the user prefers to select an existing image.
- Freezes the captured frame before model analysis.
- Allows users to review photo quality and alignment.
- Provides quick controls to discard and re-capture the image.
- Renders diagnosis results alongside a visual confidence bar.
- Displays botanical classifications and Latin scientific names.
- Displays expandable tabs containing specific symptoms, organic solutions, chemical controls, and preventive steps.
- Features a dropdown section showing alternative classifications (top 5 predictions) to assist agronomists with complex diagnoses.
- Displays a scrollable timeline of all historical scans.
- Features a local search bar to filter records by crop or disease name.
- Includes swipe-to-delete options to remove old scan files and database records.
The TFLite model classifier supports 38 classes, mapping crop types to specific health states:
- Apple: Scab, Black Rot, Cedar Rust, Healthy
- Blueberry: Healthy
- Cherry: Powdery Mildew, Healthy
- Corn (Maize): Gray Leaf Spot, Common Rust, Northern Leaf Blight, Healthy
- Grape: Black Rot, Esca (Black Measles), Leaf Blight, Healthy
- Orange: Citrus Greening (Huanglongbing)
- Peach: Bacterial Spot, Healthy
- Pepper (Bell): Bacterial Spot, Healthy
- Potato: Early Blight, Late Blight, Healthy
- Raspberry: Healthy
- Soybean: Healthy
- Squash: Powdery Mildew
- Strawberry: Leaf Scorch, Healthy
- Tomato: Bacterial Spot, Early Blight, Late Blight, Leaf Mold, Septoria Leaf Spot, Spider Mites (Two-spotted spider mite), Target Spot, Yellow Leaf Curl Virus, Mosaic Virus, Healthy
- Flutter SDK (version 3.0.0 or higher) installed and configured on the path.
- Target SDK version: Android API level 21 minimum, iOS 12.0 minimum.
- Physical device or emulator with camera permissions enabled.
Generate platform-specific wrappers for Android and iOS in the current directory:
flutter create --org com.agrosentry --project-name agro_sentry .Install package requirements:
flutter pub getVerify that your assets are properly located and mapped:
- TFLite Model File:
assets/models/plant_disease.tflite - Labels Text File:
assets/data/labels.txt - Remedies JSON File:
assets/data/remedies.json
Verify the asset configuration in pubspec.yaml:
flutter:
assets:
- assets/models/plant_disease.tflite
- assets/data/labels.txt
- assets/data/remedies.jsonLaunch the application on your target emulator or physical device:
flutter runIf the application crashes on launch when built in --release mode, the issue is typically due to Proguard shrinking the native C++ library binaries utilized by TensorFlow Lite.
To resolve this, add the following lines to your android/app/proguard-rules.pro file:
# Prevent ProGuard from renaming or stripping TFLite C++ symbols
-keep class org.tensorflow.lite.** { *; }
-dontwarn org.tensorflow.lite.**
Processing high-resolution images can exhaust heap memory on older mobile devices. To prevent memory issues:
- Garbage Collection: Reassign local image bytes variables to
nullimmediately after extracting the Float32 flat buffer. - TFLite Disposal: When navigating away from scanning workflows, call
dispose()on the model service to release GPU and CPU delegate instances.
// Properly disposing of model interpreter instances
void dispose() {
_interpreter?.close();
_interpreter = null;
_isLoaded = false;
}To prevent camera controller lag or hardware access crashes:
- Always initialize the camera controller using a
FutureBuilderor in an asyncinitStateblock. - Call
dispose()on the controller inside the screen'sdispose()lifecycle method to free camera hardware locks.
@override
void dispose() {
_cameraController?.dispose();
super.dispose();
}AgroSentry AI includes tests to verify preprocessing, model loading, and database services.
To run all local tests, execute:
flutter testThe test file test/model_service_test.dart checks the fallback detection flows when TFLite interpreter hardware features are unavailable during unit testing.
void main() {
test('Verification of offline fallback values on low confidence detection', () {
// Verifies that predictions below the 60% confidence threshold
// return an uncertain diagnostic classification.
final confidence = 0.45;
final isConfident = confidence >= 0.60;
expect(isConfident, isFalse);
});
}- Developer: Daksh Srivastava
- LinkedIn: https://www.linkedin.com/in/daksh-srivastava-2ba851344/