Quickstart

The Flutter Video for Stream Video is a highly customizable SDK that facilitates adding calling (audio and video) support to your apps.

The SDK consists of four parts:

  • Low-level client (stream_video): Responsible for establishing calls, built on top of WebRTC.
  • UI SDK (stream_video_flutter): Flutter widgets for different types of call flows.
  • VoIP SDK (stream_video_push_notification): Adds native calling interface support for Android and iOS (CallKit).
  • Screen sharing SDK (stream_video_screen_sharing): Includes a native Swift implementation of BroadcastSampleHandler, essential for the broadcast screen sharing mode on iOS.

In this guide, we will build a video calling app that shows how you can integrate the SDK in few simple steps.

If you haven’t already, we recommend starting with the introduction and installation steps first, as this guide will build on the material covered in those sections.

Client Setup

Before we can create a new video client, we must first import the Stream Video package into our application.

import 'package:stream_video_flutter/stream_video_flutter.dart';

Next, in our application’s main function, let’s add the following:

Future<void> main() async {
  // Ensure Flutter is able to communicate with Plugins
  WidgetsFlutterBinding.ensureInitialized();

	// Initialize Stream video and set the API key along with the user for our app.
  final client = StreamVideo(
    'REPLACE_WITH_API_KEY',
     user: User.regular(userId: 'REPLACE_WITH_USER_ID', name: 'Test User'),
     userToken: 'REPLACE_WITH_TOKEN',
  );

  // Set up our call object and pass it the call type and ID. The most common call type is `default`, which enables full audio and video transmission
  final call = client.makeCall(callType: StreamCallType.defaultType(), id: 'REPLACE_WITH_CALL_ID');
  // Connect to the call we created
  await call.join();

  runApp(
    DemoAppHome(
      call: call,
    ),
  );
}

In the code above, we are performing a few key steps:

  1. Initializing our SDK with the API key for our application.
  2. Defining the user we would like to connect as to participate in a call.
  3. Creating an object for our call, giving it the “default” call type and unique ID.
  4. Connecting to the call we defined.

Although it is not shown in the example above, users can choose to customize many different aspects of the SDK during initialization. Here are a few of the parameters which can be overridden during initialization:

ParameterDescription
apiKeyStream Video API key obtained from the Dashboard of your project.
latencySettingsControls the number of rounds and timeout duration for measuring latency when first connecting to a call.
retryPolicyAllows for custom handling of retries such as override the default number of max retries, passing in a custom backoff function and more.
sdpPolicyControls whether SDP Munging is enabled and gives access to functions supporting SDP Munging.
logPriorityAllows for customizing the level of logs displayed while developing or running your application.
logHandlerFunctionCan be overridden to intercept logs and perform custom actions such as sending device logs to a capture service.
muteVideoWhenInBackgroundIndicates whether the SDK should disable the video track when the app moves from the foreground to background. We highly recommend you set this to true if your app does not support picture-in-picture mode.
muteAudioWhenInBackgroundIndicates whether the SDK should disable the audio track when the app moves from the foreground to background. We highly recommend you set this to true if your app does not support picture-in-picture mode.

Call UI

Stream ships with many pre-made components to make building your call UI as simple as possible. All of our UI components are designed to be flexible and customizable, meaning as a developer, you can control exactly how much (or how little) of Stream’s stock components you would like to have in your app.

With our Call object defined, let’s create the UI for our application. Since this is meant to be a quick-start guide, we can simply pass down the call defined earlier to our UI widget, but in a production scenario, you can define these calls in a repository or any other state layer of your choice.

class DemoAppHome extends StatelessWidget {
  const DemoAppHome({Key? key, required this.call}) : super(key: key);

  final Call call;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamCallContainer( // Stream's pre-made component
        call: call,
      ),
    );
  }
}

Using our default StreamCallContainer along with the call created earlier, we can compile our sample application and examine the result.

Completed application

In just a few lines of code, we can have a fully functional call screen in our application. Out of the box, StreamCallContainer ships with an AppBar, Participant view, and Call Controls. These components can be themed to fit your app’s style or be overridden entirely using a builder to allow for custom UIs and interactions.

Taking it one step further

For fun, let’s take a look at customizing the UI of our application to include a custom icon in the control area.

Call control area

Looking at our current UI code, we can make use of Flutter’s composition and builder pattern to override the default UI with our own.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamCallContainer(
        call: call,
        callContentBuilder: (context, call, callState) {
        },
      ),
    );
  }

StreamCallContainer ships with many different builders that can be implemented to override the default UI and behavior. Since we are interested in changing the content/UI of our call, we can override the callContentBuilder as a starting point.

Please take a minute to explore and look at the other parameters you can customize. There are many options for changing colors, layouts, behaviour, etc.

Next, we can use another Stream UI component, StreamCallContent to access the control area and add our custom button.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamCallContainer(
        call: call,
        callContentBuilder: (context, call, callState) {
          return StreamCallContent(
            call: call,
            callState: callState,
            callControlsBuilder: (context, call, callState) {
						    // Override the controls builder in StreamCallContent
            },
          );
        },
      ),
    );
  }

Finally, we can return the controls and options we would like to display to the user

return StreamCallContent(
    call: call,
    callState: callState,
    callControlsBuilder: (context, call, callState) {
      final localParticipant = callState.localParticipant!;
      return StreamCallControls(
        options: [
          CallControlOption(
            icon: const Text('👋'),
            onPressed: () => ScaffoldMessenger.of(context).showSnackBar(
              const SnackBar(
                content: Text('Hello'),
              ),
            ),
          ),
          FlipCameraOption(
            call: call,
            localParticipant: localParticipant,
          ),
          LeaveCallOption(call: call, onLeaveCallTap: call.leave),
        ],
      );
    },
  );

Modified Call UI

When tapped:

Display banner

© Getstream.io, Inc. All Rights Reserved.