# Flutter

> Use Nosmai Moderation in a Flutter app: image, video, text and live-camera moderation.

> For AI agents: the complete documentation index is at https://nosmai.com/llms/moderations.txt

Product: Nosmai Moderation
Group: platform-guides
Source: https://nosmai.com/docs/moderations/flutter/

## Install

Add the plugin to `pubspec.yaml`, then run `flutter pub get`.

```yaml
dependencies:
  nosmai_moderation_sdk: ^2.0.0
```

**Android.** Bundle the native AAR in your host app. Download `nosmai-detection.aar` from the [Android releases](https://github.com/nosmai/moderation-sdk-android/releases), put it in `android/app/libs/`, and reference it:

```kotlin
android {
  defaultConfig {
    minSdk = 24
    ndk { abiFilters += "arm64-v8a" }
  }
}
dependencies {
  implementation(files("libs/nosmai-detection.aar"))
}
```

**iOS** needs no extra step. `pod install` pulls the native SDK with the plugin.

**Requirements:** Flutter 3+, Android `minSdk 24` (arm64-v8a), iOS 14+. For live camera add the camera permission to `AndroidManifest.xml` and `NSCameraUsageDescription` to `Info.plist` (the example uses `permission_handler`). For iOS App Store uploads also set `ITSAppUsesNonExemptEncryption` to `NO` in `Info.plist`, since the SDK only encrypts its own bundled models, which is export-compliance exempt.

> [!NOTE]
Distribute Android as an App Bundle (AAB). The SDK ships `arm64-v8a` only, so Play hides it from 32-bit-only devices, and it does not run on x86_64 emulators.

## Initialize

Call once at startup with your license key. It runs natively on a background thread.

```dart
import 'package:nosmai_moderation_sdk/nosmai_moderation_sdk.dart';

final res = await NosmaiModeration.initialize('NOSMAI-XXXX');
if (!res.success) {
  debugPrint('Nosmai init failed: ${res.error}');
}
```

## Moderate an image

Pass a file path (for example from `image_picker`). Returns a `NosmaiResult`.

```dart
final r = await NosmaiModeration.analyzeImage(file.path);
if (r.isUnsafe) {
  // r.nsfw -> safe / warn / block
  print('nsfw: ${r.nsfw}');
}
```

## Moderate a video

Samples one frame every `frameIntervalMs` and aggregates.

```dart
final v = await NosmaiModeration.analyzeVideo(file.path, 500);
if (v.isUnsafe) {
  print('categories: ${v.categories}, frames: ${v.framesAnalyzed}');
}
```

## Moderate text

Call `initialize` first (it validates the license), then load the text model once (it is larger), then moderate messages.

```dart
await NosmaiModeration.initializeText();          // after initialize()

final t = await NosmaiModeration.moderateText('some message');
if (t.blocked) {
  // t.layer (blocklist or classifier), t.category, t.matchedWord
  print('blocked: ${t.category?.name}');
}
```

## Live camera

The camera, frame capture and detection all run natively. Only the per-frame `NosmaiResult` crosses to Dart.

```dart
import 'package:permission_handler/permission_handler.dart';

// 1. show the native preview
const NosmaiCameraPreview();

// 2. start and listen, after granting camera permission
await Permission.camera.request();
final sub = NosmaiLive.results().listen(
  (r) => setState(() => _verdict = r.isUnsafe ? 'UNSAFE' : 'SAFE'),
  onError: (e) => debugPrint('camera failed to start: $e'),  // permission or no camera
);
await NosmaiLive.start(facing: NosmaiCameraFacing.back);  // .front is also supported, and
                                                          // falls back to the other if unavailable

// 3. on leave, always stop. The camera does not stop itself on widget dispose.
await NosmaiLive.stop();
sub.cancel();
```

## Thresholds

Adjust the NSFW bars at runtime (lower is stricter).

```dart
NosmaiModeration.setNsfwThreshold(NosmaiNsfwClass.explicit, 0.45); // BLOCK
NosmaiModeration.setNsfwThreshold(NosmaiNsfwClass.sexy, 0.55);     // WARN
```

## Cleanup

```dart
NosmaiModeration.shutdown();
```

> [!NOTE]
`analyzeImage`, `analyzeVideo`, `moderateText` and `initialize` are async. They run native inference off the platform thread, so the UI never blocks.
