1. Flutter
  2. Model Management API Reference

Flutter

Model Management API Reference

The Model Management API provides fine-grained control over OCR model lifecycle in the Flutter Vision SDK. This document provides comprehensive API documentation for all available methods, classes, and enums.

Table of Contents

  1. Overview
  2. Getting Started
  3. Core Classes
  4. API Methods
  5. Data Models
  6. Enums
  7. Exceptions
  8. Lifecycle Events
  9. Usage Examples
  10. Platform Differences

Overview

The Model Management API enables:

  • Pre-downloading models during app onboarding for offline-first UX
  • Explicit memory management by loading/unloading models on demand
  • Background updates by checking for model updates before downloading
  • Fine-grained control over model lifecycle with detailed state queries
  • Progress tracking for long-running download operations
  • Lifecycle monitoring via listener callbacks

Key Benefits

  • Reduced app initialization time: Pre-download models during onboarding
  • Memory efficiency: Load only the models you need, when you need them
  • Better UX: Show download progress, cancel operations, handle errors gracefully
  • Offline-first: Download models once, use them indefinitely without network
  • Update management: Check for updates before downloading to avoid unnecessary bandwidth usage

Getting Started

Import the Package

        import 'package:fluttervisionsdkplugin/model_manager.dart';

      

Initialize ModelManager

IMPORTANT: ModelManager must be initialized before any other API calls.

        // Basic initialization
await ModelManager.initialize((builder) {
  builder.enableLogging(true);
});

// Initialization with lifecycle listener
await ModelManager.initialize((builder) {
  builder
    .enableLogging(true)
    .lifecycleListener(MyModelLifecycleListener());
});

// Get singleton instance after initialization
final modelManager = ModelManager.getInstance();

      

Parameters

Parameter Type Required Description
configure Function(ModelManagerBuilder) Yes Builder callback to configure ModelManager

Returns

  • Future<void> - Completes when initialization is successful

Throws

  • ModelSdkNotInitializedException - If VisionSDK is not initialized first
  • ModelException - For other initialization errors

Builder Methods

Method Parameters Description
enableLogging(bool) enabled: Enable debug logging Enable/disable internal logging
lifecycleListener(ModelLifecycleListener) listener: Listener instance Set global lifecycle event listener

Core Classes

ModelManager

Singleton class for managing OCR model lifecycle.

        class ModelManager {
  // Get singleton instance (must call initialize() first)
  static ModelManager getInstance();
  
  // Initialize with configuration
  static Future<void> initialize(Function(ModelManagerBuilder) configure);
  
  // Check if ModelManager is initialized
  static bool isInitialized();
}

      

ModelManagerBuilder

Builder for configuring ModelManager initialization.

        class ModelManagerBuilder {
  ModelManagerBuilder enableLogging(bool enabled);
  ModelManagerBuilder lifecycleListener(ModelLifecycleListener listener);
}

      

OCRModule

Represents a specific model configuration (class + size).

        class OCRModule {
  final ModelClass modelClass;
  final ModelSize modelSize;
  
  const OCRModule({
    required this.modelClass,
    required this.modelSize,
  });
  
  // Helper methods
  Map<String, dynamic> toMap();
  static OCRModule fromMap(Map<String, dynamic> map);
  bool operator ==(Object other);
  int get hashCode;
}

      

API Methods

downloadModel

Download a model to device storage without loading it into memory.

        Future<void> downloadModel({
  required OCRModule module,
  String? apiKey,
  String? token,
  Function(DownloadProgress)? onProgress,
});

      

Parameters

Parameter Type Required Description
module OCRModule Yes The model to download (class + size)
apiKey String? No* API key for authentication
token String? No* Token for authentication
onProgress Function(DownloadProgress)? No Callback for download progress updates

* Either apiKey or token is required

Returns

  • Future<void> - Completes when download finishes

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized
  • ModelNoNetworkException - No network connection
  • ModelNetworkException - Network error during download
  • ModelStorageException - Insufficient storage or write error
  • ModelAlreadyDownloadedException - Model already downloaded
  • ModelException - Authentication or other errors

Example

        final module = OCRModule(
  modelClass: ModelClass.shippingLabel,
  modelSize: ModelSize.large,
);

try {
  await modelManager.downloadModel(
    module: module,
    apiKey: 'YOUR_API_KEY',
    onProgress: (progress) {
      print('${progress.progressPercent}% - ${progress.bytesDownloaded}/${progress.totalBytes}');
    },
  );
  print('Download complete!');
} on ModelNetworkException catch (e) {
  print('Network error: ${e.message}');
} on ModelStorageException catch (e) {
  print('Storage error: ${e.message}');
}

      

cancelDownload

Cancel an ongoing model download.

        Future<void> cancelDownload(OCRModule module);

      

Parameters

Parameter Type Required Description
module OCRModule Yes The model whose download to cancel

Returns

  • Future<void> - Completes when cancellation is processed

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized
  • ModelNotFoundException - No active download for this model
  • ModelException - Other errors

Example

        await modelManager.cancelDownload(module);

      

loadModel

Load a downloaded model into memory for inference.

        Future<void> loadModel({
  required OCRModule module,
  String? apiKey,
  String? token,
  ExecutionProvider? executionProvider,
});

      

Parameters

Parameter Type Required Description
module OCRModule Yes The model to load
apiKey String? No* API key for authentication
token String? No* Token for authentication
executionProvider ExecutionProvider? No Android only: CPU, NNAPI, or XNNPACK

* Either apiKey or token is required

Returns

  • Future<void> - Completes when model is loaded

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized
  • ModelNotFoundException - Model not downloaded
  • ModelLoadException - Failed to load model
  • ModelAlreadyLoadedException - Model already loaded
  • ModelException - Authentication or other errors

Example

        // Basic loading
await modelManager.loadModel(
  module: module,
  apiKey: 'YOUR_API_KEY',
);

// Android: Load with hardware acceleration
await modelManager.loadModel(
  module: module,
  apiKey: 'YOUR_API_KEY',
  executionProvider: ExecutionProvider.nnapi, // Use NNAPI acceleration
);

      

unloadModel

Unload a model from memory to free resources.

        Future<void> unloadModel(OCRModule module);

      

Parameters

Parameter Type Required Description
module OCRModule Yes The model to unload

Returns

  • Future<void> - Completes when model is unloaded

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized
  • ModelNotFoundException - Model not loaded
  • ModelException - Other errors

Example

        await modelManager.unloadModel(module);

      

deleteModel

Delete a downloaded model from device storage.

        Future<void> deleteModel(OCRModule module);

      

Parameters

Parameter Type Required Description
module OCRModule Yes The model to delete

Returns

  • Future<void> - Completes when model is deleted

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized
  • ModelNotFoundException - Model not downloaded
  • ModelException - Other errors

Notes

  • If the model is loaded, it will be unloaded before deletion
  • This operation is irreversible

Example

        await modelManager.deleteModel(module);

      

isModelLoaded

Check if a model is currently loaded in memory.

        Future<bool> isModelLoaded(OCRModule module);

      

Parameters

Parameter Type Required Description
module OCRModule Yes The model to check

Returns

  • Future<bool> - true if loaded, false otherwise

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized

Example

        if (await modelManager.isModelLoaded(module)) {
  print('Model is ready for inference');
} else {
  print('Model needs to be loaded first');
}

      

loadedModelCount

Get the number of models currently loaded in memory.

        Future<int> loadedModelCount();

      

Returns

  • Future<int> - Number of loaded models

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized

Example

        final count = await modelManager.loadedModelCount();
print('$count models loaded');

      

findLoadedModels

Get detailed information about all loaded models.

        Future<List<ModelInfo>> findLoadedModels();

      

Returns

  • Future<List<ModelInfo>> - List of loaded model information

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized

Example

        final loadedModels = await modelManager.findLoadedModels();
for (final info in loadedModels) {
  print('${info.module.modelClass} v${info.version} - Loaded: ${info.isLoaded}');
}

      

findDownloadedModels

Get detailed information about all downloaded models.

        Future<List<ModelInfo>> findDownloadedModels();

      

Returns

  • Future<List<ModelInfo>> - List of downloaded model information

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized

Example

        final downloadedModels = await modelManager.findDownloadedModels();
for (final info in downloadedModels) {
  print('Downloaded: ${info.module.modelClass} v${info.version}');
  print('Size: ${info.module.modelSize}');
  print('Currently loaded: ${info.isLoaded}');
}

      

findDownloadedModel

Get information about a specific downloaded model.

        Future<ModelInfo?> findDownloadedModel(OCRModule module);

      

Parameters

Parameter Type Required Description
module OCRModule Yes The model to query

Returns

  • Future<ModelInfo?> - Model information, or null if not downloaded

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized

Example

        final info = await modelManager.findDownloadedModel(module);
if (info != null) {
  print('Model version: ${info.version}');
  print('Is loaded: ${info.isLoaded}');
} else {
  print('Model not downloaded');
}

      

checkModelUpdates

Check if a newer version of a model is available.

        Future<ModelUpdateInfo> checkModelUpdates({
  required OCRModule module,
  String? apiKey,
  String? token,
});

      

Parameters

Parameter Type Required Description
module OCRModule Yes The model to check for updates
apiKey String? No* API key for authentication
token String? No* Token for authentication

* Either apiKey or token is required

Returns

  • Future<ModelUpdateInfo> - Update availability information

Throws

  • ModelSdkNotInitializedException - ModelManager not initialized
  • ModelNetworkException - Network error
  • ModelException - Authentication or other errors

Example

        final updateInfo = await modelManager.checkModelUpdates(
  module: module,
  apiKey: 'YOUR_API_KEY',
);

if (updateInfo.updateAvailable) {
  print('Update available for ${module.modelClass}!');
  
  // Download the update
  await modelManager.downloadModel(
    module: module,
    apiKey: 'YOUR_API_KEY',
  );
}

      

Data Models

ModelInfo

Detailed information about a model.

        class ModelInfo {
  final OCRModule module;         // Model class and size
  final String version;            // Version number (e.g., "2.4.31")
  final String versionId;          // Unique version identifier
  final bool isLoaded;             // Whether model is currently loaded
  
  const ModelInfo({
    required this.module,
    required this.version,
    required this.versionId,
    required this.isLoaded,
  });
  
  // Helper methods
  Map<String, dynamic> toMap();
  static ModelInfo fromMap(Map<String, dynamic> map);
}

      

Fields

Field Type Description
module OCRModule The model configuration (class + size)
version String Human-readable version (e.g., "2.4.31")
versionId String Unique version identifier for internal use
isLoaded bool Whether model is currently loaded in memory

DownloadProgress

Progress information for model downloads.

        class DownloadProgress {
  final OCRModule module;          // Model being downloaded
  final double progress;           // Progress from 0.0 to 1.0
  final int bytesDownloaded;       // Bytes downloaded so far
  final int totalBytes;            // Total bytes to download
  
  const DownloadProgress({
    required this.module,
    required this.progress,
    this.bytesDownloaded = 0,
    this.totalBytes = 0,
  });
  
  // Convenience getters
  double get progressPercent => progress * 100;
  String get progressString => '${progressPercent.toStringAsFixed(1)}%';
  
  // Helper methods
  Map<String, dynamic> toMap();
  static DownloadProgress fromMap(Map<String, dynamic> map);
}

      

Fields

Field Type Description
module OCRModule The model being downloaded
progress double Progress from 0.0 (0%) to 1.0 (100%)
bytesDownloaded int Number of bytes downloaded
totalBytes int Total bytes to download

Convenience Properties

Property Type Description
progressPercent double Progress as percentage (0-100)
progressString String Formatted progress string (e.g., "45.2%")

ModelUpdateInfo

Information about model update availability.

        class ModelUpdateInfo {
  final OCRModule module;          // Model checked
  final bool updateAvailable;      // Whether update is available
  
  const ModelUpdateInfo({
    required this.module,
    required this.updateAvailable,
  });
  
  // Helper methods
  Map<String, dynamic> toMap();
  static ModelUpdateInfo fromMap(Map<String, dynamic> map);
}

      

Fields

Field Type Description
module OCRModule The model that was checked
updateAvailable bool true if newer version available

Enums

ModelClass

Represents the type of OCR model.

        enum ModelClass {
  shippingLabel(1),
  itemLabel(2),
  documentClassification(3);
  
  final int value;
  const ModelClass(this.value);
}

      

Values

Value Integer Description Supported Sizes
shippingLabel 1 Shipping label OCR micro, large
itemLabel 2 Item label OCR large
documentClassification 3 Document classification large

ModelSize

Represents the size variant of a model.

        enum ModelSize {
  micro(1),
  large(2);
  
  final int value;
  const ModelSize(this.value);
}

      

Values

Value Integer Description File Size (Approx.)
micro 1 Smaller, faster model ~5-10 MB
large 2 Larger, more accurate model ~50-100 MB

Compatibility Matrix

Model Class Micro Large
Shipping Label
Item Label
Document Classification

ExecutionProvider

Android only: Execution provider for model inference.

        enum ExecutionProvider {
  cpu(1),      // CPU execution
  nnapi(2),    // Android Neural Networks API
  xnnpack(3);  // XNNPACK (optimized for mobile)
  
  final int value;
  const ExecutionProvider(this.value);
}

      

Values

Value Integer Description Performance Compatibility
cpu 1 CPU-based inference Baseline All devices
nnapi 2 Android Neural Networks API Faster on supported devices Android 8.1+
xnnpack 3 XNNPACK library Optimized for ARM Most modern devices

Notes

  • Only used on Android; ignored on iOS
  • nnapi provides hardware acceleration on supported devices
  • xnnpack is recommended for best mobile performance
  • Defaults to cpu if not specified

Exceptions

All exceptions extend ModelException base class.

Exception Hierarchy

        ModelException (base)
├── ModelSdkNotInitializedException
├── ModelNoNetworkException
├── ModelNetworkException
├── ModelStorageException
├── ModelNotFoundException
├── ModelAlreadyDownloadedException
├── ModelAlreadyLoadedException
└── ModelLoadException

      

ModelException

Base exception class for all model-related errors.

        class ModelException implements Exception {
  final String message;
  final String? code;
  final dynamic details;
  
  const ModelException(this.message, [this.code, this.details]);
  
  @override
  String toString() => 'ModelException: $message${code != null ? ' (code: $code)' : ''}';
}

      

ModelSdkNotInitializedException

Thrown when attempting to use ModelManager before initialization.

        class ModelSdkNotInitializedException extends ModelException {
  ModelSdkNotInitializedException() 
    : super('ModelManager has not been initialized. Call ModelManager.initialize() first.');
}

      

When Thrown

  • Any ModelManager method called before initialize()
  • VisionSDK not initialized before ModelManager

Solution

        // Initialize VisionSDK first
await VisionSDK().initialize(Environment.sandbox);

// Then initialize ModelManager
await ModelManager.initialize((builder) {
  builder.enableLogging(true);
});

      

ModelNoNetworkException

Thrown when network is unavailable for operations requiring connectivity.

        class ModelNoNetworkException extends ModelException {
  ModelNoNetworkException() 
    : super('No network connection available');
}

      

When Thrown

  • downloadModel() called without network
  • checkModelUpdates() called without network

Solution

  • Check network connectivity before calling
  • Show user-friendly error message
  • Retry when network is available

ModelNetworkException

Thrown for network-related errors during download or API calls.

        class ModelNetworkException extends ModelException {
  ModelNetworkException(String message, [String? code, dynamic details]) 
    : super(message, code, details);
}

      

When Thrown

  • Download fails due to server error
  • API authentication fails
  • Timeout during download
  • Invalid API key or token

Common Causes

  • Invalid API key or token
  • Server maintenance
  • Slow/unstable connection
  • Rate limiting

ModelStorageException

Thrown for storage-related errors.

        class ModelStorageException extends ModelException {
  ModelStorageException(String message, [String? code, dynamic details]) 
    : super(message, code, details);
}

      

When Thrown

  • Insufficient storage space
  • File system write error
  • Storage permissions denied

Solution

  • Check available storage before download
  • Request storage permissions
  • Free up space

ModelNotFoundException

Thrown when attempting to operate on a non-existent model.

        class ModelNotFoundException extends ModelException {
  ModelNotFoundException(String message, [String? code, dynamic details]) 
    : super(message, code, details);
}

      

When Thrown

  • loadModel() on model not downloaded
  • unloadModel() on model not loaded
  • deleteModel() on model not downloaded
  • cancelDownload() with no active download

ModelAlreadyDownloadedException

Thrown when attempting to download an already-downloaded model.

        class ModelAlreadyDownloadedException extends ModelException {
  ModelAlreadyDownloadedException(String message) : super(message);
}

      

When Thrown

  • downloadModel() on already-downloaded model

Solution

  • Check with findDownloadedModel() before downloading
  • Delete old version before re-downloading

ModelAlreadyLoadedException

Thrown when attempting to load an already-loaded model.

        class ModelAlreadyLoadedException extends ModelException {
  ModelAlreadyLoadedException(String message) : super(message);
}

      

When Thrown

  • loadModel() on already-loaded model

Solution

  • Check with isModelLoaded() before loading

ModelLoadException

Thrown when model loading fails.

        class ModelLoadException extends ModelException {
  ModelLoadException(String message, [String? code, dynamic details]) 
    : super(message, code, details);
}

      

When Thrown

  • Model files corrupted
  • Incompatible model format
  • Insufficient memory
  • Invalid execution provider (Android)

Solution

  • Delete and re-download model
  • Free up memory
  • Try different execution provider (Android)

Lifecycle Events

Monitor model lifecycle events using ModelLifecycleListener.

ModelLifecycleListener

Abstract base class for lifecycle callbacks.

        abstract class ModelLifecycleListener {
  void onDownloadStarted(OCRModule module) {}
  void onDownloadProgress(DownloadProgress progress) {}
  void onDownloadCompleted(OCRModule module) {}
  void onDownloadFailed(OCRModule module, ModelException exception) {}
  void onDownloadCancelled(OCRModule module) {}
  void onModelLoaded(OCRModule module) {}
  void onModelUnloaded(OCRModule module) {}
  void onModelDeleted(OCRModule module) {}
}

      

Event: onDownloadStarted

Called when a model download begins.

        void onDownloadStarted(OCRModule module);

      

Parameters

Parameter Type Description
module OCRModule The model that started downloading

Example

        @override
void onDownloadStarted(OCRModule module) {
  print('Download started: ${module.modelClass} (${module.modelSize})');
  setState(() {
    _downloadingModels.add(module);
  });
}

      

Event: onDownloadProgress

Called periodically during download with progress updates.

        void onDownloadProgress(DownloadProgress progress);

      

Parameters

Parameter Type Description
progress DownloadProgress Current download progress

Example

        @override
void onDownloadProgress(DownloadProgress progress) {
  print('${progress.module.modelClass}: ${progress.progressPercent}%');
  setState(() {
    _progress[progress.module] = progress.progress;
  });
}

      

Event: onDownloadCompleted

Called when a model download completes successfully.

        void onDownloadCompleted(OCRModule module);

      

Parameters

Parameter Type Description
module OCRModule The model that finished downloading

Example

        @override
void onDownloadCompleted(OCRModule module) {
  print('Download complete: ${module.modelClass}');
  setState(() {
    _downloadingModels.remove(module);
    _downloadedModels.add(module);
  });
}

      

Event: onDownloadFailed

Called when a model download fails.

        void onDownloadFailed(OCRModule module, ModelException exception);

      

Parameters

Parameter Type Description
module OCRModule The model that failed to download
exception ModelException The error that occurred

Example

        @override
void onDownloadFailed(OCRModule module, ModelException exception) {
  print('Download failed: ${module.modelClass} - ${exception.message}');
  
  // Show error to user
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(content: Text('Download failed: ${exception.message}')),
  );
  
  setState(() {
    _downloadingModels.remove(module);
  });
}

      

Event: onDownloadCancelled

Called when a model download is cancelled.

        void onDownloadCancelled(OCRModule module);

      

Parameters

Parameter Type Description
module OCRModule The model whose download was cancelled

Example

        @override
void onDownloadCancelled(OCRModule module) {
  print('Download cancelled: ${module.modelClass}');
  setState(() {
    _downloadingModels.remove(module);
  });
}

      

Event: onModelLoaded

Called when a model is successfully loaded into memory.

        void onModelLoaded(OCRModule module);

      

Parameters

Parameter Type Description
module OCRModule The model that was loaded

Example

        @override
void onModelLoaded(OCRModule module) {
  print('Model loaded: ${module.modelClass}');
  setState(() {
    _loadedModels.add(module);
  });
}

      

Event: onModelUnloaded

Called when a model is unloaded from memory.

        void onModelUnloaded(OCRModule module);

      

Parameters

Parameter Type Description
module OCRModule The model that was unloaded

Example

        @override
void onModelUnloaded(OCRModule module) {
  print('Model unloaded: ${module.modelClass}');
  setState(() {
    _loadedModels.remove(module);
  });
}

      

Event: onModelDeleted

Called when a model is deleted from storage.

        void onModelDeleted(OCRModule module);

      

Parameters

Parameter Type Description
module OCRModule The model that was deleted

Example

        @override
void onModelDeleted(OCRModule module) {
  print('Model deleted: ${module.modelClass}');
  setState(() {
    _downloadedModels.remove(module);
    _loadedModels.remove(module);
  });
}

      

ModelLifecycleCallbacks

Convenience class for selective event handling using callbacks.

        class ModelLifecycleCallbacks extends ModelLifecycleListener {
  final Function(OCRModule)? onDownloadStartedCallback;
  final Function(DownloadProgress)? onDownloadProgressCallback;
  final Function(OCRModule)? onDownloadCompletedCallback;
  final Function(OCRModule, ModelException)? onDownloadFailedCallback;
  final Function(OCRModule)? onDownloadCancelledCallback;
  final Function(OCRModule)? onModelLoadedCallback;
  final Function(OCRModule)? onModelUnloadedCallback;
  final Function(OCRModule)? onModelDeletedCallback;
  
  ModelLifecycleCallbacks({
    this.onDownloadStartedCallback,
    this.onDownloadProgressCallback,
    this.onDownloadCompletedCallback,
    this.onDownloadFailedCallback,
    this.onDownloadCancelledCallback,
    this.onModelLoadedCallback,
    this.onModelUnloadedCallback,
    this.onModelDeletedCallback,
  });
}

      

Example

        final listener = ModelLifecycleCallbacks(
  onDownloadProgressCallback: (progress) {
    print('Progress: ${progress.progressPercent}%');
  },
  onDownloadCompletedCallback: (module) {
    print('Completed: ${module.modelClass}');
  },
  onDownloadFailedCallback: (module, exception) {
    print('Failed: ${exception.message}');
  },
);

await ModelManager.initialize((builder) {
  builder.lifecycleListener(listener);
});

      

Usage Examples

Complete Workflow Example

        import 'package:fluttervisionsdkplugin/model_manager.dart';

class ModelManagementExample extends StatefulWidget {
  @override
  _ModelManagementExampleState createState() => _ModelManagementExampleState();
}

class _ModelManagementExampleState extends State<ModelManagementExample> 
    implements ModelLifecycleListener {
  
  late ModelManager _modelManager;
  final _apiKey = 'YOUR_API_KEY';
  
  final _module = OCRModule(
    modelClass: ModelClass.shippingLabel,
    modelSize: ModelSize.large,
  );
  
  double _downloadProgress = 0.0;
  bool _isDownloading = false;
  bool _isLoaded = false;
  ModelInfo? _modelInfo;
  
  @override
  void initState() {
    super.initState();
    _initializeModelManager();
  }
  
  Future<void> _initializeModelManager() async {
    try {
      // Initialize VisionSDK first
      await VisionSDK().initialize(Environment.sandbox);
      
      // Initialize ModelManager with lifecycle listener
      await ModelManager.initialize((builder) {
        builder
          .enableLogging(true)
          .lifecycleListener(this);
      });
      
      _modelManager = ModelManager.getInstance();
      
      // Check current model state
      await _refreshModelState();
      
    } catch (e) {
      print('Initialization error: $e');
    }
  }
  
  Future<void> _refreshModelState() async {
    final info = await _modelManager.findDownloadedModel(_module);
    final isLoaded = await _modelManager.isModelLoaded(_module);
    
    setState(() {
      _modelInfo = info;
      _isLoaded = isLoaded;
    });
  }
  
  Future<void> _downloadModel() async {
    setState(() => _isDownloading = true);
    
    try {
      await _modelManager.downloadModel(
        module: _module,
        apiKey: _apiKey,
        onProgress: (progress) {
          setState(() {
            _downloadProgress = progress.progress;
          });
        },
      );
    } on ModelAlreadyDownloadedException {
      print('Model already downloaded');
    } on ModelNetworkException catch (e) {
      _showError('Network error: ${e.message}');
    } on ModelStorageException catch (e) {
      _showError('Storage error: ${e.message}');
    } catch (e) {
      _showError('Download failed: $e');
    } finally {
      setState(() => _isDownloading = false);
    }
  }
  
  Future<void> _loadModel() async {
    try {
      await _modelManager.loadModel(
        module: _module,
        apiKey: _apiKey,
        executionProvider: ExecutionProvider.nnapi, // Android only
      );
    } on ModelNotFoundException {
      _showError('Model not downloaded. Download it first.');
    } on ModelAlreadyLoadedException {
      print('Model already loaded');
    } on ModelLoadException catch (e) {
      _showError('Load failed: ${e.message}');
    }
  }
  
  Future<void> _unloadModel() async {
    try {
      await _modelManager.unloadModel(_module);
    } catch (e) {
      _showError('Unload failed: $e');
    }
  }
  
  Future<void> _deleteModel() async {
    try {
      await _modelManager.deleteModel(_module);
      await _refreshModelState();
    } catch (e) {
      _showError('Delete failed: $e');
    }
  }
  
  Future<void> _checkForUpdates() async {
    try {
      final updateInfo = await _modelManager.checkModelUpdates(
        module: _module,
        apiKey: _apiKey,
      );
      
      if (updateInfo.updateAvailable) {
        _showMessage('Update available! Tap download to update.');
      } else {
        _showMessage('Model is up to date');
      }
    } on ModelNetworkException catch (e) {
      _showError('Network error: ${e.message}');
    }
  }
  
  void _showError(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message), backgroundColor: Colors.red),
    );
  }
  
  void _showMessage(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }
  
  // Lifecycle callbacks
  
  @override
  void onDownloadStarted(OCRModule module) {
    print('Download started: ${module.modelClass}');
  }
  
  @override
  void onDownloadProgress(DownloadProgress progress) {
    setState(() {
      _downloadProgress = progress.progress;
    });
  }
  
  @override
  void onDownloadCompleted(OCRModule module) {
    print('Download completed: ${module.modelClass}');
    _showMessage('Download complete!');
    _refreshModelState();
  }
  
  @override
  void onDownloadFailed(OCRModule module, ModelException exception) {
    _showError('Download failed: ${exception.message}');
  }
  
  @override
  void onModelLoaded(OCRModule module) {
    print('Model loaded: ${module.modelClass}');
    setState(() => _isLoaded = true);
  }
  
  @override
  void onModelUnloaded(OCRModule module) {
    print('Model unloaded: ${module.modelClass}');
    setState(() => _isLoaded = false);
  }
  
  @override
  void onModelDeleted(OCRModule module) {
    print('Model deleted: ${module.modelClass}');
    _showMessage('Model deleted');
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Model Management')),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            if (_modelInfo != null) ...[
              Text('Model: ${_module.modelClass}'),
              Text('Size: ${_module.modelSize}'),
              Text('Version: ${_modelInfo!.version}'),
              Text('Downloaded: Yes'),
              Text('Loaded: ${_isLoaded ? "Yes" : "No"}'),
              SizedBox(height: 16),
            ] else ...[
              Text('Model not downloaded'),
              SizedBox(height: 16),
            ],
            
            if (_isDownloading) ...[
              LinearProgressIndicator(value: _downloadProgress),
              SizedBox(height: 8),
              Text('${(_downloadProgress * 100).toStringAsFixed(1)}%'),
              SizedBox(height: 16),
            ],
            
            ElevatedButton(
              onPressed: _modelInfo == null && !_isDownloading 
                ? _downloadModel 
                : null,
              child: Text('Download Model'),
            ),
            
            ElevatedButton(
              onPressed: _modelInfo != null && !_isLoaded 
                ? _loadModel 
                : null,
              child: Text('Load Model'),
            ),
            
            ElevatedButton(
              onPressed: _isLoaded ? _unloadModel : null,
              child: Text('Unload Model'),
            ),
            
            ElevatedButton(
              onPressed: _modelInfo != null ? _checkForUpdates : null,
              child: Text('Check for Updates'),
            ),
            
            ElevatedButton(
              onPressed: _modelInfo != null ? _deleteModel : null,
              child: Text('Delete Model'),
              style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
            ),
          ],
        ),
      ),
    );
  }
}

      

Onboarding Flow Example

Pre-download models during app onboarding for offline-first UX.

        class OnboardingFlow extends StatefulWidget {
  @override
  _OnboardingFlowState createState() => _OnboardingFlowState();
}

class _OnboardingFlowState extends State<OnboardingFlow> {
  final _apiKey = 'YOUR_API_KEY';
  final _modelsToDownload = [
    OCRModule(modelClass: ModelClass.shippingLabel, modelSize: ModelSize.large),
    OCRModule(modelClass: ModelClass.itemLabel, modelSize: ModelSize.large),
  ];
  
  int _currentModelIndex = 0;
  double _overallProgress = 0.0;
  String _status = 'Preparing...';
  
  @override
  void initState() {
    super.initState();
    _startOnboarding();
  }
  
  Future<void> _startOnboarding() async {
    try {
      // Initialize SDK
      await VisionSDK().initialize(Environment.sandbox);
      await ModelManager.initialize((builder) {
        builder.enableLogging(true);
      });
      
      final modelManager = ModelManager.getInstance();
      
      // Download each model sequentially
      for (int i = 0; i < _modelsToDownload.length; i++) {
        _currentModelIndex = i;
        final module = _modelsToDownload[i];
        
        setState(() {
          _status = 'Downloading ${module.modelClass} (${i + 1}/${_modelsToDownload.length})';
        });
        
        await modelManager.downloadModel(
          module: module,
          apiKey: _apiKey,
          onProgress: (progress) {
            setState(() {
              // Calculate overall progress
              final baseProgress = i / _modelsToDownload.length;
              final currentProgress = progress.progress / _modelsToDownload.length;
              _overallProgress = baseProgress + currentProgress;
            });
          },
        );
      }
      
      setState(() {
        _status = 'Complete!';
        _overallProgress = 1.0;
      });
      
      // Navigate to main app after delay
      await Future.delayed(Duration(seconds: 1));
      Navigator.of(context).pushReplacement(
        MaterialPageRoute(builder: (_) => MainApp()),
      );
      
    } catch (e) {
      setState(() {
        _status = 'Error: $e';
      });
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(32),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Setting up...',
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
              SizedBox(height: 32),
              LinearProgressIndicator(value: _overallProgress),
              SizedBox(height: 16),
              Text(_status),
              SizedBox(height: 8),
              Text('${(_overallProgress * 100).toStringAsFixed(0)}%'),
            ],
          ),
        ),
      ),
    );
  }
}

      

Background Update Check Example

Check for and download model updates in the background.

        class BackgroundUpdateService {
  static final BackgroundUpdateService _instance = BackgroundUpdateService._();
  factory BackgroundUpdateService() => _instance;
  BackgroundUpdateService._();
  
  static const _apiKey = 'YOUR_API_KEY';
  static const _updateCheckInterval = Duration(days: 1);
  
  Timer? _updateTimer;
  
  void startPeriodicUpdateCheck() {
    _updateTimer?.cancel();
    _updateTimer = Timer.periodic(_updateCheckInterval, (_) {
      _checkForUpdates();
    });
    
    // Check immediately on start
    _checkForUpdates();
  }
  
  void stopPeriodicUpdateCheck() {
    _updateTimer?.cancel();
    _updateTimer = null;
  }
  
  Future<void> _checkForUpdates() async {
    try {
      final modelManager = ModelManager.getInstance();
      
      // Get all downloaded models
      final downloadedModels = await modelManager.findDownloadedModels();
      
      for (final modelInfo in downloadedModels) {
        // Check if update available
        final updateInfo = await modelManager.checkModelUpdates(
          module: modelInfo.module,
          apiKey: _apiKey,
        );
        
        if (updateInfo.updateAvailable) {
          print('Update available for ${modelInfo.module.modelClass}');
          
          // Download update in background
          await _downloadUpdate(modelInfo.module);
        }
      }
    } catch (e) {
      print('Update check failed: $e');
    }
  }
  
  Future<void> _downloadUpdate(OCRModule module) async {
    try {
      final modelManager = ModelManager.getInstance();
      
      // Check if model is currently loaded
      final wasLoaded = await modelManager.isModelLoaded(module);
      
      // Unload if loaded
      if (wasLoaded) {
        await modelManager.unloadModel(module);
      }
      
      // Delete old version
      await modelManager.deleteModel(module);
      
      // Download new version
      await modelManager.downloadModel(
        module: module,
        apiKey: _apiKey,
      );
      
      // Reload if it was loaded before
      if (wasLoaded) {
        await modelManager.loadModel(
          module: module,
          apiKey: _apiKey,
        );
      }
      
      print('Update completed for ${module.modelClass}');
      
    } catch (e) {
      print('Update download failed: $e');
    }
  }
}

// Usage in main app
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await VisionSDK().initialize(Environment.sandbox);
  await ModelManager.initialize((builder) {
    builder.enableLogging(true);
  });
  
  // Start background update service
  BackgroundUpdateService().startPeriodicUpdateCheck();
  
  runApp(MyApp());
}

      

Platform Differences

iOS vs Android Feature Parity

Feature iOS Android Notes
Download Model Full parity
Cancel Download Full parity
Load Model Android supports executionProvider
Unload Model Full parity
Delete Model Full parity
Query Models Full parity
Check Updates Full parity
Progress Tracking Full parity
Lifecycle Events Full parity
Execution Provider Android-only hardware acceleration

Best Practices

1. Initialize Early

        // DO: Initialize in main() or app startup
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await VisionSDK().initialize(Environment.sandbox);
  await ModelManager.initialize((builder) {
    builder.enableLogging(true);
  });
  
  runApp(MyApp());
}

      

2. Check Before Operating

        // DO: Check state before operations
if (await modelManager.isModelLoaded(module)) {
  // Model ready for inference
} else {
  final info = await modelManager.findDownloadedModel(module);
  if (info != null) {
    await modelManager.loadModel(module: module, apiKey: apiKey);
  } else {
    await modelManager.downloadModel(module: module, apiKey: apiKey);
  }
}

      

3. Handle Errors Gracefully

        // DO: Use specific exception types
try {
  await modelManager.downloadModel(module: module, apiKey: apiKey);
} on ModelAlreadyDownloadedException {
  // Model exists, just load it
  await modelManager.loadModel(module: module, apiKey: apiKey);
} on ModelNetworkException catch (e) {
  showError('Network error: ${e.message}');
} on ModelStorageException catch (e) {
  showError('Storage full: ${e.message}');
}

      

4. Unload Unused Models

        // DO: Free memory when models not needed
@override
void dispose() {
  // Unload models when leaving screen
  modelManager.unloadModel(module);
  super.dispose();
}

      

5. Pre-download for Offline

        // DO: Download during onboarding or idle time
Future<void> prepareForOfflineUse() async {
  await modelManager.downloadModel(
    module: OCRModule(
      modelClass: ModelClass.shippingLabel,
      modelSize: ModelSize.large,
    ),
    apiKey: apiKey,
  );
}

      

6. Check for Updates Periodically

        // DO: Check for updates daily or weekly
Future<void> checkUpdatesIfNeeded() async {
  final lastCheck = await getLastUpdateCheckTime();
  if (DateTime.now().difference(lastCheck) > Duration(days: 7)) {
    final models = await modelManager.findDownloadedModels();
    for (final model in models) {
      final updateInfo = await modelManager.checkModelUpdates(
        module: model.module,
        apiKey: apiKey,
      );
      if (updateInfo.updateAvailable) {
        // Notify user or download silently
      }
    }
    await setLastUpdateCheckTime(DateTime.now());
  }
}

      

7. Use Lifecycle Listener

        // DO: Monitor lifecycle events for UI updates
await ModelManager.initialize((builder) {
  builder.lifecycleListener(MyLifecycleListener());
});

class MyLifecycleListener extends ModelLifecycleListener {
  @override
  void onDownloadCompleted(OCRModule module) {
    // Update UI, notify user
  }
  
  @override
  void onDownloadFailed(OCRModule module, ModelException exception) {
    // Show error, retry logic
  }
}

      

Troubleshooting

Model Not Found After Download

Symptom: ModelNotFoundException when trying to load after successful download.

Solution:

        // Query to verify download
final info = await modelManager.findDownloadedModel(module);
if (info != null) {
  print('Model exists: ${info.version}');
} else {
  // Re-download
  await modelManager.downloadModel(module: module, apiKey: apiKey);
}

      

Download Hangs or Never Completes

Symptom: Download progress stops or lifecycle events not firing.

Solution:

  • Check network connection
  • Cancel and retry:
        await modelManager.cancelDownload(module);
await Future.delayed(Duration(seconds: 1));
await modelManager.downloadModel(module: module, apiKey: apiKey);

      

Memory Issues When Loading Multiple Models

Symptom: App crashes or ModelLoadException due to insufficient memory.

Solution:

        // Unload unused models before loading new ones
final loadedCount = await modelManager.loadedModelCount();
if (loadedCount >= 2) {
  // Unload least recently used model
  await modelManager.unloadModel(leastRecentlyUsedModule);
}
await modelManager.loadModel(module: newModule, apiKey: apiKey);

      

Authentication Errors

Symptom: ModelNetworkException with "Invalid API key" or "Unauthorized".

Solution:

  • Verify API key is correct
  • Check environment (sandbox vs production)
  • Use token instead of API key:
        await modelManager.downloadModel(
  module: module,
  token: 'YOUR_TOKEN', // Instead of apiKey
);