Skip to main content

qriib_meet 0.4.0 copy "qriib_meet: ^0.4.0" to clipboard
qriib_meet: ^0.4.0 copied to clipboard

Flutter SDK for Qriib room management and embedded audio/video meetings, with customizable meeting controls, pre-join preview and screen sharing.

qriib_meet #

Flutter SDK from Qriib for Qriib room management and embedded audio/video meetings on Android and iOS. It includes a pre-join preview, customizable meeting UI, audio routing, camera switching, and screen sharing. No public chat API is exported. Desktop and web are not supported host platforms, even where internal media code or device-capability types mention them.

Version and installation #

These examples describe version 0.3.2. The published 0.2.1 archive has a different API: it includes V4 recording retrieval methods (getRecord, getRecordingLink, getRecords), but not the V5 recording start/stop or pre-join APIs described here. Its join API also differs. Check the API for the version you install.

Add the package to your Flutter application, create a single client from trusted credentials. The SDK owns request signing and the embedded meeting experience; your application owns its navigation, identity, and secure credential delivery.

Requirements: Dart >=3.6.0 <4.0.0 and Flutter >=3.29.0. The plugin declares Android minSdk 21 and iOS 13.0; dependencies and your host's toolchain can impose additional requirements. Use a physical device to validate media and routing.

For version 0.3.2, add:

dependencies:
  qriib_meet: ^0.3.2

If 0.3.2 is not yet published, use an approved local or source dependency until it is available. Consult the API reference for the version you install.

Project secrets embedded in a distributed app can be extracted. Dart-defines and ignored local files do not make them confidential. Prefer room management on a trusted backend and fresh links for client-side joining. The credential-based example below is for integrations that explicitly accept that security boundary.

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 finalLink = finalLinkFromResponse(response);
if (finalLink == null) {
  throw const QriibMeetException('The response has no final_link to open.');
}

if (!context.mounted) return;
await qriib.meetings.join(
  context: context,
  finalLink: finalLink,
  configuration: meetingConfiguration,
);

Use the built-in finalLinkFromResponse to read the fresh final_link out of any create, join, or start response. It accepts both the response['data']['final_link'] envelope and a top-level final_link, and returns null when no usable link is present. If data is a map, that envelope takes precedence; the helper does not fall back to the outer link when the nested link is absent.

Run flutter pub get, then import only the public entry point:

import 'package:flutter/material.dart';
import 'package:qriib_meet/qriib_meet.dart';

Choose where credentials live #

Recommended for distributed apps: your authenticated backend owns the project signing secret, calls the Qriib room API, and returns a fresh final_link to the intended participant. The Flutter app can join it with QriibMeetings() without project credentials. Built-in recording controls are not available in that credential-free flow.

A fuller walkthrough of the layout, every flag, and the widget-test keys is in doc/meeting-ui.md.

The client also supports direct signed room requests, useful for trusted integrations and development. Treat that as an explicit credential-exposure choice. --dart-define, environment files, obfuscation, and CI secret injection do not protect a secret compiled into a distributed app. The SDK's credential objects retain values for signing; they are not a secret store.

Never commit, persist unnecessarily, log, or send credentials, final_link, connection tokens, or tool tokens to analytics. Examples below take sensitive values as explicit inputs; none contains usable credentials.

Configure the host #

Android #

The plugin manifest merges INTERNET, CAMERA, RECORD_AUDIO, screen-capture foreground-service permissions, and QriibScreenShareService into the host. Ensure your merged manifest retains them. Explicit camera/microphone declarations in android/app/src/main/AndroidManifest.xml may also be used:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

No additional screen-share service declaration is normally needed. See screen sharing for Android consent ordering.

On iOS, screen sharing without a Broadcast Upload Extension captures only this app's own screen (ReplayKit in-app recording). Sharing other apps or the home screen requires a broadcast extension, which this release does not yet wire up; see the iOS notes in doc/screen-sharing.md.

iOS #

Add purpose strings to 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>

Enable the permission-handler macros in your existing Podfile post_install hook. Merge this into the hook rather than adding a second one; retain existing build settings and definitions:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
      definitions = config.build_settings['GCC_PREPROCESSOR_DEFINITIONS']
      definitions = definitions.nil? ? ['$(inherited)'] : Array(definitions)
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] =
        (definitions + ['PERMISSION_CAMERA=1', 'PERMISSION_MICROPHONE=1']).uniq
    end
  end
end

Then install/update the host's pods as usual. iOS screen sharing is in-app only through this public API; a Broadcast Upload Extension is not wired up.

This widget accepts your app's authenticated backend function. That function must obtain a new link for this user, not return a cached invitation URL. Place the widget under a MaterialApp/Navigator and Scaffold.

class JoinMeetingButton extends StatefulWidget {
  const JoinMeetingButton({super.key, required this.fetchFreshFinalLink});

  final Future<String> Function() fetchFreshFinalLink;

  @override
  State<JoinMeetingButton> createState() => _JoinMeetingButtonState();
}

class _JoinMeetingButtonState extends State<JoinMeetingButton> {
  final _meetings = QriibMeetings();
  bool _joining = false;

  Future<void> _join() async {
    if (_joining) return;
    setState(() => _joining = true);
    try {
      final link = await widget.fetchFreshFinalLink();
      if (!mounted) return;
      await _meetings.join(
        context: context,
        finalLink: link,
        configuration: const QriibMeetingConfiguration(
          title: 'Team meeting',
          preJoin: QriibPreJoinConfiguration(enabled: true),
        ),
      );
    } catch (_) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Unable to join. Please try again.')),
      );
    } finally {
      if (mounted) setState(() => _joining = false);
    }
  }

  @override
  Widget build(BuildContext context) => FilledButton(
        onPressed: _joining ? null : _join,
        child: Text(_joining ? 'Joining…' : 'Join meeting'),
      );
}

join accepts context, finalLink, optional configuration, userName, userImage, onDisconnected, and (unreleased) roomId. userName and userImage are reserved: they currently do not set the participant's identity or avatar. Establish identity through room creation/join data on the server. roomId overrides the link's room ID for recording; it does not select a different media room.

Important behavior:

  • Both camera and microphone permissions are requested on every join, including audio-only rooms and joins with hidden camera controls.
  • Without pre-join, camera and microphone start enabled. Pre-join starts a local preview and forwards only the chosen camera/microphone booleans. Selected device IDs, speaker routing, and camera selection are not explicitly forwarded to the new meeting session. Cancelling preview aborts connection.
  • videoAvailable: false changes the default tile presentation. It does not disable capture, publishing, or permissions. Use session media controls to disable capture; for an initially camera-off join, let the user turn it off in pre-join. The preview itself initially enables the camera.
  • await join(...) completes after the meeting route closes (or an early return such as preview cancellation). onDisconnected runs after the meeting route returns, not as a general transport-disconnection event, and does not run on preview cancellation.

Direct client and room workflows #

Keep one client for the owning widget/service lifetime; do not create it in build. Close it after its requests and meetings no longer need its services. close() releases HTTP resources, not an active meeting. A State that owns an initialized client can call _client.close() in its dispose() before super.dispose(); keep that owner alive while its meeting route uses recording.

QriibMeetClient createClient({
  required String apiKey,
  required String secretKey,
}) => QriibMeetClient.withProjectCredentials(
      apiKey: apiKey,
      secretKey: secretKey,
      baseUrl: 'https://api.qriib.dev/api/v4',
      enableNetworkLogging: false,
    );

QriibMeetClient createScopedClient({
  required QriibProjectCredentials project,
  QriibOrganizationCredentials? organization,
}) => QriibMeetClient.withCredentials(
      project: project,
      organization: organization,
      enableNetworkLogging: false,
    );

QriibProjectCredentials and QriibOrganizationCredentials each take required apiKey and secretKey strings. withCredentials requires at least one scope. All exported room operations require project credentials; organization-only credentials do not authorize them or enable recording. Network logging defaults to true; disable it explicitly in sensitive integrations. Body/header logging is disabled by the client's default logger, but this is not a general redaction guarantee.

Create and open a quick room #

Future<void> createAndOpenRoom({
  required BuildContext context,
  required QriibMeetClient client,
  required String projectId,
  required String moderatorId,
  required String participantName,
}) async {
  final response = await client.rooms.createQuickVideoRoom(
    projectId: projectId,
    clientRoomId: newClientRoomId(),
    moderatorId: moderatorId,
    name: participantName,
    maxParticipants: 8,
    emptyTimeout: 300,
    metadata: const QriibRoomMetadata(
      roomTitle: 'Team meeting',
      welcomeMessage: 'Welcome',
    ),
  );
  final link = finalLinkFromResponse(response);
  if (link == null) {
    throw const QriibMeetException('The response has no final_link.');
  }
  if (!context.mounted) return;
  await client.meetings.join(context: context, finalLink: link);
}

newClientRoomId() generates a cryptographically random UUID-v4-style string; use a new value for each new room. finalLinkFromResponse trims a nonempty string link. If data is a map it uses only that map, even when its link is missing/empty and a top-level link exists. Otherwise it checks the top level. It does not validate the link or check expiration; join does that later.

Scheduled rooms #

Use createScheduledVideoRoom or createScheduledAudioRoom with a startAt string in the format required by your backend. The SDK forwards it unchanged without parsing or validating the schedule. Call startScheduledRoom when it is time to open the room.

Complete Rooms API #

Every method returns Future<Map<String, dynamic>>; there is no typed room response model. Read IDs from the response shape your backend returns. Do not confuse a server room ID with your generated clientRoomId.

The following function demonstrates all 12 operations as independently callable actions; invoke only the action intended by the user. All identifiers and dates are explicit inputs, and end must be confirmed by your app before invocation.

Future<Map<String, dynamic>> roomAction({
  required QriibRooms rooms,
  required String action,
  required String projectId,
  required String roomId,
  required String participantName,
  required String moderatorId,
  required DateTime scheduledStart,
}) {
  const metadata = QriibRoomMetadata(roomTitle: 'Team meeting');
  return switch (action) {
    'quick-video' => rooms.createQuickVideoRoom(
        projectId: projectId, clientRoomId: newClientRoomId(),
        metadata: metadata, name: participantName, moderatorId: moderatorId,
        maxParticipants: 8, emptyTimeout: 300),
    'quick-audio' => rooms.createQuickAudioRoom(
        projectId: projectId, clientRoomId: newClientRoomId(),
        metadata: metadata, name: participantName, moderatorId: moderatorId,
        maxParticipants: 8, emptyTimeout: 300),
    'schedule-video' => rooms.createScheduledVideoRoom(
        projectId: projectId, clientRoomId: newClientRoomId(),
        startAt: scheduledStart.toUtc().toIso8601String(), metadata: metadata,
        maxParticipants: 8, emptyTimeout: 300),
    'schedule-audio' => rooms.createScheduledAudioRoom(
        projectId: projectId, clientRoomId: newClientRoomId(),
        startAt: scheduledStart.toUtc().toIso8601String(), metadata: metadata,
        maxParticipants: 8, emptyTimeout: 300),
    'start' => rooms.startScheduledRoom(roomId, name: participantName),
    'join' => rooms.joinRoom(
        roomId: roomId,
        userInfo: QriibUserInfo(name: participantName, role: 'attendee',
            isAdmin: false, isHidden: false)),
    'invite' => rooms.createInvitationLink(
        roomId: roomId, role: QriibInvitationRole.attendee),
    'status' => rooms.getRoomStatus(roomId),
    'active-room' => rooms.getActiveRoomInfo(roomId),
    'active-rooms' => rooms.getActiveRoomsInfo(),
    'past' => rooms.fetchPastRooms(
        projectId: projectId, roomIds: [roomId], from: 0, limit: 20,
        orderBy: QriibPastRoomsOrder.desc),
    'end' => rooms.endRoom(roomId),
    _ => throw ArgumentError.value(action, 'action', 'Unknown room action'),
  };
}
  • Quick methods require projectId, clientRoomId, and metadata; their name, moderatorId, maxParticipants, and emptyTimeout are optional.
  • Scheduled methods require those three fields plus string startAt, with optional maxParticipants/emptyTimeout; they have no name/moderator inputs. Call startScheduledRoom when ready, then use its fresh link.
  • QriibRoomMetadata requires roomTitle; welcomeMessage and integer roomDuration are optional. toJson() nests duration under room_features.
  • QriibUserInfo requires name; role, isAdmin, and isHidden are optional. toJson() nests role under user_metadata. The server enforces authorization.
  • Invitation roles are admin and attendee. An invitation link is not a substitute for the fresh meeting final_link required by join.
  • fetchPastRooms requires only projectId; roomIds, from, limit, and orderBy (asc/desc) are optional.

Read fresh links from quick-create, join, and scheduled-start responses and use them immediately. For audio rooms choose an appropriate UI configuration, but remember that videoAvailable: false is not a media restriction.

Meeting UI, sessions, and recording #

  • Speaker view and gallery view. Swipe horizontally (or use the top-bar toggle) to move between a single large stage and an equal grid. initialViewMode picks the opening layout. enableViewModeSwipe: false disables swiping; the optional view-mode button can still switch layouts. In speaker view the stage shows an active screen share first, then a pinned participant (long-press any tile to pin, long-press again to unpin), then an active remote speaker, the last remote speaker still present, or the first remote participant. If there are no remotes, the first participant is used.
  • Draggable self-view. In speaker view your own camera floats in a small thumbnail that can be dragged anywhere and snaps to the nearest corner.
  • Screen sharing. session.setScreenShareEnabled(true) (or the share control) publishes the screen. The sharer is spotlighted for everyone; in gallery view the other participants collapse into a filmstrip beneath it. On Android the package asks for the screen-capture consent, then starts the required mediaProjection foreground service, then begins capturing — the order Android 14+ enforces — and declares the permissions in its own manifest, so host apps need no extra setup.
  • Front/back camera. session.switchCamera() flips lenses; the default control appears only where QriibMeetingState.canSwitchCameraPosition is true (mobile) and the camera is on.
  • Live tile badges. Every tile highlights the active speaker with speakingColor, shows a muted-mic badge, an initial avatar when the camera is off, and a warning glyph when connectionQuality is poor or lost. QriibMeetingParticipant exposes isMicrophoneEnabled, isCameraEnabled, isSpeaking, isScreenSharing and connectionQuality for custom tiles.
  • Auto-hiding chrome. The top bar and controls slide away after controlsAutoHideDelay (5 s by default) and return on tap. Pass null to disable automatic hiding. Tapping the stage can still hide/show them manually.
  • Meeting timer, participants sheet, leave confirmation. The top bar shows elapsed time since the view opened (showMeetingTimer), not server room duration. The bottom-bar Participants button opens the participant sheet (showParticipantsList). Leaving asks for confirmation (confirmLeave) unless you supply your own onLeaveRequested.

Pre-join screen #

The lobby screen is opt-in. Enable it to let participants confirm their camera, microphone, and audio devices before the meeting connects:

final meetingConfiguration = QriibMeetingConfiguration(
  preJoin: QriibPreJoinConfiguration(
    enabled: true,
    title: meetingTitle,
    showDeviceSelector: true,
  ),
);

The camera and microphone states chosen in the lobby are carried into the meeting. Selected device IDs and the speaker-route setting are not explicitly passed into the new meeting connection; do not rely on preview device selection being preserved. Cancelling the lobby aborts the join without connecting.

Session lifecycle and callbacks #

  • userName and userImage on join are reserved; establish participant identity through room creation or join data.
  • join waits for the meeting route to finish. The view automatically exits and invokes onDisconnected when the room is terminated by the server, duration expires, or when the participant leaves; it does not run when pre-join is cancelled.
  • The default meeting view calls session.leave() on disposal. A custom Leave button must also close its route; calling session.leave() alone disconnects the session but does not pop navigation.
  • Camera/microphone/screen-share callbacks are invoked by successful default control actions, not every programmatic change. A screen-share request can finish normally after consent is declined; inspect session.state for the actual sharing state rather than treating the callback argument as proof.
  • onLeaveRequested and confirmLeave guard the default Leave action, not all navigation/disposal paths. Custom UI must implement its own navigation policy.

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.

Audio device configuration #

Audio controls depend on capabilities reported by the session. On the declared Android/iOS platforms, use the speaker-route control when available. The underlying engine also has device-list branches; these are not a declaration of desktop/web support for this package.

The built-in audio-route control #

The default meeting view ships an audio-route button at the left of its top bar (next to the flip-camera button; it drops into the bottom bar when showTopBar is false). Tapping it opens a picker whose contents depend on what the platform reports:

  • Mobile — a single Speaker switch that routes audio between the earpiece and the loudspeaker. Wired headsets and Bluetooth earbuds still take priority while they are connected. The button icon follows the current route.
  • Desktop and web — a Microphone list and a Speaker list, each showing the OS-provided device names with the active device marked. Selecting an entry switches to it immediately.

The button only renders when the platform reports at least one of these capabilities, so nothing appears on a device with a single fixed route. Set QriibMeetingConfiguration(showAudioDeviceControl: false) to hide it and build your own picker instead.

The opt-in pre-join lobby offers the same choices before connecting: a speaker toggle on mobile, and microphone/speaker chips on desktop and web when QriibPreJoinConfiguration(showDeviceSelector: true). Only the camera/microphone enabled flags are explicitly forwarded from the lobby to the meeting, not device IDs or the speaker-route setting.

Driving it yourself #

QriibMeetingSession (and QriibPreJoinSession before joining) exposes the same state and operations to your own builders:

final state = session.state.value;

// Mobile: toggle the speaker on/off; wired/Bluetooth devices still take
// priority when connected.
await session.setSpeakerphoneOn(!state.speakerOn);

// Desktop/web: target a specific device by id.
if (state.canSelectSpecificDevice && state.audioInputs.isNotEmpty) {
  await session.selectAudioInput(state.audioInputs.first);
}
if (state.canSelectSpecificDevice && state.audioOutputs.isNotEmpty) {
  await session.selectAudioOutput(state.audioOutputs.first);
}

state.audioInputs and state.audioOutputs are lists of QriibAudioDevice (id, label, and a kind of QriibAudioDeviceKind.input or .output); state.selectedAudioInputId and state.selectedAudioOutputId identify the active ones. Check state.canToggleSpeakerphone (mobile) or state.canSelectSpecificDevice (desktop/web) to decide which control to show — rendering a device list on a platform that only supports a speaker toggle leaves the user with a picker that cannot change anything.

const meetingConfiguration = QriibMeetingConfiguration(
  title: 'Team meeting',
  theme: QriibMeetingTheme(primaryColor: Color(0xFF00695C)),
  texts: QriibMeetingTexts(
    mute: 'Mute microphone',
    record: 'Start recording',
    stopRecording: 'Stop recording',
    recordingError: 'Unable to confirm the request. Check before retrying.',
  ),
  showViewModeToggle: true,
  initialViewMode: QriibMeetingViewMode.gallery,
  controlsAutoHideDelay: null,
  preJoin: QriibPreJoinConfiguration(enabled: true, title: 'Ready to join?'),
);

controlsAutoHideDelay: null disables the timer, not manual tap-to-hide. Disabling swipes does not disable an enabled view-mode toggle. Visibility flags control default UI, not permissions, server capabilities, or your replacement builders.

The client also provides createInvitationLink, endRoom, getRoomStatus, getActiveRoomInfo, getActiveRoomsInfo, and fetchPastRooms. Catch QriibApiException and QriibMeetException and display application-owned messages. Exception messages and raw responses can include server-provided content; they are not automatically safe to display or log.

See meeting UI and public session reference for every flag, theme field, builder, callback, audio-device operation, and view lifecycle. See screen sharing for Android/iOS behavior.

Unreleased: server-side recording requires project credentials, an outer tool_token in the fresh link, and a room ID. The SDK layout enum serializes speaker or gallery; it has no grid value. Backend acceptance is separate: do not assume gallery aliases grid. state.recordingStatus reports the media room's observed active/inactive state or unknown; the legacy isRecording is only local command state. After a reconnect, the room value can be cached until a fresh update arrives. A successful HTTP request is not the same as room confirmation, and neither indicates that a recording file is ready. Use a base URL ending in /api/v4; supplying /api/v5 currently duplicates the recording path.

Tool-token renewal is disabled by default. Opt in with QriibMeetingConfiguration(trustedToolTokenRenewalEndpoint: Uri.parse('wss://trusted.example/socket')). Only use an endpoint trusted with credentials. Renewal is best effort; actual backend compatibility has not been verified. See the recording guide for prerequisites and security limitations.

Errors and cleanup #

Room requests map HTTP 400/422 to QriibValidationException, 401/403 to QriibAuthenticationException, 404 to QriibNotFoundException, and 429 to QriibRateLimitException; all extend QriibApiException. Other room failures and recording HTTP failures use the base exception. Its message, nullable statusCode, and response can contain server/transport details: do not assume they are safe to display or log verbatim.

QriibMeetException covers meeting/link errors; QriibSessionException and QriibNetworkException extend it. Catch errors at your async UI boundary, present an app-owned message, and retain only sanitized diagnostics. There is no blanket automatic retry policy. In particular, do not blindly repeat room creation or recording commands after an ambiguous timeout.

Check mounted/context.mounted after awaits before navigation or state updates. The built-in meeting view leaves its session when disposed; pre-join sessions created by join are disposed by that flow. Custom session calls do not invoke the default UI callbacks or pop routes automatically. A custom leave control must await session.leave() and then pop its route if still mounted. Do not retain sessions or their listenables after leave/disposal.

More documentation #

  • Meeting UI and customization
  • Screen sharing
  • Recording (unreleased)
  • Troubleshooting, including Android surface callback compilation errors
  • Minimal compilable example
  • Example playground
  • Backend-issued-link widget
  • Package quality and release checklist
  • Release history
  • Published SDK documentation

Historical design records under specs/ and docs/superpowers/ are repository-only material, excluded from the published package. They are not a promise of current public API support.

2
likes
60
points
902
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Flutter SDK for Qriib room management and embedded audio/video meetings, with customizable meeting controls, pre-join preview and screen sharing.

Homepage
Repository

Topics

#video-conferencing #webrtc #meetings #screen-sharing

License

Apache-2.0 (license)

Dependencies

async, collection, connectivity_plus, crypto, dart_jsonwebtoken, dart_webrtc, device_info_plus, dio, fixnum, flutter, flutter_webrtc, http, json_annotation, logging, meta, mime_type, path, permission_handler, pretty_dio_logger, protobuf, sdp_transform, synchronized, uuid, web

More

Packages that depend on qriib_meet

Packages that implement qriib_meet