Flutter SDK Guide
Flutter SDK install, join a channel, events and platform support.
The Flutter platform is Dart with the RtcEngine shape (isomorphic with Android/iOS): RtcEngine + RtcEngineEventHandler, with return values wrapped in Future.
Prerequisites
- Flutter 3.22+, Dart 3.4+.
- Native permissions per platform:
CAMERA/RECORD_AUDIOon Android,NSCameraUsageDescription/NSMicrophoneUsageDescriptionon iOS. - You already have an
appIdand a token service (see Enable services and get credentials).
Install
# pubspec.yaml — local path dependency (not yet published to pub.dev)
dependencies:
nimeet_rtc:
path: ../ni-meet/sdk/flutterGet a token
A Flutter client must not hold customerSecret; tokens are issued by your backend and injected on the Flutter side through a TokenProvider:
final provider = FallbackTokenProvider([
CallbackTokenProvider((channel, uid, role) => authApi.fetchRtcToken(channel, uid, role)),
StaticTokenProvider(Platform.environment['NIMEET_TEST_TOKEN'] ?? ''),
]);Join a channel
import 'package:nimeet_rtc/nimeet_rtc.dart';
class MeetingController extends RtcEngineEventHandler {
late final RtcEngine engine;
Future<void> start(String appId, String token) async {
engine = await RtcEngine.create(RtcEngineConfig(appId: appId));
engine.registerEventHandler(this);
await engine.setChannelProfile(ChannelProfileType.channelProfileLiveBroadcasting);
await engine.enableVideo();
await engine.setupLocalVideo(const VideoCanvas(uid: 0));
await engine.startPreview();
await engine.joinChannel(
token: token,
channelId: 'weekly-sync',
uid: 10001,
options: const ChannelMediaOptions(clientRoleType: ClientRoleType.clientRoleBroadcaster),
);
}
@override
void onJoinChannelSuccess(RtcConnection connection, int elapsed) {
// Join succeeded
}
@override
void onUserJoined(RtcConnection connection, int remoteUid, int elapsed) {
// A remote user joined
}
@override
void onUserOffline(RtcConnection connection, int remoteUid, UserOfflineReasonType reason) {
// A remote user left
}
@override
void onTokenPrivilegeWillExpire(String token) {
// After refreshing the token, call engine.renewToken(newToken)
}
}Publish and subscribe
await engine.muteLocalAudioStream(true); // mute the microphone
await engine.muteLocalVideoStream(true); // turn off the local video
await engine.enableLocalVideo(false); // stop capture
await engine.updateChannelMediaOptions(options);Screen sharing: getScreenCaptureSources() enumerates sources → startScreenCapture(...); stop with stopScreenCapture().
Events
RtcEngineEventHandler is the callback class; it covers onJoinChannelSuccess, onUserJoined, onUserOffline, onFirstRemoteVideoDecoded, onTokenPrivilegeWillExpire, onError, onConnectionStateChanged and others. Flutter callbacks return to the main isolate by default, so you can call setState directly.
Error handling
- A failed
FuturethrowsRtcException(carryingcode/sdkCode/message/retryable); - Asynchronous errors go through the
onErrorcallback; - Unimplemented capabilities throw
-4(ERR_NOT_SUPPORTED).
Troubleshooting
| Symptom | Cause | Handling |
|---|---|---|
| The iOS build fails | Pods are not installed, or the platform minimum is not met | Run cd ios && pod install, and raise platform :ios |
| Black screen on Android | Runtime permission was not requested | The host app must request CAMERA/RECORD_AUDIO itself |
| Callbacks are lost after a hot reload | The handler was recreated | Register in initState and unregister in dispose |
MissingPluginException at compile time | The native side did not register the plugin | Do a full restart (flutter run rather than a hot reload) |
| Cannot recover after the token expires | onTokenPrivilegeWillExpire is not handled | Renew the token and call renewToken |
Next steps
- Full interfaces and overloads: API reference for this platform
- Error codes and troubleshooting: Error code table, FAQ
- Issue tokens on your server: Token authentication
Platform information
| Item | value |
|---|---|
| Platform | Flutter |
| Language | Dart |
| SDK package | nimeet_rtc |
| Interface version | 6.6.4 |
| Compatible Agora API | Agora flutter API |
The public interfaces are aligned with the Agora flutter API; installation and runtime requirements are listed under Prerequisites above.
Availability
This platform's SDK: Generally available. The examples use the public API; runtime capability follows the published SDK package.
join-channel flow
The method names, parameter types and return values match the API reference for the same platform. Where a method has several overloads, the most common one is shown here; see the API reference for all overloads.
| Step | Interface | Method | Overload count |
|---|---|---|---|
| subscribe to remote media | BaseSpatialAudioEngine | muteRemoteAudioStream | 1 |
| Create instance / initialize | LocalSpatialAudioEngine | initialize | 1 |
| Renew the Token | MusicContentCenter | renewToken | 1 |
| enable video and local preview | RtcEngine | enableVideo | 1 |
| Join a channel | RtcEngine | joinChannel | 1 |
| Leave a channel | RtcEngine | leaveChannel | 1 |
| Channel scenarios | RtcEngine | setChannelProfile | 1 |
| switch roles | RtcEngine | setClientRole | 1 |
| bind the local video | RtcEngine | setupLocalVideo | 1 |
muteRemoteAudioStream(mute: int uid,required bool): Future
initialize(): Future
renewToken(token: String): Future
enableVideo(): Future
joinChannel(options: String token,required String channelId,required int uid,required ChannelMediaOptions): Future
leaveChannel(options?: LeaveChannelOptions?): Future
setChannelProfile(profile: ChannelProfileType): Future
setClientRole(options?: ClientRoleType role,ClientRoleOptions?): Future
setupLocalVideo(canvas: VideoCanvas): FutureEvents and callbacks
Callbacks are registered on RtcEngineEventHandler; naming differs by platform, and the full list is in the API reference for the same platform.
| Callbacks | Signature | Purpose |
|---|---|---|
onConnectionStateChanged | onConnectionStateChanged(connection: RtcConnection, state: ConnectionStateType, reason: ConnectionChangedReasonType): void | join result / connection-state changes:update the UI accordingly and decide whether to reconnect. |
onUserJoined | onUserJoined(connection: RtcConnection, remoteUid: int, elapsed: int): void | Remote user joined:allocate the frame container. |
onFirstRemoteVideoDecoded | onFirstRemoteVideoDecoded(connection: RtcConnection, remoteUid: int, width: int, height: int, elapsed: int): void | Remote video status:it can render once the first frame is decoded. |
onUserOffline | onUserOffline(connection: RtcConnection, remoteUid: int, reason: UserOfflineReasonType): void | Remote user left:release the container and subscription. |
onRequestToken | onRequestToken(connection: RtcConnection): void | Token expiring soon:call the server to renew, then `renewToken`. |
onError | onError(err: ErrorCodeType, msg: String): void | Runtime errors:`err` is an Agora error number, route by the error-code table. |
onNetworkQuality | onNetworkQuality(connection: RtcConnection, remoteUid: int, txQuality: QualityType, rxQuality: QualityType): void | Network quality:report to Analytics or prompt the user. |
Error codes
| Constant | value |
|---|---|
RtcEngine | null |
errAborted | 20 |
errAdmGeneralError | 1005 |
errAdmInitPlayout | 1008 |
errAdmInitRecording | 1011 |
errAdmStartPlayout | 1009 |
errAdmStartRecording | 1012 |
errAdmStopPlayout | 1010 |
errAdmStopRecording | 1013 |
errAlreadyInUse | 19 |
errBitrateLimit | 115 |
errBufferTooSmall | 6 |
errClientIsBannedByServer | 123 |
errConnectionInterrupted | 111 |
errConnectionLost | 112 |
errDatastreamDecryptionFailed | 122 |
errDecryptionFailed | 120 |
errEncryptedStreamNotAllowedPublish | 130 |
errFailed | 1 |
errInitNetEngine | 21 |
errInvalidAppId | 101 |
errInvalidArgument | 2 |
errInvalidChannelName | 102 |
errInvalidState | 8 |
errInvalidToken | null |
errInvalidUserAccount | 134 |
errInvalidUserId | 121 |
errJoinChannelRejected | 17 |
errLeaveChannelRejected | 18 |
errLoadMediaEngine | 1001 |
errNoPermission | 9 |
errNoServerResources | 103 |
errNotInChannel | 113 |
errNotInitialized | 7 |
errNotReady | 3 |
errNotSupported | 4 |
errOk | 0 |
errPcmsendBufferoverflow | 201 |
errPcmsendFormat | 200 |
errRefused | 5 |
errResourceLimited | 22 |
errSetClientRoleNotAuthorized | 119 |
errSizeTooLarge | 114 |
errStreamMessageTimeout | 117 |
errTimedout | 10 |
errTokenExpired | 109 |
errTooManyDataStreams | 116 |
errVdmCameraNotAuthorized | 1501 |
the platform REST layer uses REST error codes;the SDK layer maps connection/authentication failures to Agora error numbers, for the mapping table see SDK error identifiers per platform。