Building a Real-Time game app with Flutter and WebRTC.

The game industry is moving to a new era with real-time communication. Like all other industries Gaming world also sees the need for real-time collaboration for multiplayer games. With web-based games built to be played on cloud trending WebRTC is becoming the go-to technology to build these real-time game applications. In this article, we discuss how WebRTC can be used to exchange both data and video streams for real-time collaboration. To keep things simple we used Tic Tac Toe as the game hence peer to peer video communication is used to communicate between the two players.

Flutter is Google's portable SDK for creating natively compiled mobile, web, and desktop applications and it has become increasingly popular, establishing itself as the go-to technology for developing cross-platform mobile apps.

Therefore we decided to build a real-time simple mobile game application with the help of the flutter_webrtc package as an experiment. . flutter_webrtc is the Dart implementation of standard Javascript WebRTC APIs that we have seen on the Web browser.

Since flutter_webrtc exposes the same APIs as Javascript WebRTC APIs it is easier to create a mobile application with it if you are familiar with JS WebRTC APIs. For simplicity, we use Firebase Firestore as a signaling service.

After creating the new Flutter project we have to add the flutter_webrtc package to our project.

To access the device camera and microphone we have to add some code snippets to both android and iOS configuration flies as flutter_webrtc documentation said.

For iOS

Add the following entry to your Info.plist file, located in <project root>/ios/Runner/Info.plist:

<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) Camera Usage!</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) Microphone Usage!</string>

This entry allows your app to access the camera and microphone.

For Android

Ensure the following permission is present in your Android Manifest file, located in <project root>/android/app/src/main/AndroidManifest.xml:

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

Also, you will need to set your build settings to Java 8, because the official WebRTC jar now uses static methods in the interface. Just add this to your app-level build.gradle:

android {
    //...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

After configuring permission we can start to build the application in flutter. Here we are writing all code in a single widget screen in the file. First import the flutter_webrtc package to main.dart file and initiate required variables in the screen widget class.

// RTCVideoRenderer is the equilent of HTML video object. 
final RTCVideoRenderer _localRenderer = RTCVideoRenderer();
final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();

// RTCPeerConnection instance
RTCPeerConnection? peerConnection;

// MediaStream instances for referencing local and remort streams.
MediaStream? localStream;
MediaStream? remoteStream;

// RTCDataChannel instance for exchange game related data.
RTCDataChannel? dataChannel;

// Remote MediaStream listner callback function.
StreamStateCallback? onAddRemoteStream;

Once the Screen widget is rendered to the tree, we are going to initiate renderers and start the local camera feed.

@override
void  initState() {
	_localRenderer.initialize();
	_remoteRenderer.initialize();

	onAddRemoteStream = ((stream) {
		_remoteRenderer.srcObject = stream;
	});

	openUserMedia(_localRenderer, _remoteRenderer);
	super.initState();
}

Future<void> openUserMedia(
	RTCVideoRenderer  localVideo,
	RTCVideoRenderer  remoteVideo,
) async {
	var  stream = await  navigator.mediaDevices
	.getUserMedia({'video': true, 'audio': false});

	localVideo.srcObject = stream;
	localStream = stream;

	remoteVideo.srcObject = await  createLocalMediaStream('key');
}

When Create Room button is pressed, the app creates a new room in Firestore and registers signaling listeners, peer connection listeners, and RTC DataChannel for game data sharing.

void  initDataChannel() async {

	dataChannel = await  peerConnection?.createDataChannel('gameDataChannel', RTCDataChannelInit());
	
	dataChannel?.onMessage = (message) {
		// Execute functions when game related data message revieved.
	};
}

Future<String> createRoom(RTCVideoRenderer  remoteRenderer) async {
	...
	peerConnection = await  createPeerConnection(configuration);
	initDataChannel();
	registerPeerConnectionListeners();
	...
}

...

void  sendGameData(dynamic  data) {
	dataChannel?.send(RTCDataChannelMessage(data.toString()));
}
...

Once one player-created game room, another player can join the same room by using generated room id.

Future<void> joinRoom(String  roomId, RTCVideoRenderer  remoteVideo) async {
	// valitate entered room ID

	// create peer connection
	...
	registerPeerConnectionListeners();
	...
}

After the second player is connected to the game room, they can play the game together.

That's it. Even though this was a simple example of implementing a real-time game with tic tac toe this could be developed into advanced multiplayer games. Real-time video chatting gives the real world feeling for players to express their ideas. Stay tuned for more experiments!


We are a development company specialized in Real-Time Communication. If you need a custom video application to be build please contact us via support@telzee.io