You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
13 KiB
395 lines
13 KiB
7 months ago
|
import 'dart:convert';
|
||
|
import 'dart:io';
|
||
|
import 'package:flutter/material.dart';
|
||
|
import 'package:flutter/services.dart';
|
||
|
import 'package:healthcare_pharmacy/pages/index.dart';
|
||
|
import 'package:healthcare_pharmacy/settings.dart';
|
||
|
import 'package:url_launcher/url_launcher.dart' as UrlLauncher;
|
||
|
import 'package:web_socket_channel/io.dart';
|
||
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||
|
import 'models/ChatMessage.dart';
|
||
|
import 'package:image_picker/image_picker.dart';
|
||
|
import 'package:flutter_callkeep/flutter_callkeep.dart';
|
||
|
|
||
|
|
||
|
class ChatUI extends StatefulWidget {
|
||
|
|
||
|
const ChatUI({Key? key}) : super(key: key);
|
||
|
|
||
|
@override
|
||
|
State<ChatUI> createState() => _ChatUIState();
|
||
|
}
|
||
|
|
||
|
class _ChatUIState extends State<ChatUI> {
|
||
|
WebSocketChannel channel=IOWebSocketChannel.connect(
|
||
|
"wss://socketsbay.com/wss/v2/1/demo/",
|
||
|
);
|
||
|
// WebSocket channel for call signaling
|
||
|
late WebSocketChannel callChannel;
|
||
|
|
||
|
WebSocketConnectionState _connectionState = WebSocketConnectionState.connecting;
|
||
|
late ScrollController _scrollController;
|
||
|
final TextEditingController _controller = TextEditingController();
|
||
|
// Store messages
|
||
|
final List<ChatMessage> _messages = [];
|
||
|
String myUserId = 'user456';
|
||
|
String otherUserId = 'user123';
|
||
|
final ImagePicker _picker = ImagePicker();
|
||
|
|
||
|
Future pickImageFromGallery() async {
|
||
|
try {
|
||
|
final image = await _picker.pickImage(source: ImageSource.gallery);
|
||
|
if (image == null) return;
|
||
|
final imageTemp = File(image.path);
|
||
|
setState(() {
|
||
|
AppSettings.updatedImage = imageTemp;
|
||
|
});
|
||
|
// uploadProfileApi(AppSettings.updatedImage);
|
||
|
AppSettings.saveProfile(image.path);
|
||
|
|
||
|
|
||
|
} on PlatformException catch (e) {
|
||
|
print('Failed to pick image: $e');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Future takeImageFromCamera() async {
|
||
|
try {
|
||
|
final image = await _picker.pickImage(source: ImageSource.camera);
|
||
|
if (image == null) return;
|
||
|
final imageTemp = File(image.path);
|
||
|
setState(() {
|
||
|
AppSettings.updatedImage = imageTemp;
|
||
|
});
|
||
|
|
||
|
//uploadProfileApi(AppSettings.updatedImage);
|
||
|
AppSettings.saveProfile(image.path);
|
||
|
|
||
|
} on PlatformException catch (e) {
|
||
|
print('Failed to pick image: $e');
|
||
|
}
|
||
|
}
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
appBar: AppBar(
|
||
|
backgroundColor: _connectionState == WebSocketConnectionState.error ? Colors.red : _connectionState == WebSocketConnectionState.closed ? Colors.red : Colors.green,
|
||
|
elevation: 0,
|
||
|
automaticallyImplyLeading: false,
|
||
|
flexibleSpace: SafeArea(
|
||
|
child: Container(
|
||
|
padding: EdgeInsets.only(right: 16),
|
||
|
child: Row(
|
||
|
children: <Widget>[
|
||
|
IconButton(
|
||
|
onPressed: (){
|
||
|
Navigator.pop(context);
|
||
|
},
|
||
|
icon: Icon(Icons.arrow_back,color: Colors.black,),
|
||
|
),
|
||
|
SizedBox(width: 2,),
|
||
|
CircleAvatar(
|
||
|
backgroundImage: NetworkImage("https://ehealth.eletsonline.com/wp-content/uploads/2020/12/pharma-industry-in-2021.jpg"),
|
||
|
maxRadius: 20,
|
||
|
),
|
||
|
SizedBox(width: 12,),
|
||
|
Expanded(
|
||
|
child: Column(
|
||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
children: <Widget>[
|
||
|
Text("Suresh",style: TextStyle( fontSize: 16 ,fontWeight: FontWeight.w600),),
|
||
|
// SizedBox(height: 6,),
|
||
|
// Text("Online",style: TextStyle(color: Colors.grey.shade600, fontSize: 13),),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
IconButton(
|
||
|
onPressed: () {
|
||
|
UrlLauncher.launch("tel://8328206298");
|
||
|
},
|
||
|
icon: Icon(Icons.call, color: Colors.black),
|
||
|
),
|
||
|
IconButton(
|
||
|
onPressed: () {
|
||
|
Navigator.push(
|
||
|
context,
|
||
|
MaterialPageRoute(
|
||
|
builder: (context) => IndexPage()),
|
||
|
);
|
||
|
},
|
||
|
icon: Icon(Icons.videocam, color: Colors.black),
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
body: Stack(
|
||
|
children: <Widget>[
|
||
|
ListView.builder(
|
||
|
controller: _scrollController,
|
||
|
itemCount: _messages.length,
|
||
|
shrinkWrap: true,
|
||
|
padding: EdgeInsets.only(top: 10,bottom: 60),
|
||
|
itemBuilder: (context, index){
|
||
|
bool isSentByMe = _messages[index].senderId == myUserId;
|
||
|
return Container(
|
||
|
padding: EdgeInsets.only(left: 14,right: 14,top: 10,bottom: 10),
|
||
|
child: Align(
|
||
|
alignment: (isSentByMe?Alignment.topRight:Alignment.topLeft),
|
||
|
child: Container(
|
||
|
decoration: BoxDecoration(
|
||
|
borderRadius: BorderRadius.circular(20),
|
||
|
color: (isSentByMe?Colors.blue[200]:Colors.grey[200]),
|
||
|
),
|
||
|
padding: EdgeInsets.symmetric(horizontal: 16,vertical: 10),
|
||
|
child: Text(_messages[index].messageContent, style: TextStyle(fontSize: 15),),
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
},
|
||
|
),
|
||
|
Align(
|
||
|
alignment: Alignment.bottomLeft,
|
||
|
child: Container(
|
||
|
padding: EdgeInsets.only(left: 10, bottom: 10, top: 10),
|
||
|
height: 60,
|
||
|
width: double.infinity,
|
||
|
color: Colors.white,
|
||
|
child: Row(
|
||
|
children: <Widget>[
|
||
|
GestureDetector(
|
||
|
onTap: () {
|
||
|
showModalBottomSheet<void>(
|
||
|
context: context,
|
||
|
builder: (BuildContext context) {
|
||
|
return SizedBox(
|
||
|
height: 200,
|
||
|
child: Center(
|
||
|
child: Row(
|
||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
children: <Widget>[
|
||
|
GestureDetector(
|
||
|
child: Icon(
|
||
|
Icons.camera_alt_outlined,
|
||
|
size: 100,
|
||
|
color: greyColor,
|
||
|
),
|
||
|
onTap: () async {
|
||
|
await takeImageFromCamera();
|
||
|
Navigator.pop(context);
|
||
|
},
|
||
|
),
|
||
|
SizedBox(
|
||
|
width:
|
||
|
MediaQuery.of(context).size.width * .20,
|
||
|
),
|
||
|
GestureDetector(
|
||
|
child: Icon(
|
||
|
Icons.photo,
|
||
|
size: 100,
|
||
|
color: greyColor,
|
||
|
),
|
||
|
onTap: () async {
|
||
|
await pickImageFromGallery();
|
||
|
Navigator.pop(context);
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
});
|
||
|
},
|
||
|
child: Container(
|
||
|
height: 30,
|
||
|
width: 30,
|
||
|
decoration: BoxDecoration(
|
||
|
color: Colors.lightBlue,
|
||
|
borderRadius: BorderRadius.circular(30),
|
||
|
),
|
||
|
child: Icon(
|
||
|
Icons.add,
|
||
|
color: Colors.white,
|
||
|
size: 20,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
SizedBox(
|
||
|
width: 15,
|
||
|
),
|
||
|
Expanded(
|
||
|
child: TextField(
|
||
|
controller: _controller,
|
||
|
decoration: InputDecoration(
|
||
|
hintText: "Write message...",
|
||
|
hintStyle: TextStyle(color: Colors.black54),
|
||
|
border: InputBorder.none),
|
||
|
),
|
||
|
),
|
||
|
SizedBox(
|
||
|
width: 15,
|
||
|
),
|
||
|
FloatingActionButton(
|
||
|
onPressed: () {
|
||
|
String newMessage = _controller.text;
|
||
|
if (newMessage.isNotEmpty) {
|
||
|
// Add the new message to the list
|
||
|
setState(() {
|
||
|
_sendMessage();
|
||
|
});
|
||
|
|
||
|
// Clear the text field
|
||
|
_controller.clear();
|
||
|
}
|
||
|
},
|
||
|
child: Icon(
|
||
|
Icons.send,
|
||
|
color: Colors.white,
|
||
|
size: 18,
|
||
|
),
|
||
|
backgroundColor: Colors.blue,
|
||
|
elevation: 0,
|
||
|
),
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
|
));
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void _sendMessage() {
|
||
|
if (_controller.text.isNotEmpty) {
|
||
|
var message = ChatMessage(
|
||
|
senderId: myUserId,
|
||
|
receiverId: otherUserId,
|
||
|
messageType: 'text',
|
||
|
messageContent: _controller.text,
|
||
|
);
|
||
|
channel.sink.add(chatMessageToJson(message));
|
||
|
_messages.add(message);
|
||
|
_controller.clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
@override
|
||
|
void initState() {
|
||
|
super.initState();
|
||
|
// Initialize the WebSocket channel for call signaling
|
||
|
callChannel = IOWebSocketChannel.connect(
|
||
|
"wss://socketsbay.com/wss/v2/1/demo/call", // adjust the URL accordingly
|
||
|
);
|
||
|
_scrollController = ScrollController();
|
||
|
channel.stream.listen((message) {
|
||
|
if (message is String) {
|
||
|
var receivedMessage = chatMessageFromJson(message);
|
||
|
setState(() {
|
||
|
_messages.add(receivedMessage);
|
||
|
});
|
||
|
_scrollToBottom();
|
||
|
}
|
||
|
}, onError: (error) {
|
||
|
setState(() {
|
||
|
_connectionState = WebSocketConnectionState.error;
|
||
|
});
|
||
|
},onDone: (){
|
||
|
setState(() {
|
||
|
_connectionState = WebSocketConnectionState.closed;
|
||
|
});
|
||
|
},);
|
||
|
}
|
||
|
|
||
|
|
||
|
// ... existing code ...
|
||
|
|
||
|
void startCall(String callId) {
|
||
|
// Send a call initiation message to the other user
|
||
|
var callMessage = {
|
||
|
'type': 'call',
|
||
|
'callId': callId,
|
||
|
'senderId': myUserId,
|
||
|
'receiverId': otherUserId,
|
||
|
};
|
||
|
callChannel.sink.add(jsonEncode(callMessage));
|
||
|
|
||
|
// Update UI or perform other actions as needed
|
||
|
// ...
|
||
|
|
||
|
// For simplicity, let's assume the call is accepted after a short delay
|
||
|
Future.delayed(Duration(seconds: 2), () {
|
||
|
// Handle call accepted
|
||
|
onCallAccepted(callId);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void onCallAccepted(String callId) {
|
||
|
// Update UI or perform other actions as needed
|
||
|
// ...
|
||
|
|
||
|
// Send a call accepted message to the other user
|
||
|
var acceptedMessage = {
|
||
|
'type': 'call_accepted',
|
||
|
'callId': callId,
|
||
|
'senderId': myUserId,
|
||
|
'receiverId': otherUserId,
|
||
|
};
|
||
|
callChannel.sink.add(jsonEncode(acceptedMessage));
|
||
|
|
||
|
// Start the actual call
|
||
|
// ...
|
||
|
}
|
||
|
|
||
|
void endCall(String callId) {
|
||
|
// Send a call end message to the other user
|
||
|
var endMessage = {
|
||
|
'type': 'call_end',
|
||
|
'callId': callId,
|
||
|
'senderId': myUserId,
|
||
|
'receiverId': otherUserId,
|
||
|
};
|
||
|
callChannel.sink.add(jsonEncode(endMessage));
|
||
|
|
||
|
// Update UI or perform other actions as needed
|
||
|
// ...
|
||
|
|
||
|
// For simplicity, let's assume the call is ended immediately
|
||
|
onCallEnded(callId);
|
||
|
}
|
||
|
|
||
|
void onCallEnded(String callId) {
|
||
|
// Update UI or perform other actions as needed
|
||
|
// ...
|
||
|
|
||
|
// Close the call WebSocket channel
|
||
|
callChannel.sink.close();
|
||
|
|
||
|
// For simplicity, let's assume the call ended immediately
|
||
|
// ...
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void _scrollToBottom() {
|
||
|
// Scroll to the bottom of the list
|
||
|
_scrollController.animateTo(
|
||
|
_scrollController.position.maxScrollExtent,
|
||
|
duration: Duration(milliseconds: 200),
|
||
|
curve: Curves.easeOut,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
@override
|
||
|
void dispose() {
|
||
|
channel.sink.close();
|
||
|
super.dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
enum WebSocketConnectionState { connecting, open, closing, closed, error }
|