0

I am developing an application in which user can contact each other via video calls. I have setup my server on railway by following the Agora documentation. If anyone has any suggestions or know what I am doing wrong please do let me know. I have tried leaving the token empty ('') but it still gives invalid token error. The token is getting generated successfully but when users join the call. Remote view is not showing up even though the onUserJoined callback is getting triggered perfectly on both caller and receiver side.

This is the call screen code which will enable users to contact with each other

// ignore_for_file: prefer_typing_uninitialized_variables, use_build_context_synchronously

import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../../constants/constants.dart';
import '../../global/firebase_ref.dart';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wakelock/wakelock.dart';

import '../../methods/call_methods.dart';
import '../../models/call_model.dart';
import '../../services/app_services.dart';
import '../../services/connectivity_services.dart';
import '../../services/user_services.dart';
import '../../widgets/custom_images.dart';
import '../../widgets/custom_widgets.dart';

class VideoCallScreen extends StatefulWidget {
  const VideoCallScreen(this.call, {Key? key}) : super(key: key);
  final CallModel call;

  @override
  State<VideoCallScreen> createState() => _VideoCallScreenState();
}

class _VideoCallScreenState extends State<VideoCallScreen> {
  final _users = <int>[];
  final _infoStrings = <String>[];
  bool muted = false;
  RtcEngine? _engine;
  bool isspeaker = true;
  bool isalreadyendedcall = false;
  String current = Get.find<UserServices>().adminid;
  CollectionReference? reference;
  String token = '';

  Stream<DocumentSnapshot>? stream;

  @override
  void dispose() {
    _users.clear();
    _engine!.leaveChannel();
    _engine!.release();
    super.dispose();
  }

  getToken() async {
    final url = Uri.parse(
      'https://agora-token-service-production-59a1.up.railway.app/rtc/${widget.call.channelid}/1/uid/0',
    );
    Get.log('Token URL $url');
    final response = await http.get(url);
    debugPrint('Response: $response');
    if (response.statusCode == 200) {
      setState(() {
        token = response.body;
        token = jsonDecode(token)['rtcToken'];
        Get.log('token: $token');
      });
    } else {
      Get.log('Failed to fetch the token');
    }
  }

  @override
  void initState() {
    initialize();
    super.initState();

    if (widget.call.by == current) {
      reference = userRef.doc(widget.call.receiver!.id).collection('History');
      stream = reference!.doc(widget.call.timeepoch.toString()).snapshots();
    } else {
      reference = adminRef.doc(widget.call.caller!.id).collection('History');
      stream = reference!.doc(widget.call.timeepoch.toString()).snapshots();
    }
  }

  Future<void> initialize() async {
    try {
      await [Permission.microphone, Permission.camera].request();
      await getToken();
      if (Get.find<AppServices>().appid.isEmpty) {
        setState(() {
          _infoStrings.add(
            'Agora_APP_IDD missing, please provide your Agora_APP_IDD in app_constant.dart',
          );
          _infoStrings.add('Agora Engine is not starting');
        });
        return;
      }

      await _initAgoraRtcEngine();
      _addAgoraEventHandlers();
      VideoEncoderConfiguration configuration = const VideoEncoderConfiguration(
        dimensions: VideoDimensions(height: 1920, width: 1080),
      );
      await _engine!.setVideoEncoderConfiguration(configuration);
      Get.log('Channel id: ${widget.call.channelid}');
      await _engine!.joinChannel(
        token: token,
        channelId: widget.call.channelid!,
        uid: 0,
        options: const ChannelMediaOptions(),
      );
    } catch (e) {
      Get.log('Catch: $e');
    }
  }

  Future<void> _initAgoraRtcEngine() async {
    _engine = createAgoraRtcEngine();
    await _engine!.initialize(
      RtcEngineContext(
        appId: Get.find<AppServices>().appid,
        channelProfile: ChannelProfileType.channelProfileCommunication,
      ),
    );
    // _engine = await RtcEngine.create(Get.find<AppServices>().agoraid);
    await _engine!.enableVideo();
    await _engine!.enableAudio();
    await _engine!.enableLocalVideo(true);
    await _engine!.enableLocalAudio(true);
    await _engine!.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
    Get.log('---engine----');
  }

  var remoteid;
  void _addAgoraEventHandlers() {
    _engine!.registerEventHandler(
      RtcEngineEventHandler(
        onError: (code, value) {
          setState(() {
            final info = 'onErrorCode: $code';
            _infoStrings.add(info);
            Get.log(info);
            final infp = 'onError: $value';
            _infoStrings.add(infp);
            Get.log(infp);
          });
        },
        onJoinChannelSuccess: (channel, elapsed) {
          setState(() {
            final info = 'onJoinChannel: $channel';
            _infoStrings.add(info);
            Get.log(info);
          });
          if (widget.call.caller!.id == current) {
            adminRef
                .doc(widget.call.caller!.id!)
                .collection('History')
                .doc(widget.call.timeepoch.toString())
                .set({
              'TYPE': 'OUTGOING',
              'ISVIDEOCALL': widget.call.video,
              'PEER': widget.call.receiver!.id,
              'TARGET': widget.call.receiver!.id,
              'TIME': widget.call.timeepoch,
              'DP': widget.call.receiver!.picture,
              'ISMUTED': false,
              'ISJOINEDEVER': false,
              'STATUS': 'calling',
              'STARTED': null,
              'ENDED': null,
              'CALLERNAME': widget.call.caller!.name,
              'CHANNEL': channel.channelId,
              'UID': channel.localUid,
            }, SetOptions(merge: true)).then(
              (value) => Get.log('added'),
            );
            userRef
                .doc(widget.call.receiver!.id!)
                .collection('History')
                .doc(widget.call.timeepoch.toString())
                .set({
              'TYPE': 'INCOMING',
              'ISVIDEOCALL': widget.call.video,
              'PEER': widget.call.caller!.id,
              'TARGET': widget.call.receiver!.id,
              'TIME': widget.call.timeepoch,
              'DP': widget.call.caller!.picture,
              'ISMUTED': false,
              'ISJOINEDEVER': true,
              'STATUS': 'missedcall',
              'STARTED': null,
              'ENDED': null,
              'CALLERNAME': widget.call.caller!.name,
              'CHANNEL': channel.channelId,
              'UID': channel.localUid,
            }, SetOptions(merge: true));
          }
          Wakelock.enable();
        },
        onLeaveChannel: (connection, stats) {
          // setState(() {
          _infoStrings.add('onLeaveChannel');
          _users.clear();
          // });
          if (isalreadyendedcall == false) {
            adminRef
                .doc(widget.call.caller!.id!)
                .collection("History")
                .doc(widget.call.timeepoch.toString())
                .set({
              'STATUS': 'ended',
              'ENDED': DateTime.now(),
              'ISMUTED': false,
              'UID': -1,
            }, SetOptions(merge: true));
            userRef
                .doc(widget.call.receiver!.id!)
                .collection('History')
                .doc(widget.call.timeepoch.toString())
                .set({
              'STATUS': 'ended',
              'ENDED': DateTime.now(),
              'ISMUTED': false,
              'UID': -1,
            }, SetOptions(merge: true));
            // //----------
            // userRef
            //     .doc(widget.call.receiver!.id)
            //     .collection('recent')
            //     .doc(widget.call.id)
            //     .set({
            //   'id': widget.call.caller!.id,
            //   'ENDED': DateTime.now().millisecondsSinceEpoch,
            //   'CALLERNAME': widget.call.receiver!.name,
            // }, SetOptions(merge: true));
          }
          Wakelock.disable();
        },
        onUserJoined: (connection, uid, elapsed) {
          setState(() {
            final info = 'userJoined: $uid';
            _infoStrings.add(info);
            _users.add(uid);
            Get.log(info);
            remoteid = uid;
            Get.log(remoteid);
          });
          startTimerNow();
          if (Get.find<UserServices>().adminid == widget.call.caller!.id) {
            adminRef
                .doc(widget.call.caller!.id!)
                .collection('History')
                .doc(widget.call.timeepoch.toString())
                .set({
              'STARTED': DateTime.now(),
              'STATUS': 'pickedup',
              'ISJOINEDEVER': true,
            }, SetOptions(merge: true));
            userRef
                .doc(widget.call.receiver!.id!)
                .collection('History')
                .doc(widget.call.timeepoch.toString())
                .set({
              'STARTED': DateTime.now(),
              'STATUS': 'pickedup',
            }, SetOptions(merge: true));
          }
          Wakelock.enable();
        },
        onUserOffline: (connection, uid, elapsed) {
          setState(() {
            final info = 'userOffline: $uid';
            _infoStrings.add(info);
            _users.remove(uid);
            Get.log(info);
            remoteid = null;
          });
          if (isalreadyendedcall == false) {
            adminRef
                .doc(widget.call.caller!.id!)
                .collection('History')
                .doc(widget.call.timeepoch.toString())
                .set({
              'STATUS': 'ended',
              'ENDED': DateTime.now(),
              'ISMUTED': false,
              'UID': -1,
            }, SetOptions(merge: true));
            userRef
                .doc(widget.call.receiver!.id!)
                .collection('History')
                .doc(widget.call.timeepoch.toString())
                .set({
              'STATUS': 'ended',
              'ENDED': DateTime.now(),
              'ISMUTED': false,
              'UID': -1,
            }, SetOptions(merge: true));
            //----------
            // userRef
            //     .doc(widget.call.receiver!.id)
            //     .collection('recent')
            //     .doc(widget.call.id)
            //     .set({
            //   'id': widget.call.caller!.id,
            //   'ENDED': DateTime.now().millisecondsSinceEpoch,
            //   'CALLERNAME': widget.call.receiver!.name,
            // }, SetOptions(merge: true));
          }
        },
        onFirstRemoteVideoFrame: (connection, uid, width, height, elapsed) {
          setState(() {
            final info = 'firstRemoteVideo: $uid ${width}x $height';
            _infoStrings.add(info);
            Get.log(info);
          });
        },
        onTokenPrivilegeWillExpire: (connection, string) async {
          await getToken();
          await _engine!.renewToken(token);
        },
      ),
    );
  }

  void onCallEnd(BuildContext context) async {
    await CallMethods.endCall(call: widget.call);
    DateTime now = DateTime.now();
    if (isalreadyendedcall == false) {
      await adminRef
          .doc(widget.call.caller!.id!)
          .collection('History')
          .doc(widget.call.timeepoch.toString())
          .set({
        'STATUS': 'ended',
        'ENDED': now,
        'ISMUTED': false,
        "UID": -1,
      }, SetOptions(merge: true));
      await userRef
          .doc(widget.call.receiver!.id!)
          .collection('History')
          .doc(widget.call.timeepoch.toString())
          .set({
        'STATUS': 'ended',
        'ENDED': now,
        'ISMUTED': false,
        'UID': -1,
      }, SetOptions(merge: true));
      // //----------
      // userRef
      //     .doc(widget.call.receiver!.id)
      //     .collection('recent')
      //     .doc(widget.call.id)
      //     .set({
      //   'id': widget.call.caller!.id,
      //   'ENDED': DateTime.now().millisecondsSinceEpoch,
      //   'CALLERNAME': widget.call.receiver!.name,
      // }, SetOptions(merge: true));
    }
    Wakelock.disable();
    Navigator.pop(context);
  }

  Widget callView({
    String status = 'calling',
    bool muted = false,
    int? remoteuid,
  }) {
    var w = MediaQuery.of(context).size.width;
    var h = MediaQuery.of(context).size.height;
    return Container(
      alignment: Alignment.center,
      decoration: status == 'pickedup'
          ? null
          : BoxDecoration(
              image: DecorationImage(
                fit: BoxFit.cover,
                image: providerImage(
                  widget.call.caller!.id == current
                      ? widget.call.receiver!.picture ?? ''
                      : widget.call.caller!.picture ?? '',
                ),
              ),
            ),
      child: Container(
        color: status == 'pickedup' ? null : Colors.white.withOpacity(0.3),
        child: Stack(
          alignment: Alignment.center,
          children: [
            status != 'pickedup'
                ? Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: [
                      Container(
                        width: w,
                        height: h / 5,
                        alignment: Alignment.center,
                        margin: EdgeInsets.only(
                            top: MediaQuery.of(context).padding.top),
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: const [
                                Icon(
                                  Icons.lock_rounded,
                                  size: 17,
                                  color: Colors.white38,
                                ),
                                SizedBox(width: 6),
                                Text(
                                  'End-to-end encrypted',
                                  style: TextStyle(
                                    color: Colors.white38,
                                    fontWeight: FontWeight.w400,
                                    fontFamily: AppStrings.opensans,
                                  ),
                                ),
                              ],
                            ).marginOnly(top: 50, bottom: 7),
                            SizedBox(
                              width: w / 1.1,
                              child: Text(
                                widget.call.caller!.id ==
                                        Get.find<UserServices>().adminid
                                    ? widget.call.receiver!.name!
                                    : widget.call.caller!.name!,
                                maxLines: 1,
                                overflow: TextOverflow.ellipsis,
                                textAlign: TextAlign.center,
                                style: const TextStyle(
                                  fontWeight: FontWeight.w500,
                                  fontSize: 27,
                                  fontFamily: AppStrings.opensans,
                                ),
                              ),
                            ),
                          ],
                        ),
                      ),
                      Text(
                        status == 'calling'
                            ? widget.call.receiver!.id ==
                                    Get.find<UserServices>().adminid
                                ? 'Connecting...'
                                : 'Calling...'
                            : status == 'pickedup'
                                ? '$hoursStr : $minutesStr: $secondsStr'
                                : status == 'ended'
                                    ? 'Call Ended'
                                    : status == 'rejected'
                                        ? 'Rejected'
                                        : 'Please wait...',
                        style: const TextStyle(
                          fontWeight: FontWeight.w500,
                          fontSize: 18,
                          fontFamily: AppStrings.opensans,
                        ),
                      ).marginOnly(bottom: 16, top: 10),
                      Stack(
                        children: [
                          widget.call.caller!.id ==
                                  Get.find<UserServices>().adminid
                              ? status == 'ended' || status == 'rejected'
                                  ? Container(
                                      height: w + (w / 11),
                                      width: w,
                                      color: Colors.white12,
                                      child: Icon(
                                        status == 'ended'
                                            ? Icons.person_off
                                            : status == 'rejected'
                                                ? Icons.call_end_rounded
                                                : Icons.person,
                                        size: 140,
                                      ),
                                    )
                                  : Container()
                              : status == 'ended' || status == 'rejected'
                                  ? Container(
                                      height: w + (w / 11),
                                      width: w,
                                      color: Colors.white12,
                                      child: Icon(
                                        status == 'ended'
                                            ? Icons.person_off
                                            : status == 'rejected'
                                                ? Icons.call_end_rounded
                                                : Icons.person,
                                        size: 140,
                                      ),
                                    )
                                  : Container(),
                          Positioned(
                            bottom: 20,
                            child: SizedBox(
                              width: w,
                              height: 20,
                              child: Center(
                                child: status == 'pickedup'
                                    ? muted == true
                                        ? const Text(
                                            'Muted',
                                            textAlign: TextAlign.center,
                                            style: TextStyle(
                                              fontWeight: FontWeight.w600,
                                              fontSize: 16,
                                              fontFamily: AppStrings.opensans,
                                            ),
                                          )
                                        : const SizedBox(height: 0)
                                    : const SizedBox(height: 0),
                              ),
                            ),
                          ),
                        ],
                      ),
                      SizedBox(height: h / 6),
                    ],
                  )
                : _engine == null
                    ? SizedBox()
                    : SizedBox(
                        child: AgoraVideoView(
                          controller: VideoViewController.remote(
                            rtcEngine: _engine!,
                            canvas: VideoCanvas(uid: remoteuid),
                            connection: RtcConnection(
                              channelId: widget.call.channelid!,
                            ),
                          ),
                        ),
                      ),
            if (status == 'pickedup')
              Positioned(
                top: 150,
                child: Text(
                  '$hoursStr: $minutesStr: $secondsStr',
                  style: const TextStyle(
                    fontWeight: FontWeight.w500,
                    fontSize: 18,
                    color: Colors.white,
                    fontFamily: AppStrings.opensans,
                  ),
                ),
              ),
            if (status != 'ended' || status != 'rejected')
              _engine == null
                  ? SizedBox()
                  : Align(
                      alignment: Alignment.bottomRight,
                      child: SizedBox(
                        width: 200,
                        height: 200,
                        child: AgoraVideoView(
                          controller: VideoViewController(
                            rtcEngine: _engine!,
                            canvas: const VideoCanvas(uid: 0),
                          ),
                        ),
                      ),
                    ),
          ],
        ),
      ),
    );
  }

  onToggleMute() {
    setState(() {
      muted = !muted;
    });
    _engine!.muteLocalAudioStream(muted);
    reference!
        .doc(widget.call.timeepoch.toString())
        .set({'ISMUTED': muted}, SetOptions(merge: true));
  }

  onSwitchCamera() => setState(() => _engine!.switchCamera());

  Widget toolbar({String status = 'calling'}) {
    return Container(
      alignment: Alignment.bottomCenter,
      padding: const EdgeInsets.symmetric(vertical: 35),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          status != 'ended' && status != 'rejected'
              ? SizedBox(
                  width: 65.67,
                  child: RawMaterialButton(
                    onPressed: onToggleMute,
                    shape: const CircleBorder(),
                    elevation: 2.0,
                    fillColor: muted ? Colors.blueAccent : Colors.white,
                    padding: const EdgeInsets.all(12.0),
                    child: Icon(
                      muted ? Icons.mic_off : Icons.mic,
                      color: muted ? Colors.white : Colors.blueAccent,
                      size: 22.0,
                    ),
                  ),
                )
              : const SizedBox(height: 42, width: 65.67),
          SizedBox(
            width: 65.67,
            child: RawMaterialButton(
              onPressed: () async {
                Get.log('--on call end---');
                setState(() {
                  isalreadyendedcall =
                      status == 'ended' || status == 'rejected' ? true : false;
                  onCallEnd(context);
                });
              },
              shape: const CircleBorder(),
              elevation: 2.0,
              fillColor: status == 'ended' || status == 'rejected'
                  ? Colors.black
                  : Colors.redAccent,
              padding: const EdgeInsets.all(15.0),
              child: Icon(
                status == 'ended' || status == 'rejected'
                    ? Icons.close
                    : Icons.call,
                color: Colors.white,
                size: 35.0,
              ),
            ),
          ),
          status == 'ended' || status == 'rejected'
              ? const SizedBox(width: 65.67)
              : SizedBox(
                  width: 65.67,
                  child: RawMaterialButton(
                    onPressed: onSwitchCamera,
                    shape: const CircleBorder(),
                    elevation: 2.0,
                    fillColor: Colors.white,
                    padding: const EdgeInsets.all(12.0),
                    child: const Icon(
                      Icons.switch_camera,
                      color: Colors.blueAccent,
                      size: 20.0,
                    ),
                  ),
                ),
        ],
      ),
    );
  }

  Widget panel() {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 48),
      alignment: Alignment.bottomCenter,
      child: FractionallySizedBox(
        heightFactor: 0.5,
        child: Container(
          padding: const EdgeInsets.symmetric(vertical: 48),
          child: ListView.builder(
            reverse: true,
            itemCount: _infoStrings.length,
            itemBuilder: (BuildContext context, int index) {
              if (_infoStrings.isEmpty) return const SizedBox();
              return Padding(
                padding:
                    const EdgeInsets.symmetric(vertical: 3, horizontal: 10),
                child: Text(_infoStrings[index]),
              );
            },
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Obx(
      () => Get.find<ConnectivityService>().connectionStatus.value ==
              ConnectivityResult.none
          ? const DisconnectedWidget()
          : Scaffold(
              body: Stack(
                children: [
                  _engine == null
                      ? Center(
                          child: Stack(
                            children: [callView(), panel(), toolbar()],
                          ),
                        )
                      : StreamBuilder<DocumentSnapshot>(
                          stream: stream as Stream<DocumentSnapshot>,
                          builder: (context, snapshot) {
                            if (snapshot.hasData &&
                                snapshot.data!.data() != null &&
                                snapshot.data != null) {
                              var doc = snapshot.data!;
                              Get.log(doc.toString());
                              return Center(
                                child: Stack(
                                  children: [
                                    callView(
                                      status: doc['STATUS'],
                                      muted: doc['ISMUTED'],
                                      remoteuid: doc['UID'],
                                    ),
                                    panel(),
                                    toolbar(status: doc['STATUS']),
                                  ],
                                ),
                              );
                            }
                            return Center(
                              child: Stack(
                                children: [callView(), panel(), toolbar()],
                              ),
                            );
                          },
                        ),
                ],
              ),
            ),
    );
  }
}
Ayesha Iftikhar
  • 358
  • 3
  • 13
  • the remote view is not showing up on either caller or receiver side but sometimes shows up a blank screen or call ends automatically without any errors. – Ayesha Iftikhar Feb 03 '23 at 05:12

1 Answers1

1

I didn't set it up with a rails server but it should work the same. On the flutter side im calling a function at the very first point:

final token = await createToken(channelName, userId);

channel name to identify the channel for the users and a userId of my user who should be able to join the channel.

  Future<dynamic> createToken(String channelName, int uid) async {
    try {
      //404
      // final response = await dio.get('${url}/api/video/create-token?agChannelName=$channelName&agRole=$role&agUid=$uid&agExpireTime=$expireTime');

      final response = await dio.get(
          '${url}/api/video/create-token?agChannelName=$channelName&agUid=$uid');
      print('res is ${response.data["token"]}');
      return response.data["token"];
    } on DioError catch (e) {
      // The request was made and the server responded with a status code
      // that falls out of the range of 2xx and is also not 304.
      if (e.response != null) {
        //print(HttpException(e.response!.data["message"]));
        return e.response!.data;
        //print(e.response!.headers);
        //print(e.response!.requestOptions);
      } else {
        // Something happened in setting up or sending the request that triggered an Error
        //print(e.requestOptions);
        print('get events:  ${e.message}');
      }
    }
  }

On my server side where im using a javascript framework, im doing the following:

...

  const token = RtcTokenBuilder.buildTokenWithUid(
    process.env.AGORA_APP_ID,
    process.env.AGORA_APP_CERTIFICATE,
    channelName,
    uid,
    RtcRole.PUBLISHER,
    privilegeExpireTime
  );
  console.log(token)
  return res.status(201).json({ token: token });

For that im using the agora-access-token library on npm https://www.npmjs.com/package/agora-access-token

Marcel Dz
  • 2,321
  • 4
  • 14
  • 49
  • that's done and working fine. I have to downgrade Agora_rtc_engine version though. But the remote view is not showing up on either caller or receiver side but sometimes shows up a blank screen or call ends automatically without any errors. – Ayesha Iftikhar Feb 03 '23 at 05:12
  • great, i assume youre testing with a simulator? you cant just refresh and try to connect again. insert a button which disconnects the user. If you press that button before you will refresh your app code, you can also test and work with the simulator. if you forget to do so you have to completely close and reopen the emulator. i know its annoying. – Marcel Dz Feb 03 '23 at 12:47
  • and in the very first step make sure with the logs, that your events are fired and the remote user is really joining the channel – Marcel Dz Feb 03 '23 at 12:49
  • I have tried it on both simulators, emulators and real devices but still, it shows a black screen for remote users after they join. I have checked with logs multiple times but the remote user is joining the channel successfully. – Ayesha Iftikhar Feb 04 '23 at 13:10
  • which version do you use? i can confirm its working for me on 5.3.1. also i see your widget looks different to mine. mine is called: RtcRemoteView.SurfaceView( uid: state.remote, renderMode: VideoRenderMode.Fit, channelId: channelName, ) – Marcel Dz Feb 04 '23 at 22:52
  • I am using 6.0.0 and after downgrading to 5.3.1 it still gives the same issues – Ayesha Iftikhar Feb 06 '23 at 05:21
  • I am facing the same issue. Did you solve it? – Hadi Khan Feb 26 '23 at 18:16
  • I downgrade it to 5.3.2 and then it worked. – Ayesha Iftikhar Mar 03 '23 at 13:28
  • 1
    nice to hear that it works for you now! – Marcel Dz Mar 03 '23 at 21:16