Chat Fix & Web RTC Methods
parent
2b35c3d8d6
commit
74e01e60fb
Binary file not shown.
Binary file not shown.
@ -0,0 +1,61 @@
|
|||||||
|
// To parse this JSON data, do
|
||||||
|
//
|
||||||
|
// final remoteIceCandidatePayLoad = remoteIceCandidatePayLoadFromJson(jsonString);
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
class RemoteIceCandidatePayLoad {
|
||||||
|
RemoteIceCandidatePayLoad({
|
||||||
|
this.target,
|
||||||
|
this.candidate,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? target;
|
||||||
|
Candidate? candidate;
|
||||||
|
|
||||||
|
factory RemoteIceCandidatePayLoad.fromRawJson(String str) => RemoteIceCandidatePayLoad.fromJson(json.decode(str));
|
||||||
|
|
||||||
|
String toRawJson() => json.encode(toJson());
|
||||||
|
|
||||||
|
factory RemoteIceCandidatePayLoad.fromJson(Map<String, dynamic> json) => RemoteIceCandidatePayLoad(
|
||||||
|
target: json["target"],
|
||||||
|
candidate: json["candidate"] == null ? null : Candidate.fromJson(json["candidate"]),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"target": target,
|
||||||
|
"candidate": candidate?.toJson(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Candidate {
|
||||||
|
Candidate({
|
||||||
|
this.candidate,
|
||||||
|
this.sdpMid,
|
||||||
|
this.sdpMLineIndex,
|
||||||
|
this.usernameFragment,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? candidate;
|
||||||
|
String? sdpMid;
|
||||||
|
int? sdpMLineIndex;
|
||||||
|
String? usernameFragment;
|
||||||
|
|
||||||
|
factory Candidate.fromRawJson(String str) => Candidate.fromJson(json.decode(str));
|
||||||
|
|
||||||
|
String toRawJson() => json.encode(toJson());
|
||||||
|
|
||||||
|
factory Candidate.fromJson(Map<String, dynamic> json) => Candidate(
|
||||||
|
candidate: json["candidate"],
|
||||||
|
sdpMid: json["sdpMid"],
|
||||||
|
sdpMLineIndex: json["sdpMLineIndex"],
|
||||||
|
usernameFragment: json["usernameFragment"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"candidate": candidate,
|
||||||
|
"sdpMid": sdpMid,
|
||||||
|
"sdpMLineIndex": sdpMLineIndex,
|
||||||
|
"usernameFragment": usernameFragment,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,171 @@
|
|||||||
|
// import 'dart:async';
|
||||||
|
// import 'dart:io';
|
||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
//
|
||||||
|
// class DraggableCam extends StatefulWidget {
|
||||||
|
// //final Size availableScreenSize;
|
||||||
|
// final Widget child;
|
||||||
|
// final double scaleFactor;
|
||||||
|
// // final Stream<bool> onButtonBarVisible;
|
||||||
|
// // final Stream<double> onButtonBarHeight;
|
||||||
|
//
|
||||||
|
// const DraggableCam({
|
||||||
|
// Key? key,
|
||||||
|
// //@required this.availableScreenSize,
|
||||||
|
// required this.child,
|
||||||
|
// // @required this.onButtonBarVisible,
|
||||||
|
// // @required this.onButtonBarHeight,
|
||||||
|
//
|
||||||
|
// /// The portion of the screen the DraggableWidget should use.
|
||||||
|
// this.scaleFactor = .25,
|
||||||
|
// }) : assert(scaleFactor != null && scaleFactor > 0 && scaleFactor <= .4),
|
||||||
|
// // assert(availableScreenSize != null),
|
||||||
|
// // assert(onButtonBarVisible != null),
|
||||||
|
// // assert(onButtonBarHeight != null),
|
||||||
|
// super(key: key);
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// _DraggablePublisherState createState() => _DraggablePublisherState();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// class _DraggablePublisherState extends State<DraggableCam> {
|
||||||
|
// bool _isButtonBarVisible = true;
|
||||||
|
// double _buttonBarHeight = 0;
|
||||||
|
// late double _width;
|
||||||
|
// late double _height;
|
||||||
|
// late double _top;
|
||||||
|
// late double _left;
|
||||||
|
// late double _viewPaddingTop;
|
||||||
|
// late double _viewPaddingBottom;
|
||||||
|
// final double _padding = 8.0;
|
||||||
|
// final Duration _duration300ms = const Duration(milliseconds: 300);
|
||||||
|
// final Duration _duration0ms = const Duration(milliseconds: 0);
|
||||||
|
// late Duration _duration;
|
||||||
|
// late StreamSubscription _streamSubscription;
|
||||||
|
// late StreamSubscription _streamHeightSubscription;
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// void initState() {
|
||||||
|
// super.initState();
|
||||||
|
// _duration = _duration300ms;
|
||||||
|
// _width = widget.availableScreenSize.width * widget.scaleFactor;
|
||||||
|
// _height = _width * (widget.availableScreenSize.height / widget.availableScreenSize.width);
|
||||||
|
// _top = widget.availableScreenSize.height - (_buttonBarHeight + _padding) - _height;
|
||||||
|
// _left = widget.availableScreenSize.width - _padding - _width;
|
||||||
|
//
|
||||||
|
// _streamSubscription = widget.onButtonBarVisible.listen(_buttonBarVisible);
|
||||||
|
// _streamHeightSubscription = widget.onButtonBarHeight.listen(_getButtonBarHeight);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// void didChangeDependencies() {
|
||||||
|
// var mediaQuery = MediaQuery.of(context);
|
||||||
|
// _viewPaddingTop = mediaQuery.viewPadding.top;
|
||||||
|
// _viewPaddingBottom = mediaQuery.viewPadding.bottom;
|
||||||
|
// super.didChangeDependencies();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// void dispose() {
|
||||||
|
// _streamSubscription.cancel();
|
||||||
|
// _streamHeightSubscription.cancel();
|
||||||
|
// super.dispose();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void _getButtonBarHeight(double height) {
|
||||||
|
// setState(() {
|
||||||
|
// _buttonBarHeight = height;
|
||||||
|
// _positionWidget();
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void _buttonBarVisible(bool visible) {
|
||||||
|
// if (!mounted) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// setState(() {
|
||||||
|
// _isButtonBarVisible = visible;
|
||||||
|
// if (_duration == _duration300ms) {
|
||||||
|
// // only position the widget when we are not currently dragging it around
|
||||||
|
// _positionWidget();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return AnimatedPositioned(
|
||||||
|
// top: _top,
|
||||||
|
// left: _left,
|
||||||
|
// width: _width,
|
||||||
|
// height: _height,
|
||||||
|
// duration: _duration,
|
||||||
|
// child: Listener(
|
||||||
|
// onPointerDown: (_) => _duration = _duration0ms,
|
||||||
|
// onPointerMove: (PointerMoveEvent event) {
|
||||||
|
// setState(() {
|
||||||
|
// _left = (_left + event.delta.dx).roundToDouble();
|
||||||
|
// _top = (_top + event.delta.dy).roundToDouble();
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// onPointerUp: (_) => _positionWidget(),
|
||||||
|
// onPointerCancel: (_) => _positionWidget(),
|
||||||
|
// child: ClippedVideo(
|
||||||
|
// height: _height,
|
||||||
|
// width: _width,
|
||||||
|
// child: widget.child,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// double _getCurrentStatusBarHeight() {
|
||||||
|
// if (_isButtonBarVisible) {
|
||||||
|
// return _viewPaddingTop;
|
||||||
|
// }
|
||||||
|
// final _defaultViewPaddingTop = Platform.isIOS ? 20.0 : Platform.isAndroid ? 24.0 : 0.0;
|
||||||
|
// if (_viewPaddingTop > _defaultViewPaddingTop) {
|
||||||
|
// // There must be a hardware notch in the display.
|
||||||
|
// return _viewPaddingTop;
|
||||||
|
// }
|
||||||
|
// return 0.0;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// double _getCurrentButtonBarHeight() {
|
||||||
|
// if (_isButtonBarVisible) {
|
||||||
|
// return _buttonBarHeight + _viewPaddingBottom;
|
||||||
|
// }
|
||||||
|
// return _viewPaddingBottom;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void _positionWidget() {
|
||||||
|
// // Determine the center of the object being dragged so we can decide
|
||||||
|
// // in which corner the object should be placed.
|
||||||
|
// var dx = (_width / 2) + _left;
|
||||||
|
// dx = dx < 0 ? 0 : dx >= widget.availableScreenSize.width ? widget.availableScreenSize.width - 1 : dx;
|
||||||
|
// var dy = (_height / 2) + _top;
|
||||||
|
// dy = dy < 0 ? 0 : dy >= widget.availableScreenSize.height ? widget.availableScreenSize.height - 1 : dy;
|
||||||
|
// final draggableCenter = Offset(dx, dy);
|
||||||
|
//
|
||||||
|
// setState(() {
|
||||||
|
// _duration = _duration300ms;
|
||||||
|
// if (Rect.fromLTRB(0, 0, widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
|
||||||
|
// // Top-left
|
||||||
|
// _top = _getCurrentStatusBarHeight() + _padding;
|
||||||
|
// _left = _padding;
|
||||||
|
// } else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, 0, widget.availableScreenSize.width, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
|
||||||
|
// // Top-right
|
||||||
|
// _top = _getCurrentStatusBarHeight() + _padding;
|
||||||
|
// _left = widget.availableScreenSize.width - _padding - _width;
|
||||||
|
// } else if (Rect.fromLTRB(0, widget.availableScreenSize.height / 2, widget.availableScreenSize.width / 2, widget.availableScreenSize.height).contains(draggableCenter)) {
|
||||||
|
// // Bottom-left
|
||||||
|
// _top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
|
||||||
|
// _left = _padding;
|
||||||
|
// } else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2, widget.availableScreenSize.width, widget.availableScreenSize.height).contains(draggableCenter)) {
|
||||||
|
// // Bottom-right
|
||||||
|
// _top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
|
||||||
|
// _left = widget.availableScreenSize.width - _padding - _width;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@ -0,0 +1,267 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:core';
|
||||||
|
import 'dart:ui';
|
||||||
|
import 'package:draggable_widget/draggable_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||||
|
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||||
|
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
|
||||||
|
import 'package:mohem_flutter_app/provider/chat_call_provider.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class StartCallPage extends StatefulWidget {
|
||||||
|
RTCVideoRenderer localRenderer;
|
||||||
|
RTCVideoRenderer remoteRenderer;
|
||||||
|
|
||||||
|
StartCallPage({required this.localRenderer, required this.remoteRenderer});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_StartCallPageState createState() => _StartCallPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StartCallPageState extends State<StartCallPage> {
|
||||||
|
final dragController = DragController();
|
||||||
|
late ChatCallProvider callProvider;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
callProvider = Provider.of<ChatCallProvider>(context, listen: false);
|
||||||
|
super.initState();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
Future.delayed(const Duration(seconds: 1), () {
|
||||||
|
callProvider.notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<ChatCallProvider>(builder: (BuildContext context, ChatCallProvider provider, Widget? child) {
|
||||||
|
return SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
child: Stack(
|
||||||
|
alignment: FractionalOffset.center,
|
||||||
|
children: [
|
||||||
|
if (provider.isVideoCall)
|
||||||
|
Positioned.fill(
|
||||||
|
child: RTCVideoView(
|
||||||
|
widget.remoteRenderer,
|
||||||
|
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
|
||||||
|
key: const Key('remote'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (provider.isVideoCall)
|
||||||
|
DraggableWidget(
|
||||||
|
bottomMargin: 20,
|
||||||
|
topMargin: 40,
|
||||||
|
intialVisibility: true,
|
||||||
|
horizontalSpace: 20,
|
||||||
|
shadowBorderRadius: 50,
|
||||||
|
initialPosition: AnchoringPosition.topLeft,
|
||||||
|
dragController: dragController,
|
||||||
|
normalShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
|
||||||
|
draggingShadow: const BoxShadow(spreadRadius: 0.0, blurRadius: 0.0),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 200,
|
||||||
|
width: 140,
|
||||||
|
child: RTCVideoView(
|
||||||
|
widget.localRenderer,
|
||||||
|
mirror: true,
|
||||||
|
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!provider.isVideoCall)
|
||||||
|
Positioned.fill(
|
||||||
|
child: ClipRect(
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: MyColors.grey57Color.withOpacity(
|
||||||
|
0.3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
40.height,
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.all(21.0),
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
left: 10.0,
|
||||||
|
right: 10.0,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: <Widget>[
|
||||||
|
SvgPicture.asset(
|
||||||
|
"assets/images/user.svg",
|
||||||
|
height: 70,
|
||||||
|
width: 70,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
10.height,
|
||||||
|
Text(
|
||||||
|
callProvider.outGoingCallData.receiverName!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 21,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: MyColors.white,
|
||||||
|
letterSpacing: -1.26,
|
||||||
|
height: 23 / 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"On Call",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(
|
||||||
|
0xffC6C6C6,
|
||||||
|
),
|
||||||
|
letterSpacing: -0.48,
|
||||||
|
height: 23 / 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
bottom: 20,
|
||||||
|
left: 40,
|
||||||
|
right: 40,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
// if (provider.isVideoCall)
|
||||||
|
RawMaterialButton(
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
onPressed: () {
|
||||||
|
callProvider.loudOn();
|
||||||
|
},
|
||||||
|
elevation: 2.0,
|
||||||
|
fillColor: callProvider.isLoudSpeaker ? MyColors.green2DColor : Colors.grey,
|
||||||
|
padding: const EdgeInsets.all(
|
||||||
|
10.0,
|
||||||
|
),
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.volume_up,
|
||||||
|
color: MyColors.white,
|
||||||
|
size: 30.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RawMaterialButton(
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
onPressed: () {
|
||||||
|
provider.camOff();
|
||||||
|
},
|
||||||
|
elevation: 2.0,
|
||||||
|
fillColor: provider.isCamOff ? MyColors.green2DColor : Colors.grey,
|
||||||
|
padding: const EdgeInsets.all(
|
||||||
|
10.0,
|
||||||
|
),
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
child: Icon(
|
||||||
|
provider.isCamOff ? Icons.videocam_off : Icons.videocam,
|
||||||
|
color: MyColors.white,
|
||||||
|
size: 30.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RawMaterialButton(
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
onPressed: () {
|
||||||
|
provider.switchCamera();
|
||||||
|
},
|
||||||
|
elevation: 2.0,
|
||||||
|
fillColor: provider.isFrontCamera ? Colors.grey : MyColors.green2DColor,
|
||||||
|
padding: const EdgeInsets.all(
|
||||||
|
10.0,
|
||||||
|
),
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
child: Icon(
|
||||||
|
provider.isFrontCamera ? Icons.switch_camera_outlined : Icons.switch_camera,
|
||||||
|
color: MyColors.white,
|
||||||
|
size: 30.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RawMaterialButton(
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
onPressed: () {
|
||||||
|
provider.micOff();
|
||||||
|
},
|
||||||
|
elevation: 2.0,
|
||||||
|
fillColor: provider.isMicOff ? MyColors.green2DColor : Colors.grey,
|
||||||
|
padding: const EdgeInsets.all(
|
||||||
|
10.0,
|
||||||
|
),
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
child: Icon(
|
||||||
|
provider.isMicOff ? Icons.mic_off : Icons.mic,
|
||||||
|
color: MyColors.white,
|
||||||
|
size: 30.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RawMaterialButton(
|
||||||
|
constraints: BoxConstraints(),
|
||||||
|
onPressed: () {
|
||||||
|
provider.endCall().then((value) {
|
||||||
|
if (value) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
elevation: 2.0,
|
||||||
|
fillColor: MyColors.redA3Color,
|
||||||
|
padding: const EdgeInsets.all(
|
||||||
|
10.0,
|
||||||
|
),
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.call_end,
|
||||||
|
color: MyColors.white,
|
||||||
|
size: 30.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue