Install
Add the plugin to pubspec.yaml, then run flutter pub get.
dependencies: nosmai_moderation_sdk: ^1.0.0
Android. Bundle the native AAR in your host app. Download nosmai-detection.aar from the Android releases, put it in android/app/libs/, and reference it:
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-v8aonly, 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.
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.
final r = await NosmaiModeration.analyzeImage(file.path);
if (r.isUnsafe) {
// r.detections -> [{category, confidence}], r.nsfw -> safe / warn / block
for (final d in r.detections) {
print('${d?.category?.name} ${(d!.confidence! * 100).round()}%');
}
}
Moderate a video
Samples one frame every frameIntervalMs and aggregates.
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.
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.
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();
On the live camera, a suggestive (warn) verdict that persists for several seconds is escalated to block, so sustained borderline content is treated as unsafe.
Thresholds
Adjust any object class or NSFW bar at runtime (lower is stricter).
NosmaiModeration.setThreshold(NosmaiCategory.weapon, 0.7); NosmaiModeration.setNsfwThreshold(NosmaiNsfwClass.explicit, 0.45); // BLOCK NosmaiModeration.setNsfwThreshold(NosmaiNsfwClass.sexy, 0.55); // WARN
Cleanup
NosmaiModeration.shutdown();
[!NOTE]
analyzeImage,analyzeVideo,moderateTextandinitializeare async. They run native inference off the platform thread, so the UI never blocks.