A few months ago, my colleague Yohann wrote a fantastic article on How to create a React Native live streaming application for mobile targets. This time, you will learn how to create the same application with a React Native competitor: Flutter.
In this article, you will develop a live streaming application in Dart. For further details, check out our official Flutter live stream repository.
Few words about Flutter
Google developed Flutter in 2015. In 2021, it was the most used cross-platform mobile framework (42% against 38% for React Native, source). It is used to develop cross-platform applications (including iOS and Android) quickly. The main difference between Flutter and React Native is that Flutter compiles Dart code to native bytecode/libraries, whereas React Native uses an engine to interpret JavaScript or TypeScript code. If you haven't heard of Flutter, you can learn more on the Flutter website.
Getting started
First, follow the development setup steps from Flutter's official documentation.
If you're new to Flutter, you may also need to learn the basics.
Quick reminder: iOS development is only enabled for macOS users. If you're on Windows or Linux, you can develop for Android.
Create a new Flutter app
- Open Android Studio.
- You should see a “New Flutter Project” button. Click on it.
- Verify the Flutter SDK path and click on “Next.”
- Enter your project name: “live_stream_app” (according to the pubspec documentation, the name must be lower_case_with_underscore.)
- Make sure the project type is set to Application.
- Click on “Finish.”
Launch the application
Once Android Studio is launched, you can directly run your application with the control toolbar:
- Select your phone.
- Click on the Run ▶️ icon.
It will take several minutes to finish, stay calm and grab a coffee ☕️
When the application build completes, it will be launched on your phone. Check your phone 🤳🏾
🎉🎉🎉Congratulations, you have created your first Flutter application 🎉🎉🎉
Signs your iOS application (optional)
If you have trouble building the application for an iOS device, it might be because the application is not associated with a development profile.
Open Xcode, click "Open a project or file," and open the Runner.xcworkspace
located in live_stream_app/ios.
Click on Runner, go to the Signing & Capabilities
tab, add your team, and create a unique bundle identifier.
Add the live streaming client to your app
Now let's install the Flutter live streaming client!
Installation
Open a terminal in your live_stream_app
folder and the following command:
flutter pub add apivideo_live_stream
Also, you need to set the Android minSdkVersion to the one supported by Flutter live stream client.
Open your application build.gradle. It's located in the live_stream_app/android/app directory and replace:
minSdkVersion flutter.minSdkVersion
to:
minSdkVersion 21
Permissions
⚠️ Both iOS and Android need permission to broadcast from your device's camera and record audio.
You will also need to ask for internet permission for Android.
Android
From your project folder, open the AndroidManifest.xml
file. It’s located in the live_stream_app/android/app/src/main
directory.
Add the following content just under the opening <manifest>
tag to enable internet, audio recording, and the camera for your application:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
iOS
From your project folder, open the Info.plist
file. It's located in the live_stream_app/ios/Runner
directory.
Add the following content just under the opening <dict>
tag to enable the camera and microphone:
<key>NSCameraUsageDescription</key>
<string>Your description of the application</string>
<key>NSMicrophoneUsageDescription</key>
<string>Your description of the application</string>
Don't forget to add the app description, as shown above, if you want to deploy your application in app stores.
Usage
You can check the Flutter live streaming sample application for the whole application picture.
Create a controller and the camera preview
Change your main.dart
file located in the live_stream_app/lib
with the following code:
import 'package:apivideo_live_stream/apivideo_live_stream.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'My live stream app'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late final ApiVideoLiveStreamController _controller;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Fills the whole view
Expanded(
child: Container(
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Center(
child: ApiVideoCameraPreview(controller: _controller),
),
),
),
),
],
),
),
);
}
void initState() {
_controller = createLiveStreamController();
_controller.initialize().catchError((e) {
print('Failed to initialize controller: $e');
});
super.initState();
}
ApiVideoLiveStreamController createLiveStreamController() {
return ApiVideoLiveStreamController(
initialAudioConfig: AudioConfig(),
initialVideoConfig: VideoConfig.withDefaultBitrate(),
onConnectionSuccess: () {
print('Connection succeeded');
},
onConnectionFailed: (error) {
print('Connection failed: $error');
},
onDisconnection: () {
print('Disconnected');
});
}
}
Launch your application. You now have the camera preview.
Manage application lifecycle
The next step is to manage application lifecycle. You will have to stop/start the camera when your application goes to/from the background.
The class _MyHomePageState
must implement the abstract class WidgetsBindingObserver
. Replace its declaration with:
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver
Register the lifecycle observer, add in initState:
WidgetsBinding.instance.addObserver(this);
Manage lifecycle:
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.inactive) {
_controller.stop();
} else if (state == AppLifecycleState.resumed) {
_controller.startPreview();
}
}
Add a control panel
You still can't start a live stream because there is no way to do it.
We are going to add a Row of IconButton that can control your live streaming.
Import the following package:
import 'package:flutter/services.dart';
Then, add the following code:
/// Keeps the streaming state in sync with the controller.
var _isStreaming = false;
/// Display the control bar with buttons to take pictures and record videos.
Widget _controlRowWidget() {
final ApiVideoLiveStreamController? controller = _controller;
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: const Icon(Icons.fiber_manual_record),
color: Colors.red,
onPressed: controller != null && !_isStreaming
? onStartStreamingButtonPressed
: null,
),
IconButton(
icon: const Icon(Icons.stop),
color: Colors.red,
onPressed: controller != null && _isStreaming
? onStopStreamingButtonPressed
: null),
],
);
}
void onStartStreamingButtonPressed() {
startStreaming().then((_) {
if (mounted) {
setIsStreaming(true);
}
}).catchError((error) {
if (error is PlatformException) {
print("Error: failed to start stream: ${error.message}");
} else {
print("Error: failed to start stream: $error");
}
});
}
void onStopStreamingButtonPressed() {
stopStreaming().then((_) {
if (mounted) {
setIsStreaming(false);
}
}).catchError((error) {
if (error is PlatformException) {
print("Error: failed to stop stream: ${error.message}");
} else {
print("Error: failed to stop stream: $error");
}
});
}
Future<void> startStreaming() async {
final ApiVideoLiveStreamController? controller = _controller;
if (controller == null) {
print('Error: create a camera controller first.');
return;
}
await controller.startStreaming(streamKey: 'YOUR_STREAM_KEY');
}
Future<void> stopStreaming() async {
final ApiVideoLiveStreamController? controller = _controller;
if (controller == null) {
print('Error: create a camera controller first.');
return;
}
await controller.stopStreaming();
}
void setIsStreaming(bool isStreaming) {
setState(() {
_isStreaming = isStreaming;
});
}
Then, add the _controlRowWidget() in the Colum under the ApiVideoCameraPreview. Replace your _MyHomePageState build method by:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Fills the whole view
Expanded(
child: Container(
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Center(
child: ApiVideoCameraPreview(controller: _controller),
),
),
),
),
_controlRowWidget(),
],
),
),
);
}
Finally, update the _isStreaming
variable in the controller’s callbacks to update the IconButton
state when the live streaming is stopped.
Replace createLiveStreamController
by:
ApiVideoLiveStreamController createLiveStreamController() {
return ApiVideoLiveStreamController(
initialAudioConfig: AudioConfig(),
initialVideoConfig: VideoConfig.withDefaultBitrate(),
onConnectionSuccess: () {
print('Connection succeeded');
},
onConnectionFailed: (error) {
print('Connection failed: $error');
if (mounted) {
setIsStreaming(false);
}
},
onDisconnection: () {
print('Disconnected');
if (mounted) {
setIsStreaming(false);
}
});
}
Run ▶️ your application again.
Launch your first live stream
⚠️⚠️⚠️ If you want to run your live stream, you will need to change YOUR_STREAM_KEY
to a real one.
You can find yours in the api.video dashboard. If you don't have an account yet, create one, it's free. Then, create a livestream from the overview or the “Live streams” page.
A modal will appear with several pieces of information in it, including the shareable link and stream key. Save the shareable link to access your live stream later.
💡 You can also use the api.video API to create a live stream and retrieve your stream key dynamically. Find how to do this right here.
Paste your stream key in place of `YOUR_STREAM_KEY`.
Click on the live button in your application; it should be disabled if the live streaming properly starts. Then, go to the shareable link you just saved.
There you go! You can see what your mobile is streaming in real-time! If not, wait a few seconds and check your stream key value.
🎉🎉🎉 Congratulations, you have created your first live streaming Flutter application 🎉🎉🎉
You can now do an actual live stream directly from your Android and iOS devices.
And now what?
api.video provides two development environments. The sandbox allows you to live stream directly from your computer or mobile, but a watermark will be applied to your video, and there is a 30-minute limit for live streams.
To avoid these limitations, subscribes to api.video and go into production mode 🚀.
Also, the Flutter RTMP live stream client allows us to do much more in terms of configuration!
You can match both audio and video configuration to your needs.
Also, there are some properties can be updated during the live stream:
- Camera: front or back
- Audio: muted or unmuted.
Check out the README for exhaustive information about the apivideo_live_stream
package.
Thibault Beyou
Senior Mobile Developer
Follow our latest news by subscribing to our newsletter
Create your free account
Start building with video now