I successfully implemented socketio on my clientside in flutter. For the backend im using a really basic node.js server. In my root main.dart
im calling socket.connect()
initially, then im logging in a mockuser to my app with the given socket.id
. Everything just works fine. Now when I call my signout()
function, the mock user is logging out and the function calls socket.disconnect()
, this also works fine. Now when I want so signin()
calling my signin function, im also calling socket.connect()
this time, because its not initially connecting anymore from my main.dart
, but im not able to pass the socket.id
to my mockuser, since there is no way for me atm how I can await the connection to the socket server and WAIT for the socketid
.
my socket
class SocketRepository {
late Socket socket;
Auth auth = Auth.instance;
static final SocketRepository _socketRepo = SocketRepository._internal();
factory SocketRepository() {
return _socketRepo;
}
SocketRepository._internal() {
try {
socket = io('http://127.0.0.1:3000', <String, dynamic>{
'transports': ['websocket'],
'autoConnect': false,
});
socket.on('connect', (_) {
print(socket.id);
});
//! Error
socket.on("error", (data) {
/// You could call the one rror from the chat bloc here (if any)
});
//? Disconnected
socket.on('disconnect', (_) {
// Do some cleanup if needed
});
} catch (e) {
/// You could call the one rror from the chat bloc here (if any)
rethrow;
}
}
void connect() {
socket.connect();
}
void disconnect() {
socket.disconnect();
}
}
user repository calling signin, signout (Here i cant await the socket id in my signin)
class UserRepository {
Auth auth = Auth.instance;
//UserRepository({required this.auth});
// sign in with username
//TODO: change hardcoded username to email and passwort later on
Future<User> signIn() async {
try{
await Future.delayed(Duration(seconds: 1));
if(SocketRepository().socket.id == null){
print('connecting to socket again');
SocketRepository().connect();
await Future.delayed(Duration(seconds: 3));
}
//print('connectedId : ${connectedId!}');
final socketId = auth.currentUser.socketId;
var finalUser = User(id: 1, socketId: SocketRepository().socket.id, userName: 'Logged in User');
auth.currentUser = finalUser;
print('signed id with credentials: ${finalUser}');
return auth.currentUser;
}catch(e){
print(e.toString());
throw e;
}
}
//TODO: change hardcoded username to email and passwort later
Future<User> signOut() async {
try{
await Future.delayed(Duration(seconds: 1));
var credentials = User.empty;
auth.currentUser = credentials;
SocketRepository().disconnect();
print('signed out with credentials: ${auth.currentUser}');
return auth.currentUser;
}catch(e){
print(e.toString());
throw e;
}
}
}
calling socket.connect()
in my main.dart
initially which works fine
void main() {
SocketRepository().connect();
BlocOverrides.runZoned(
() {
runApp(const MyApp());
},
blocObserver: SimpleBlocObserver(),
);
}
EDIT: By adding a hardcoded delay to my signin()
function I can make it work actually. But i'm searching for a better approach..
Future<Null> delay(int milliseconds) {
return new Future.delayed(new Duration(milliseconds: milliseconds));
}
// sign in with username
//TODO: change hardcoded username to email and passwort later on
Future<User> signIn() async {
try{
await Future.delayed(Duration(seconds: 1));
if(SocketRepository().socket.id == null){
print('connecting to socket again');
SocketRepository().connect();
await delay(1000);
if(auth.socketId == null){
await delay(4000);
}
}
//print('connectedId : ${connectedId!}');
final socketId = auth.socketId;
var finalUser = User(id: 1, socketId: socketId, userName: 'Logged in User');
auth.currentUser = finalUser;
print('signed id with credentials: ${finalUser}');
return auth.currentUser;
}catch(e){
print(e.toString());
throw e;
}
}
EDIT with the help of Pierre Monier
Signin()
function awaiting the getter socketid
Future<User> signIn() async {
try {
await Future.delayed(Duration(seconds: 1));
if (socketRepo.socket.id == null) {
socketRepo.connect();
final connectionId = await socketRepo.socketId;
}
auth.currentUser = User(id: 1, socketId: auth.socketId, userName: 'Logged in User'); print('signed id with credentials: ${auth.currentUser}');
return auth.currentUser;
} catch (e) {
print(e.toString());
throw e;
}
}
Socketrepository
class SocketRepository {
late Socket socket;
Auth auth = Auth.instance;
Completer<String?> _socketIdCompleter = Completer();
Future<String?> get socketId => _socketIdCompleter.future;
static final SocketRepository _socketRepo = SocketRepository._internal();
factory SocketRepository() {
return _socketRepo;
}
SocketRepository._internal() {
try {
socket = io('http://127.0.0.1:3000', <String, dynamic>{
'transports': ['websocket'],
'autoConnect': false,
});
socket.on('connect', (_) {
print(socket.id);
});
//? Reconnected
/// Just in case user gets disconnected from server we reconnect ans send
/// the same payload
socket.on('reconnect', (_) {
auth.socketId = socket.id;
});
//! Error
/// You could call the one rror from the chat bloc here (if any)
socket.on("error", (data) => _socketIdCompleter.completeError(data));
//? Disconnected
socket.on('disconnect', (_) => '');
//? on connected
socket.onConnect((_) {
auth.socketId = socket.id;
_socketIdCompleter.complete(socket.id);
});
} catch (e) {
/// You could call the one rror from the chat bloc here (if any)
rethrow;
}
}
void connect() {
//streamSocket = StreamSocket();
if(_socketIdCompleter.isCompleted == true) _socketIdCompleter = Completer();
socket.connect();
}
void disconnect() {
//streamSocket.dispose();
socket.disconnect();
if(_socketIdCompleter.isCompleted == false) _socketIdCompleter.complete();
}
CodePudding user response:
I think this is a good use case for a Completer.
Basically it's a object that can produce Future
. In your SocketRepository
you can add a property named _socketIdCompleter
of type Completer<Type of socket.id>
.
Then you can add a getter method like this
Future<Type of socket.id> get socketId => _socketIdCompleter.future
Finally you just have to change your callback method for something like this
socket.on('connect', (_) {
_socketIdCompleter.complete(socket.id);
});
//! Error
socket.on("error", (data) {
_socketIdCompleter.completeError(data);
});
This should do the job, you can just await for the getter socketId
to complete, after calling SocketRepository().connect()