Skip to content

Commit ce3aec1

Browse files
authored
Merge pull request #39 from cloudwebrtc/flutter_web
Add web support.
2 parents bdea0d1 + 88f4a82 commit ce3aec1

11 files changed

+267
-111
lines changed

lib/main.dart

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import 'dart:core';
22
import 'dart:io';
33
import 'package:flutter/material.dart';
4-
import 'package:shared_preferences/shared_preferences.dart';
4+
import 'package:flutter_webrtc/webrtc.dart';
5+
6+
import 'src/utils/key_value_store.dart'
7+
if (dart.library.js) 'src/utils/key_value_store_web.dart';
58
import 'package:flutter/foundation.dart'
69
show debugDefaultTargetPlatformOverride;
710

@@ -10,13 +13,9 @@ import 'src/call_sample/call_sample.dart';
1013
import 'src/call_sample/data_channel_sample.dart';
1114
import 'src/route_item.dart';
1215

13-
bool isDesktop() {
14-
return Platform.isWindows || Platform.isLinux || Platform.isMacOS;
15-
}
16-
1716
void main(){
18-
if(isDesktop())
19-
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
17+
if (WebRTC.platformIsDesktop)
18+
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
2019
runApp(new MyApp());
2120
}
2221

@@ -33,7 +32,7 @@ enum DialogDemoAction {
3332
class _MyAppState extends State<MyApp> {
3433
List<RouteItem> items;
3534
String _serverAddress = '';
36-
SharedPreferences prefs;
35+
KeyValueStore keyValueStore = KeyValueStore();
3736
bool _datachannel = false;
3837
@override
3938
initState() {
@@ -71,9 +70,9 @@ class _MyAppState extends State<MyApp> {
7170
}
7271

7372
_initData() async {
74-
prefs = await SharedPreferences.getInstance();
73+
await keyValueStore.init();
7574
setState(() {
76-
_serverAddress = prefs.getString('server') ?? 'demo.cloudwebrtc.com';
75+
_serverAddress = keyValueStore.getString('server') ?? 'demo.cloudwebrtc.com';
7776
});
7877
}
7978

@@ -85,12 +84,13 @@ class _MyAppState extends State<MyApp> {
8584
// The value passed to Navigator.pop() or null.
8685
if (value != null) {
8786
if (value == DialogDemoAction.connect) {
88-
prefs.setString('server', _serverAddress);
87+
keyValueStore.setString('server', _serverAddress);
8988
Navigator.push(
9089
context,
9190
MaterialPageRoute(
92-
builder: (BuildContext context) =>
93-
_datachannel? DataChannelSample(ip: _serverAddress) : CallSample(ip: _serverAddress)));
91+
builder: (BuildContext context) => _datachannel
92+
? DataChannelSample(ip: _serverAddress)
93+
: CallSample(ip: _serverAddress)));
9494
}
9595
}
9696
});

lib/src/call_sample/call_sample.dart

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:flutter/material.dart';
2-
import 'dart:io';
32
import 'dart:core';
43
import 'signaling.dart';
54
import 'package:flutter_webrtc/webrtc.dart';
@@ -17,8 +16,6 @@ class CallSample extends StatefulWidget {
1716

1817
class _CallSampleState extends State<CallSample> {
1918
Signaling _signaling;
20-
String _displayName =
21-
Platform.localHostname + '(' + Platform.operatingSystem + ")";
2219
List<dynamic> _peers;
2320
var _selfId;
2421
RTCVideoRenderer _localRenderer = new RTCVideoRenderer();
@@ -50,8 +47,7 @@ class _CallSampleState extends State<CallSample> {
5047

5148
void _connect() async {
5249
if (_signaling == null) {
53-
_signaling = new Signaling(serverIP, _displayName)
54-
..connect();
50+
_signaling = new Signaling(serverIP)..connect();
5551

5652
_signaling.onStateChange = (SignalingState state) {
5753
switch (state) {
@@ -114,9 +110,7 @@ class _CallSampleState extends State<CallSample> {
114110
_signaling.switchCamera();
115111
}
116112

117-
_muteMic() {
118-
119-
}
113+
_muteMic() {}
120114

121115
_buildRow(context, peer) {
122116
var self = (peer['id'] == _selfId);
@@ -164,25 +158,26 @@ class _CallSampleState extends State<CallSample> {
164158
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
165159
floatingActionButton: _inCalling
166160
? new SizedBox(
167-
width: 200.0,
168-
child: new Row(
169-
mainAxisAlignment: MainAxisAlignment.spaceBetween,
170-
children: <Widget>[
171-
FloatingActionButton(
172-
child: const Icon(Icons.switch_camera),
173-
onPressed: _switchCamera,
174-
),
175-
FloatingActionButton(
176-
onPressed: _hangUp,
177-
tooltip: 'Hangup',
178-
child: new Icon(Icons.call_end),
179-
backgroundColor: Colors.pink,
180-
),
181-
FloatingActionButton(
182-
child: const Icon(Icons.mic_off),
183-
onPressed: _muteMic,
184-
)
185-
])) : null,
161+
width: 200.0,
162+
child: new Row(
163+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
164+
children: <Widget>[
165+
FloatingActionButton(
166+
child: const Icon(Icons.switch_camera),
167+
onPressed: _switchCamera,
168+
),
169+
FloatingActionButton(
170+
onPressed: _hangUp,
171+
tooltip: 'Hangup',
172+
child: new Icon(Icons.call_end),
173+
backgroundColor: Colors.pink,
174+
),
175+
FloatingActionButton(
176+
child: const Icon(Icons.mic_off),
177+
onPressed: _muteMic,
178+
)
179+
]))
180+
: null,
186181
body: _inCalling
187182
? OrientationBuilder(builder: (context, orientation) {
188183
return new Container(

lib/src/call_sample/data_channel_sample.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import 'dart:core';
44
import 'dart:async';
55
import 'dart:typed_data';
66
import 'signaling.dart';
7-
import 'package:flutter_webrtc/rtc_data_channel.dart';
7+
import 'package:flutter_webrtc/webrtc.dart';
88

99
class DataChannelSample extends StatefulWidget {
1010
static String tag = 'call_sample';
@@ -19,8 +19,6 @@ class DataChannelSample extends StatefulWidget {
1919

2020
class _DataChannelSampleState extends State<DataChannelSample> {
2121
Signaling _signaling;
22-
String _displayName =
23-
Platform.localHostname + '(' + Platform.operatingSystem + ")";
2422
List<dynamic> _peers;
2523
var _selfId;
2624
bool _inCalling = false;
@@ -47,7 +45,7 @@ class _DataChannelSampleState extends State<DataChannelSample> {
4745

4846
void _connect() async {
4947
if (_signaling == null) {
50-
_signaling = new Signaling(serverIP, _displayName)
48+
_signaling = new Signaling(serverIP)
5149
..connect();
5250

5351
_signaling.onDataChannelMessage = (dc, RTCDataChannelMessage data){

lib/src/call_sample/signaling.dart

Lines changed: 30 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import 'dart:convert';
22
import 'dart:async';
3-
import 'dart:io';
4-
import 'dart:math';
53
import 'package:flutter_webrtc/webrtc.dart';
64
import 'random_string.dart';
75

6+
import '../utils/device_info.dart'
7+
if (dart.library.js) '../utils/device_info_web.dart';
8+
import '../utils/websocket.dart'
9+
if (dart.library.js) '../utils/websocket_web.dart';
10+
811
enum SignalingState {
912
CallStateNew,
1013
CallStateRinging,
@@ -28,11 +31,10 @@ typedef void DataChannelCallback(RTCDataChannel dc);
2831

2932
class Signaling {
3033
String _selfId = randomNumeric(6);
31-
var _socket;
34+
SimpleWebSocket _socket;
3235
var _sessionId;
3336
var _host;
3437
var _port = 4443;
35-
var _displayName;
3638
var _peerConnections = new Map<String, RTCPeerConnection>();
3739
var _dataChannels = new Map<String, RTCDataChannel>();
3840
var _remoteCandidates = [];
@@ -84,7 +86,7 @@ class Signaling {
8486
'optional': [],
8587
};
8688

87-
Signaling(this._host, this._displayName);
89+
Signaling(this._host);
8890

8991
close() {
9092
if (_localStream != null) {
@@ -258,74 +260,36 @@ class Signaling {
258260
}
259261
}
260262

261-
Future<WebSocket> _connectForSelfSignedCert(String host, int port) async {
262-
try {
263-
Random r = new Random();
264-
String key = base64.encode(List<int>.generate(8, (_) => r.nextInt(255)));
265-
SecurityContext securityContext = new SecurityContext();
266-
HttpClient client = HttpClient(context: securityContext);
267-
client.badCertificateCallback =
268-
(X509Certificate cert, String host, int port) {
269-
print('Allow self-signed certificate => $host:$port. ');
270-
return true;
271-
};
272-
273-
HttpClientRequest request = await client.getUrl(
274-
Uri.parse('https://$host:$port/ws')); // form the correct url here
275-
request.headers.add('Connection', 'Upgrade');
276-
request.headers.add('Upgrade', 'websocket');
277-
request.headers.add(
278-
'Sec-WebSocket-Version', '13'); // insert the correct version here
279-
request.headers.add('Sec-WebSocket-Key', key.toLowerCase());
280-
281-
HttpClientResponse response = await request.close();
282-
Socket socket = await response.detachSocket();
283-
var webSocket = WebSocket.fromUpgradedSocket(
284-
socket,
285-
protocol: 'signaling',
286-
serverSide: false,
287-
);
288-
289-
return webSocket;
290-
} catch (e) {
291-
throw e;
292-
}
293-
}
294-
295263
void connect() async {
296-
try {
297-
/*
298-
var url = 'ws://$_host:$_port';
299-
_socket = await WebSocket.connect(url);
300-
*/
301-
_socket = await _connectForSelfSignedCert(_host, _port);
264+
var url = 'wss://$_host:$_port';
265+
_socket = SimpleWebSocket(url);
302266

303-
if (this.onStateChange != null) {
304-
this.onStateChange(SignalingState.ConnectionOpen);
305-
}
306-
307-
_socket.listen((data) {
308-
print('Recivied data: ' + data);
309-
JsonDecoder decoder = new JsonDecoder();
310-
this.onMessage(decoder.convert(data));
311-
}, onDone: () {
312-
print('Closed by server!');
313-
if (this.onStateChange != null) {
314-
this.onStateChange(SignalingState.ConnectionClosed);
315-
}
316-
});
267+
print('connect to $url');
317268

269+
_socket.onOpen = () {
270+
print('onOpen');
271+
this?.onStateChange(SignalingState.ConnectionOpen);
318272
_send('new', {
319-
'name': _displayName,
273+
'name': DeviceInfo.label,
320274
'id': _selfId,
321-
'user_agent':
322-
'flutter-webrtc/' + Platform.operatingSystem + '-plugin 0.0.1'
275+
'user_agent': DeviceInfo.userAgent
323276
});
324-
} catch (e) {
277+
};
278+
279+
_socket.onMessage = (message) {
280+
print('Recivied data: ' + message);
281+
JsonDecoder decoder = new JsonDecoder();
282+
this.onMessage(decoder.convert(message));
283+
};
284+
285+
_socket.onClose = (int code, String reason) {
286+
print('Closed by server [$code => $reason]!');
325287
if (this.onStateChange != null) {
326-
this.onStateChange(SignalingState.ConnectionError);
288+
this.onStateChange(SignalingState.ConnectionClosed);
327289
}
328-
}
290+
};
291+
292+
await _socket.connect();
329293
}
330294

331295
Future<MediaStream> createStream(media, user_screen) async {
@@ -440,7 +404,6 @@ class Signaling {
440404
_send(event, data) {
441405
data['type'] = event;
442406
JsonEncoder encoder = new JsonEncoder();
443-
if (_socket != null) _socket.add(encoder.convert(data));
444-
print('send: ' + encoder.convert(data));
407+
_socket.send(encoder.convert(data));
445408
}
446409
}

lib/src/utils/device_info.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import 'dart:io';
2+
3+
class DeviceInfo {
4+
static String get label {
5+
return Platform.localHostname + '(' + Platform.operatingSystem + ")";
6+
}
7+
8+
static String get userAgent {
9+
return 'flutter-webrtc/' + Platform.operatingSystem + '-plugin 0.0.1';
10+
}
11+
}

lib/src/utils/device_info_web.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import 'dart:html' as HTML;
2+
3+
class DeviceInfo {
4+
static String get label {
5+
return 'Flutter Web ( ' + HTML.window.navigator.userAgent + ' )';
6+
}
7+
8+
static String get userAgent {
9+
return 'flutter-webrtc/web-plugin 0.0.1';
10+
}
11+
}

lib/src/utils/key_value_store.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import 'dart:async';
2+
import 'package:shared_preferences/shared_preferences.dart';
3+
4+
class KeyValueStore {
5+
KeyValueStore();
6+
SharedPreferences _preferences;
7+
8+
init() async {
9+
_preferences = await SharedPreferences.getInstance();
10+
}
11+
12+
String getString(String key) => _preferences.getString(key);
13+
14+
Future<bool> setString(String key, String value) =>
15+
_preferences.setString(key, value);
16+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import 'dart:async';
2+
import 'dart:convert';
3+
import 'dart:html';
4+
5+
class KeyValueStore {
6+
KeyValueStore();
7+
Storage _storage;
8+
9+
init() async {
10+
_storage = window.localStorage;
11+
}
12+
13+
String getString(String key) => _storage[key];
14+
15+
Future<bool> setString(String key, String value) => _setValue(key, value);
16+
17+
Future<bool> _setValue(String key, dynamic value) {
18+
if (value is String) {
19+
_storage[key] = value;
20+
} else if (value is bool || value is double || value is int) {
21+
_storage[key] = value.toString();
22+
} else if (value is List) {
23+
_storage[key] = json.encode(value);
24+
}
25+
26+
return Future.value(true);
27+
}
28+
}

0 commit comments

Comments
 (0)