jitsi_meet_mb 0.2.2

  • Readme
  • Changelog
  • Example
  • Installing
  • new59

jitsi_meet_mb #

Jitsi Meet Plugin for Flutter. Supports Android and iOS platforms.

"Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses Jitsi Videobridge to provide high quality, secure and scalable video conferences."

Find more information about Jitsi Meet here

Table of Contents #

Configuration #

Android #

Gradle

Set dependencies of build tools gradle to minimum 3.6.3:

dependencies {
    classpath 'com.android.tools.build:gradle:3.6.3' <!-- Upgrade this -->
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}

Set distribution gradle wrapper to minimum 5.6.4.

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip <!-- Upgrade this -->

Add Java 1.8 compatibility support to your project by adding the following lines into your build.gradle file:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

AndroidManifest.xml

Jitsi Meet's SDK AndroidManifest.xml will conflict with your project, namely the application:label field. To counter that, go into android/app/src/main/AndroidManifest.xml and add the tools library and tools:replace="android:label" to the application tag.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="yourpackage.com"
    xmlns:tools="http://schemas.android.com/tools"> <!-- Add this -->
    <application 
        tools:replace="android:label"  
        android:name="your.application.name"
        android:label="My Application"
        android:icon="@mipmap/ic_launcher">
        ...
    </application>
...
</manifest>

Minimum SDK Version 21

Update your minimum sdk version to 21 in android/app/build.gradle

defaultConfig {
    // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
    applicationId "com.harryluu.jitsi_meet_mb_example"
    minSdkVersion 21 //Required for Jitsi
    targetSdkVersion 28
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}

Proguard

Jitsi's SDK enables proguard, but without a proguard-rules.pro file, your release apk build will be missing the Flutter Wrapper as well as react-native code. In your Flutter project's android/app/build.gradle file, add proguard support

buildTypes {
    release {
        // TODO: Add your own signing config for the release build.
        // Signing with the debug keys for now, so `flutter run --release` works.
        signingConfig signingConfigs.debug
        
        // Add below 3 lines for proguard
        minifyEnabled true
        useProguard true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

Then add a file in the same directory called proguard-rules.pro. See the example app's proguard-rules.pro file to know what to paste in.

Note
If you do not create the proguard-rules.pro file, then your app will crash when you try to join a meeting or the meeting screen tries to open but closes immediately. You will see one of the below errors in logcat.

## App crashes ##
java.lang.RuntimeException: Parcel android.os.Parcel@8530c57: Unmarshalling unknown type code 7536745 at offset 104
    at android.os.Parcel.readValue(Parcel.java:2747)
    at android.os.Parcel.readSparseArrayInternal(Parcel.java:3118)
    at android.os.Parcel.readSparseArray(Parcel.java:2351)
    .....
## Meeting won't open and you go to previous screen ##
W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.BV.LinearGradient.LinearGradientManager
W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.uimanager.g
W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTGroupViewManager
W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.a
.....

Join A Meeting #

_joinMeeting() async {
    try {
      var options = JitsiMeetingOptions()
        ..room = "myroom" // Required, spaces will be trimmed
        ..serverURL = "https://someHost.com"
        ..subject = "Meeting with harryluu"
        ..userDisplayName = "My Name"
        ..userEmail = "myemail@email.com"
        ..audioOnly = true
        ..audioMuted = true
        ..videoMuted = true;

      await JitsiMeet.joinMeeting(options);
    } catch (error) {
      debugPrint("error: $error");
    }
  }

JitsiMeetingOptions #

FieldRequiredDefaultDescription
roomYesN/AUnique room name that will be appended to serverURL. Valid characters: alphanumeric, dashes, and underscores.
subjectNo$roomMeeting name displayed at the top of the meeting. If null, defaults to room name where dashes and underscores are replaced with spaces and first characters are capitalized.
userDisplayNameNo"Fellow Jitster"User's display name
userEmailNononeUser's email address
audioOnlyNofalseStart meeting without video. Can be turned on in meeting.
audioMutedNofalseStart meeting with audio muted. Can be turned on in meeting.
videoMutedNofalseStart meeting with video muted. Can be turned on in meeting.
serverURLNomeet.jitsi.siSpecify your own hosted server. Must be a valid absolute URL of the format <scheme>://<host>[/path], i.e. https://someHost.com. Defaults to Jitsi Meet's servers.
userAvatarURLN/AnoneNot yet implemented. User's avatar URL.
tokenN/AnoneNot yet implemented. JWT token used for authentication.
iosAppBarRGBAColorNo"00000000"only IOS, only RGBA values accepted
featureFlagsNosee belowMap of feature flags and their values (true/false), used to enable/disable features of the Jitsi Meet SDK

A new param has been added iosAppBarRGBAColor . This will change the AppBar color of the View in IOS only. Please note : the ioaAppBarRGBAColor param takes RGBA color format only .. for example Black - "00000000".

FeatureFlags

Feature flags allow you to enable or disable any feature of the Jitsi Meet SDK.
If you don't provide any flag to JitsiMeetingOptions, default values will be used.
If you don't provide a flag in featureFlags in JitsiMeetingOptions, its default value will be used.
We are using the official list of flags, taken from the Jitsi Meet repository

FlagDefault (Android)Default (iOS)Description
ADD_PEOPLE_ENABLEDtruetrueEnable the blue button "Add people", showing up when you are alone in a call. Requires flag INVITE_ENABLED to work.
CALENDAR_ENABLEDtrueautoEnable calendar integration.
CALL_INTEGRATION_ENABLEDtruetrueEnable call integration (CallKit on iOS, ConnectionService on Android). SEE REMARK BELOW
CLOSE_CAPTIONS_ENABLEDtruetrueEnable close captions (subtitles) option in menu.
CHAT_ENABLEDtruetrueEnable chat (button and feature).
INVITE_ENABLEDtruetrueEnable invite option in menu.
IOS_RECORDING_ENABLEDN/AfalseEnable recording in iOS.
LIVE_STREAMING_ENABLEDautoautoEnable live-streaming option in menu.
MEETING_NAME_ENABLEDtruetrueDisplay meeting name.
MEETING_PASSWORD_ENABLEDtruetrueDisplay meeting password option in menu (if a meeting has a password set, the dialog will still show up).
PIP_ENABLEDautoautoEnable Picture-in-Picture mode.
RAISE_HAND_ENABLEDtruetrueEnable raise hand option in menu.
RAISE_HAND_ENABLEDtruetrueEnable raise hand option in menu.
RECORDING_ENABLEDautoN/AEnable recording option in menu.
TILE_VIEW_ENABLEDtruetrueEnable tile view option in menu.
TILE_VIEW_ENABLEDtruetrueToolbox (buttons and menus) always visible during call (if not, a single tap displays it).
WELCOME_PAGE_ENABLEDfalsefalseEnable welcome page. "The welcome page lists recent meetings and calendar appointments and it's meant to be used by standalone applications."

REMARK about Call integration Call integration on Android (known as ConnectionService) has been disabled on the official Jitsi Meet app because it creates a lot of issues. You should disable it too to avoid these issues.

JitsiMeetingResponse #

FieldTypeDescription
isSuccessboolSuccess indicator.
messageStringSuccess message or error as a String.
errordynamicOptional, only exists if isSuccess is false. The error object.

Listening to Meeting Events #

Events supported

NameDescription
onConferenceWillJoinMeeting is loading.
onConferenceJoinedUser has joined meeting.
onConferenceTerminatedUser has exited the conference.
onErrorError has occurred with listening to meeting events.

Per Meeting Events

To listen to meeting events per meeting, pass in a JitsiMeetingListener in joinMeeting. The listener will automatically be removed when an
onConferenceTerminated event is fired.

await JitsiMeet.joinMeeting(options,
  listener: JitsiMeetingListener(onConferenceWillJoin: ({message}) {
    debugPrint("${options.room} will join with message: $message");
  }, onConferenceJoined: ({message}) {
    debugPrint("${options.room} joined with message: $message");
  }, onConferenceTerminated: ({message}) {
    debugPrint("${options.room} terminated with message: $message");
  }));

Global Meeting Events

To listen to global meeting events, simply add a JitsiMeetListener with
JitsiMeet.addListener(myListener). You can remove listeners using
JitsiMeet.removeListener(listener) or JitsiMeet.removeAllListeners().

@override
void initState() {
  super.initState();
  JitsiMeet.addListener(JitsiMeetingListener(
    onConferenceWillJoin: _onConferenceWillJoin,
    onConferenceJoined: _onConferenceJoined,
    onConferenceTerminated: _onConferenceTerminated,
    onError: _onError));
}

@override
void dispose() {
  super.dispose();
  JitsiMeet.removeAllListeners();
}

_onConferenceWillJoin({message}) {
  debugPrint("_onConferenceWillJoin broadcasted");
}

_onConferenceJoined({message}) {
  debugPrint("_onConferenceJoined broadcasted");
}

_onConferenceTerminated({message}) {
  debugPrint("_onConferenceTerminated broadcasted");
}

_onError(error) {
  debugPrint("_onError broadcasted");
}

Title bar #

When Jitsi Meet is opening, the title bar will reflect:

  • For Android: the android:label tag in the AndroidManifest.xml in
  • For iOS: the Bundle name in Info.plist For IOS - Support added for Xcode 11.4 Ensure in your Podfile you have an entry like below declaring platform of 11.0 or above. platform :ios, '11.0' Also in Info.plist add the following This will ensure the right permissions are configured in your project.

Contributing #

Send a pull request with as much information as possible clearly describing the issue or feature. Try to keep changes small and for one issue at a time.

0.2.1 #

  • Updated JitsiMeet SDK to v2.9.0

0.2.0 #

  • Added IOSAppBarRGBAColor Param

0.1.9 #

  • Upgrading to IOS Cocoapods JitsiMeet v2.8.1

0.1.9pod install #

  • Fix defects for Android and SDK version

0.1.8y #

  • Fix defect for ISO Xcode 11.4 compile architecture issues

0.1.7 #

  • Fix defect with per meeting listener not initializing event channel

0.1.6 #

  • Added per meeting listeners
  • Added Map<dynamic, dynamic> data returned in listener functions

0.1.5 #

  • Support for Meeting Events: onConferenceWillJoin, onConferenceJoined, onConferenceTerminated, and onError

0.1.4 #

  • Move and rename Kotlin TAG variable into plugin class to eliminate naming conflicts

0.1.3+1 #

  • Example app proguard to fix release apk build crashing
  • Update README about proguard and minimum sdk

0.1.3 #

  • Added support for serverURL

0.1.2 #

  • Update license to MIT

0.1.1 #

  • Room name character validation.
  • Allow nullable fields in iOS.

0.1.0 #

  • Initial release for Android and iOS.

example/lib/main.dart

import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:jitsi_meet_mb/jitsi_meet.dart';
import 'package:jitsi_meet_mb/jitsi_meeting_listener.dart';
import 'package:jitsi_meet_mb/room_name_constraint.dart';
import 'package:jitsi_meet_mb/room_name_constraint_type.dart';
import 'package:jitsi_meet_mb/feature_flag/feature_flag_enum.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final serverText = TextEditingController();
  final roomText = TextEditingController(text: "plugintestroom");
  final subjectText = TextEditingController(text: "My Plugin Test Meeting");
  final nameText = TextEditingController(text: "Plugin Test User");
  final emailText = TextEditingController(text: "fake@email.com");
  final iosAppBarRGBAColor = TextEditingController(text: "#0080FF80");//transparent blue
  var isAudioOnly = true;
  var isAudioMuted = true;
  var isVideoMuted = true;

  @override
  void initState() {
    super.initState();
    JitsiMeet.addListener(JitsiMeetingListener(
        onConferenceWillJoin: _onConferenceWillJoin,
        onConferenceJoined: _onConferenceJoined,
        onConferenceTerminated: _onConferenceTerminated,
        onError: _onError));
  }

  @override
  void dispose() {
    super.dispose();
    JitsiMeet.removeAllListeners();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Container(
          padding: const EdgeInsets.symmetric(
            horizontal: 16.0,
          ),
          child: SingleChildScrollView(
            child: Column(
              children: <Widget>[
                SizedBox(
                  height: 24.0,
                ),
                TextField(
                  controller: serverText,
                  decoration: InputDecoration(
                      border: OutlineInputBorder(),
                      labelText: "Server URL",
                      hintText: "Hint: Leave empty for meet.jitsi.si"),
                ),
                SizedBox(
                  height: 16.0,
                ),
                TextField(
                  controller: roomText,
                  decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: "Room",
                  ),
                ),
                SizedBox(
                  height: 16.0,
                ),
                TextField(
                  controller: subjectText,
                  decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: "Subject",
                  ),
                ),
                SizedBox(
                  height: 16.0,
                ),
                TextField(
                  controller: nameText,
                  decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: "Display Name",
                  ),
                ),
                SizedBox(
                  height: 16.0,
                ),
                TextField(
                  controller: emailText,
                  decoration: InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: "Email",
                  ),
                ),
                SizedBox(
                  height: 16.0,
                ),
                TextField(
                  controller: iosAppBarRGBAColor,
                  decoration: InputDecoration(
                      border: OutlineInputBorder(),
                      labelText: "AppBar Color(IOS only)",
                      hintText: "Hint: This HAS to be in HEX RGBA format"),
                ),
                SizedBox(
                  height: 16.0,
                ),
                CheckboxListTile(
                  title: Text("Audio Only"),
                  value: isAudioOnly,
                  onChanged: _onAudioOnlyChanged,
                ),
                SizedBox(
                  height: 16.0,
                ),
                CheckboxListTile(
                  title: Text("Audio Muted"),
                  value: isAudioMuted,
                  onChanged: _onAudioMutedChanged,
                ),
                SizedBox(
                  height: 16.0,
                ),
                CheckboxListTile(
                  title: Text("Video Muted"),
                  value: isVideoMuted,
                  onChanged: _onVideoMutedChanged,
                ),
                Divider(
                  height: 48.0,
                  thickness: 2.0,
                ),
                SizedBox(
                  height: 64.0,
                  width: double.maxFinite,
                  child: RaisedButton(
                    onPressed: () {
                      _joinMeeting();
                    },
                    child: Text(
                      "Join Meeting",
                      style: TextStyle(color: Colors.white),
                    ),
                    color: Colors.blue,
                  ),
                ),
                SizedBox(
                  height: 48.0,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  _onAudioOnlyChanged(bool value) {
    setState(() {
      isAudioOnly = value;
    });
  }

  _onAudioMutedChanged(bool value) {
    setState(() {
      isAudioMuted = value;
    });
  }

  _onVideoMutedChanged(bool value) {
    setState(() {
      isVideoMuted = value;
    });
  }

  _joinMeeting() async {
    String serverUrl =
        serverText.text?.trim()?.isEmpty ?? "" ? null : serverText.text;

    try {

      // Enable or disable any feature flag here
      // If feature flag are not provided, default values will be used
      // Full list of feature flags (and defaults) available in the README
      Map<FeatureFlagEnum, bool> featureFlags =
      {
        FeatureFlagEnum.WELCOME_PAGE_ENABLED : false,
      };

      // Here is an example, disabling features for each platform
      if (Platform.isAndroid)
      {
        // Disable ConnectionService usage on Android to avoid issues (see README)
        featureFlags[FeatureFlagEnum.CALL_INTEGRATION_ENABLED] = false;
      }
      else if (Platform.isIOS)
      {
        // Disable PIP on iOS as it looks weird
        featureFlags[FeatureFlagEnum.PIP_ENABLED] = false;
      }

      // Define meetings options here
      var options = JitsiMeetingOptions()
        ..room = roomText.text
        ..serverURL = serverUrl
        ..subject = subjectText.text
        ..userDisplayName = nameText.text
        ..userEmail = emailText.text
        ..iosAppBarRGBAColor = iosAppBarRGBAColor.text
        ..audioOnly = isAudioOnly
        ..audioMuted = isAudioMuted
        ..videoMuted = isVideoMuted
        ..featureFlags.addAll(featureFlags);

      debugPrint("JitsiMeetingOptions: $options");
      await JitsiMeet.joinMeeting(options,
          listener: JitsiMeetingListener(onConferenceWillJoin: ({message}) {
            debugPrint("${options.room} will join with message: $message");
          }, onConferenceJoined: ({message}) {
            debugPrint("${options.room} joined with message: $message");
          }, onConferenceTerminated: ({message}) {
            debugPrint("${options.room} terminated with message: $message");
          }),
          // by default, plugin default constraints are used
          //roomNameConstraints: new Map(), // to disable all constraints
          //roomNameConstraints: customContraints, // to use your own constraint(s)
      );
    } catch (error) {
      debugPrint("error: $error");
    }
  }

  static final Map<RoomNameConstraintType, RoomNameConstraint> customContraints =
  {
    RoomNameConstraintType.MAX_LENGTH : new RoomNameConstraint(
            (value) { return value.trim().length <= 50; },
            "Maximum room name length should be 30."),

    RoomNameConstraintType.FORBIDDEN_CHARS : new RoomNameConstraint(
            (value) { return RegExp(r"[$€£]+", caseSensitive: false, multiLine: false).hasMatch(value) == false; },
            "Currencies characters aren't allowed in room names."),
  };

  void _onConferenceWillJoin({message}) {
    debugPrint("_onConferenceWillJoin broadcasted with message: $message");
  }

  void _onConferenceJoined({message}) {
    debugPrint("_onConferenceJoined broadcasted with message: $message");
  }

  void _onConferenceTerminated({message}) {
    debugPrint("_onConferenceTerminated broadcasted with message: $message");
  }

  _onError(error) {
    debugPrint("_onError broadcasted: $error");
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  jitsi_meet_mb: ^0.2.2

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:jitsi_meet_mb/feature_flag/feature_flag_enum.dart';
import 'package:jitsi_meet_mb/feature_flag/feature_flag_helper.dart';
import 'package:jitsi_meet_mb/jitsi_meet.dart';
import 'package:jitsi_meet_mb/jitsi_meeting_listener.dart';
import 'package:jitsi_meet_mb/room_name_constraint.dart';
import 'package:jitsi_meet_mb/room_name_constraint_type.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
34
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
60
Overall:
Weighted score of the above. [more]
59
Learn more about scoring.

We analyzed this package on Jul 14, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.15
  • Flutter: 1.17.5

Analysis suggestions

Package does not support Flutter platform Linux

Because:

  • package:jitsi_meet_mb/feature_flag/feature_flag_enum.dart that declares support for platforms: Android, iOS

Package does not support Flutter platform Web

Because:

  • package:jitsi_meet_mb/feature_flag/feature_flag_enum.dart that declares support for platforms: Android, iOS

Package does not support Flutter platform Windows

Because:

  • package:jitsi_meet_mb/feature_flag/feature_flag_enum.dart that declares support for platforms: Android, iOS

Package does not support Flutter platform macOS

Because:

  • package:jitsi_meet_mb/feature_flag/feature_flag_enum.dart that declares support for platforms: Android, iOS

Package not compatible with SDK dart

Because:

  • jitsi_meet_mb that is a package requiring null.

Health suggestions

Format lib/feature_flag/feature_flag_enum.dart.

Run flutter format to format lib/feature_flag/feature_flag_enum.dart.

Format lib/feature_flag/feature_flag_helper.dart.

Run flutter format to format lib/feature_flag/feature_flag_helper.dart.

Format lib/jitsi_meet.dart.

Run flutter format to format lib/jitsi_meet.dart.

Format lib/room_name_constraint.dart.

Run flutter format to format lib/room_name_constraint.dart.

Format lib/room_name_constraint_type.dart.

Run flutter format to format lib/room_name_constraint_type.dart.

Maintenance issues and suggestions

Homepage URL doesn't exist. (-20 points)

At the time of the analysis the homepage field https://github.com/harryluu/jitsi_meet_mb was unreachable.

The package description is too short. (-20 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8 1.2.2
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
flutter_test