qriib_meet 0.1.0
qriib_meet: ^0.1.0 copied to clipboard
Flutter APIs for creating Qriib rooms and embedding secure, customizable audio and video meeting experiences.
qriib_meet #
Flutter room and meeting APIs for Qriib applications.
Getting started #
Add the package to your Flutter application, create a single client from
trusted credentials, create or join a room, then open the fresh final_link
returned by Qriib. The SDK owns request signing and the embedded meeting
experience; your application owns its navigation, identity, and secure
credential delivery.
QriibMeetClient keeps room REST calls and the meeting experience together:
The identifiers and room settings below are application-owned values. Obtain them from authenticated application state or your backend; do not copy user data, credentials, or environment-specific values into source code.
Use the built-in newClientRoomId() for every create-room request. It creates
a cryptographically random UUID-style string and contains no user data,
credentials, or room metadata.
final qriib = QriibMeetClient.withProjectCredentials(
apiKey: const String.fromEnvironment('QRIIB_API_KEY'),
secretKey: const String.fromEnvironment('QRIIB_SECRET_KEY'),
enableNetworkLogging: false,
);
final response = await qriib.rooms.createQuickVideoRoom(
projectId: projectId,
moderatorId: moderatorId,
name: participantName,
maxParticipants: roomCapacity,
emptyTimeout: emptyRoomTimeoutSeconds,
clientRoomId: newClientRoomId(),
metadata: QriibRoomMetadata(
roomTitle: roomTitle,
welcomeMessage: welcomeMessage,
),
);
final payload = response['data'] is Map
? Map<String, dynamic>.from(response['data'] as Map)
: response;
final finalLink = payload['final_link'] as String;
await qriib.meetings.join(
context: context,
finalLink: finalLink,
userName: participantName,
configuration: meetingConfiguration,
);
Use the built-in openMeetingFromResponse to extract the response’s fresh
final_link and open the meeting. It throws QriibMeetException when the
link is absent and automatically disables video when audioOnly is true:
await openMeetingFromResponse(
context: context,
qriib: qriib,
response: response,
userName: participantName,
audioOnly: isAudioRoom,
configuration: meetingConfiguration,
);
For a room created with createQuickAudioRoom (or a scheduled audio room),
join with videoEnabled: false:
await qriib.meetings.join(
context: context,
finalLink: finalLink,
videoEnabled: false,
);
This requests microphone permission only and never starts the camera. The default video-off control remains visible but disabled, with an accessibility label and tooltip explaining that video is unavailable in an audio-only room.
QriibMeetingConfiguration is optional. Without it, the package displays its
premium default meeting view. The supplied builders receive Qriib session and
participant types only; consumers do not need to import the media engine.
Always obtain a fresh final_link before joining. Do not store or log it.
Meeting UI configuration #
Pass a QriibMeetingConfiguration to join or
openMeetingFromResponse to style the built-in meeting surface or replace
individual regions. All options are optional; omit a builder to retain that
part of the premium default UI.
final meetingConfiguration = QriibMeetingConfiguration(
title: meetingTitle,
theme: QriibMeetingTheme(
backgroundColor: meetingBackgroundColor,
surfaceColor: meetingSurfaceColor,
primaryColor: brandColor,
dangerColor: leaveButtonColor,
tileColor: participantTileColor,
participantNameStyle: participantNameTextStyle,
topBarTextStyle: topBarTextStyle,
cameraOnIcon: cameraOnIcon,
cameraOffIcon: cameraOffIcon,
microphoneOnIcon: microphoneOnIcon,
microphoneOffIcon: microphoneOffIcon,
leaveIcon: leaveIcon,
),
showTopBar: showTopBar,
showParticipantNames: showParticipantNames,
showCameraControl: showCameraControl,
showMicrophoneControl: showMicrophoneControl,
showLeaveControl: showLeaveControl,
videoAvailable: roomAllowsVideo,
onCameraChanged: (session, enabled) {
// Update app-owned UI or analytics without recording sensitive media data.
},
onMicrophoneChanged: (session, enabled) {
// Update app-owned UI or analytics without recording sensitive media data.
},
onLeaveRequested: (session) async {
// Return false to keep the participant in the meeting.
return await confirmLeaveWithUser();
},
);
Visibility and audio-only behavior #
showTopBar, showParticipantNames, showCameraControl,
showMicrophoneControl, and showLeaveControl hide default elements.
videoAvailable controls whether video is permitted. For audio-only rooms,
join with videoEnabled: false and keep videoAvailable: false: the package
renders a visible, disabled camera-off control with an accessible explanation,
instead of starting a camera or hiding the reason it is unavailable.
Replace individual regions #
The three builders receive neutral Qriib types and a QriibMeetingSession:
final meetingConfiguration = QriibMeetingConfiguration(
participantBuilder: (context, participant, video, session) {
// `video` is the SDK-provided renderer. Do not retain it when local video
// is disabled; listen to the neutral session state instead.
return ValueListenableBuilder<QriibMeetingState>(
valueListenable: session.state,
builder: (context, state, child) {
final localCameraOff = participant.isLocal && !state.cameraEnabled;
if (!localCameraOff) return video;
final initial = participant.label.isEmpty
? fallbackInitial
: participant.label.characters.first.toUpperCase();
return ColoredBox(
color: participantTileColor,
child: Center(child: CircleAvatar(child: Text(initial))),
);
},
);
},
topBarBuilder: (context, state, session) => MeetingHeader(
title: meetingTitle,
participantCount: state.participants.length,
),
controlsBuilder: (context, state, session) => MeetingControls(
cameraEnabled: state.cameraEnabled,
microphoneEnabled: state.microphoneEnabled,
onCameraPressed: () => session.setCameraEnabled(!state.cameraEnabled),
onMicrophonePressed: () =>
session.setMicrophoneEnabled(!state.microphoneEnabled),
onLeavePressed: session.leave,
),
);
When replacing controls, provide the same accessible labels, focus behavior, disabled video indication for audio-only rooms, and a way to leave. When replacing participant tiles, do not render a camera frame when the relevant camera state is off; show an initial/avatar on a fixed background instead.
Host permissions #
Add these declarations to the host app's
android/app/src/main/AndroidManifest.xml, directly under <manifest>:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
On iOS, add purpose strings to the host application's
ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>Use the camera to share video in a meeting.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Use the microphone to share audio in a meeting.</string>
permission_handler_apple also requires the iOS host's Podfile to enable
the camera and microphone permission macros. Add these definitions to every
Pods build configuration in the post_install hook:
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
'PERMISSION_CAMERA=1',
'PERMISSION_MICROPHONE=1',
]
The example app is a generated Android/iOS smoke host. It reads
QRIIB_API_KEY, QRIIB_SECRET_KEY, and a fresh QRIIB_FINAL_LINK from
compile-time environment definitions; it contains no credential or link
values. Supply them through your development or CI secret store when running
the example on a trusted device.
Transport and credentials #
qriib_meet owns the signed Qriib V4 transport. Applications do not create a
second HTTP client, manually set key or hash-signature, or depend on a
separate V.cloud package. Keep project API credentials and fresh final_link
values out of source control and application logs.
Room workflows #
The integration examples use a single QriibMeetClient for the lifetime of
the host widget, then call close() from dispose().
Quick rooms #
Supply a unique clientRoomId for each create request. Quick video rooms can
be opened immediately from the response:
final response = await qriib.rooms.createQuickVideoRoom(
projectId: projectId,
clientRoomId: clientRoomId,
moderatorId: moderatorId,
name: participantName,
maxParticipants: roomCapacity,
emptyTimeout: emptyRoomTimeoutSeconds,
metadata: QriibRoomMetadata(
roomTitle: roomTitle,
welcomeMessage: welcomeMessage,
),
);
final payload = response['data'] is Map
? Map<String, dynamic>.from(response['data'] as Map)
: response;
final finalLink = payload['final_link'] as String;
Use createQuickAudioRoom for an audio-only room, then join it with
videoEnabled: false as shown above.
Scheduled rooms #
Use createScheduledVideoRoom or createScheduledAudioRoom with the same
room fields plus an ISO-8601 startAt value. When it is time to open the
room, call:
final response = await qriib.rooms.startScheduledRoom(
roomId,
name: participantName,
);
Extract the fresh final_link from that response before joining. Set
videoEnabled: false only when starting an audio room.
Join and manage rooms #
await qriib.rooms.joinRoom(
roomId: roomId,
userInfo: const QriibUserInfo(
name: participantName,
role: 'attendee',
isAdmin: false,
isHidden: false,
),
);
The client also provides createInvitationLink, endRoom, getRoomStatus,
getActiveRoomInfo, getActiveRoomsInfo, and fetchPastRooms. Catch
QriibApiException and QriibMeetException to show their user-safe
message values.
Complete Rooms API map #
The bundled example implements every operation below. Inputs marked “room ID” must refer to the room returned by a previous create response.
| Operation | Method and required inputs | Result / next step |
|---|---|---|
| Quick video | createQuickVideoRoom(projectId, clientRoomId, metadata, name, moderatorId, maxParticipants, emptyTimeout) |
Extract final_link, then join with video enabled. |
| Quick audio | createQuickAudioRoom(...) with the same fields |
Extract final_link, then join with videoEnabled: false. |
| Scheduled video | createScheduledVideoRoom(projectId, clientRoomId, startAt, metadata, maxParticipants, emptyTimeout) |
Store the returned room ID and start it later. |
| Scheduled audio | createScheduledAudioRoom(...) with the same scheduled fields |
Start later, then join with videoEnabled: false. |
| Start scheduled | startScheduledRoom(roomId, name: participantName) |
Extract its fresh final_link; select audio/video to match the room type. |
| Join | joinRoom(roomId: roomId, userInfo: QriibUserInfo(...)) |
Extract its fresh final_link and open the meeting. |
| Invitation | createInvitationLink(roomId: roomId, role: QriibInvitationRole.attendee) |
Share the returned HTTP(S) link only with the intended participant. |
| Inspect active room | getActiveRoomInfo(roomId) |
Displays current active-room details. |
| Inspect all active rooms | getActiveRoomsInfo() |
Displays the active-room collection. |
| Status | getRoomStatus(roomId) |
Displays the current lifecycle status. |
| Past rooms | fetchPastRooms(projectId: projectId, from: offset, limit: pageSize, orderBy: QriibPastRoomsOrder.desc) |
Displays a paged, newest-first history. |
| End | endRoom(roomId) |
Ends an active room; confirm this action with the user before invoking it in production. |
final_link is short-lived meeting access data. Read it from either
response['data']['final_link'] or response['final_link'], use it
immediately, and do not persist it.
Run the complete example #
The example application is a full Rooms API playground. It covers quick and scheduled audio/video rooms, joining, invitation links, active and past room lookups, status, and ending rooms. It has no embedded secrets.
Run it on a trusted device with values supplied only at build time:
cd example
flutter run \
--dart-define=QRIIB_API_KEY=your-project-api-key \
--dart-define=QRIIB_SECRET_KEY=your-project-secret \
--dart-define=QRIIB_PROJECT_ID=your-project-id
The example displays API responses in an in-app dialog and automatically opens
the meeting when a create, join, or start response contains a final_link.
Treat all credentials and final links as sensitive: do not commit, log, or
send them through analytics.