Home > Back-end >  How to await the socket id on flutter client
How to await the socket id on flutter client

Time:07-20

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()

  • Related