Nosmai  Effects Nosmai  Moderation Nosmai Try-ons coming soon
Docs menu Flutter
How-to guide

Use Nosmai Effects with Agora in Flutter

Publish the processed Flutter camera feed through the official Nosmai Agora bridge on iOS and Android.

Platform Flutter SDK version Nosmai Flutter 1.0.2 · Agora Flutter 6.5.3 Updated Aug 22, 2026 Time 8 min

The streaming path

Camera frames pass through Nosmai Effects before Agora publishes them

Nosmai owns and processes the camera feed. Agora publishes only the processed output while the application keeps its local preview and session controls.

Prerequisites

Install the packages

dependencies:
  nosmai_effects_sdk: ^1.0.2
  nosmai_agora_bridge:
    git:
      url: https://github.com/nosmai/nosmai_agora_bridge.git
  agora_rtc_engine: ^6.5.3

Declare and request camera and microphone permissions on both platforms.

Use the required initialization order

Create the shared native engine before Nosmai creates its graphics context:

final handle = await NosmaiAgoraBridge.getNativeHandle(
  agoraAppId: agoraAppId,
);

final initialized = await NosmaiFlutter.initialize(nosmaiLicenseKey);
if (!initialized) {
  throw StateError('Nosmai initialization was not accepted');
}

final engine = createAgoraRtcEngine(sharedNativeHandle: handle);

Calling NosmaiFlutter.initialize first can leave Android remote video black. On iOS, using the bridge handle also prevents two Dart wrappers from driving conflicting native engine state.

Configure a portrait stream

await engine.initialize(RtcEngineContext(appId: agoraAppId));
await engine.enableVideo();
await engine.enableAudio();

await engine.setVideoEncoderConfiguration(
  const VideoEncoderConfiguration(
    dimensions: VideoDimensions(width: 720, height: 1280),
    frameRate: 30,
    orientationMode: OrientationMode.orientationModeFixedPortrait,
  ),
);

Start streaming

final started = await NosmaiAgoraBridge.startStreaming(
  channelName: channelName,
  token: token?.isEmpty == true ? null : token,
  uid: uid,
);

The bridge joins with a custom video track. Do not also join or publish the raw camera on this path. Guard the button against duplicate starts.

Stop and dispose

await NosmaiAgoraBridge.stopStreaming();
if (!Platform.isIOS) {
  await engine.leaveChannel();
}

await engine.release();
await NosmaiAgoraBridge.disposeNative();

Call stopStreaming for each session. Release the engine and bridge only when the whole live experience is being torn down.

Common errors

SymptomCauseFix
Local preview works but remote video is blackThe shared native handle was created too late or ignoredCall getNativeHandle before Nosmai initialization and wrap the same handle
Second live session freezesThe first bridge session was not stoppedPair every successful start with stopStreaming
Remote video is croppedEncoder aspect or orientation differs from 720 by 1280 portrait outputUse fixed portrait and do not override it later
iOS camera switch loses outputThe bridge was not notifiedCall NosmaiAgoraBridge.notifyCameraSwitch() after switching

Verify it worked

Apply a visible effect, join from a second device, and confirm the remote video is filtered, portrait-oriented, and stable after stop and rejoin.

Next steps

References

Was this page helpful?

Your response helps us prioritize documentation improvements.

Nosmai

An AR and AI company. Founded on the idea that the next decade of software will be visual, and that the tools for it should be within reach of anyone building.

Newsletter

Product updates and release notes. No spam.

© 2026 nosmai, inc · all rights reserved