Как реализовать голосовой вызов с помощью agora с flutter?

#android #flutter

Вопрос:

Я пытался реализовать голосовые вызовы с помощью Agora с помощью flutter, но столкнулся с проблемой, не имея возможности передавать голос. Если кто-нибудь реализовал это, пожалуйста, поделитесь кодом.

Вот мой код:

 import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_ringtone_player/flutter_ringtone_player.dart';
import 'package:hys/Calling/callingfeedback.dart';
import 'package:hys/database/crud.dart';
import 'package:hys/database/questionDB.dart';
import '../navBar.dart';
import '../utils/AppID.dart';
import 'package:agora_rtc_engine/rtc_engine.dart';
import 'package:agora_rtc_engine/rtc_local_view.dart' as RtcLocalView;
import 'package:agora_rtc_engine/rtc_remote_view.dart' as RtcRemoteView;
import 'package:firebase_database/firebase_database.dart';
import 'package:readmore/readmore.dart';
import 'package:flutter_tex/flutter_tex.dart';
import 'package:flutter_placeholder_textlines/flutter_placeholder_textlines.dart';

//Audio call page
class AudioCallPage extends StatefulWidget {
  final String channelName;
  final String questioned;
  final String userid;
  const AudioCallPage({Key key, this.channelName, this.questionid, this.userid})
      : super(key: key);
  @override
  _AudioCallPageState createState() => _AudioCallPageState();
}

class _AudioCallPageState extends State<AudioCallPage> {
  static final _users = <int>[];
  final _infoStrings = <String>[];
  bool muted = false;
  RtcEngine _engine;
  DataSnapshot callData;
  final databaseReference = FirebaseDatabase.instance.reference();
  String _currentUserId = FirebaseAuth.instance.currentUser.uid;
  DocumentSnapshot questionDetails;
  QuerySnapshot callLogs;
  CrudMethods crudobj = CrudMethods();
  QuestionDB qDB = QuestionDB();

  @override
  void dispose() {
    // clear users
    _users.clear();
    // destroy SDK
    _engine.leaveChannel();
    _engine.destroy();
    super.dispose();
  }

   @override
  void initState() {
qDB.getQuestionByID(widget.questionid).then((value) {
  setState(() {
    questionDetails = value;
  });
});
crudobj.getCallLogs().then((value) {
  setState(() {
    callLogs = value;
  });
});
databaseReference
    .child("hys_calling_data")
    .child("usercallstatus")
    .child(_currentUserId)
    .set({"callstatus": true});
super.initState();
// initialize agora sdk
initialize();
}

Future<void> initialize() async {
   if (appID.isEmpty) {
     setState(() {
      _infoStrings.add(
       'APP_ID missing, please provide your APP_ID in settings.dart',
      );
      _infoStrings.add('Agora Engine is not starting');
    });
    return;
  }
  await _initAgoraRtcEngine();
  _addAgoraEventHandlers();
  // await _engine.enableWebSdkInteroperability(true);
  await _engine.joinChannel(null, widget.channelName, null, 0);
}

 Future<void> _initAgoraRtcEngine() async {
  RtcEngineConfig config = RtcEngineConfig(appID); 
 _engine = await RtcEngine.createWithConfig(config);
  await _engine.disableVideo();
  await _engine.enableAudio();
  } 

  void _addAgoraEventHandlers() {
  _engine.setEventHandler(RtcEngineEventHandler(
    error: (code) {
      setState(() {
        final info = 'onError: $code';
        _infoStrings.add(info);
      });
    },
    joinChannelSuccess: (channel, uid, elapsed) {
      setState(() {
        final info = 'onJoinChannel: $channel, uid: $uid';
        _infoStrings.add(info);
      });
    },
    leaveChannel: (stats) {
      setState(() {
        _infoStrings.add('onLeaveChannel');
        _users.clear();
      });
     },
     userJoined: (uid, elapsed) {
     setState(() {
       final info = 'userJoined: $uid';
        _infoStrings.add(info);
        _users.add(uid);
      });
    },
     userOffline: (uid, reason) {
      setState(() {
        final info = 'userOffline: $uid , reason: $reason';
        _infoStrings.add(info);
      _users.remove(uid);
    });
    },
    firstRemoteVideoFrame: (uid, width, height, elapsed) {
      setState(() {
        final info = 'firstRemoteVideoFrame: $uid';
        _infoStrings.add(info);
      });
    },
  ));
   }

  _questionExpandable() {
     return widget.questionid != ""
      ? InkWell(
          onTap: () {
            _showquestionDialogBox();
          },
          child: Container(
            padding: EdgeInsets.all(10),
            decoration: BoxDecoration(
                color: Colors.white, borderRadius: BorderRadius.circular(40)),
          child: Text("View Question",
                style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600)),
          ),
        )
     : SizedBox();
  } 

 void _showquestionDialogBox() {
   AlertDialog alertDialog = AlertDialog(
    backgroundColor: Color.fromRGBO(242, 246, 248, 1),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
   content: Container(
        height: 220,
      width: 300,
        padding: EdgeInsets.all(10),
       decoration: BoxDecoration(
           color: Color.fromRGBO(242, 246, 248, 1),
           borderRadius: BorderRadius.all(Radius.circular(20))),
        child: Column(
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
            questionDetails.get("questiontype") == "text"
                ? ReadMoreText(
                    questionDetails.get("question"),
                   textAlign: TextAlign.left,
                  trimLines: 4,
                  colorClickableText: Color(0xff0962ff),
                  trimMode: TrimMode.Line,
                  trimCollapsedText: 'read more',
                  trimExpandedText: 'Show less',
                  style: TextStyle(
                    fontFamily: 'Nunito Sans',
                    fontSize: 14,
                    color: Color.fromRGBO(0, 0, 0, 0.8),
                    fontWeight: FontWeight.w400,
                  ),
                  lessStyle: TextStyle(
                    fontFamily: 'Nunito Sans',
                    fontSize: 12,
                    color: Color(0xff0962ff),
                    fontWeight: FontWeight.w700,
                  ),
                  moreStyle: TextStyle(
                    fontFamily: 'Nunito Sans',
                    fontSize: 12,
                    color: Color(0xff0962ff),
                    fontWeight: FontWeight.w700,
                  ),
                )
              : questionDetails.get("questiontype") == "ocr"
                  ? Container(
                      margin: EdgeInsets.only(top: 5, left: 10, right: 10),
                      child: SizedBox(
                        child: TeXView(
                          loadingWidgetBuilder: (context) {
                            return Container(
                              width:
                                  MediaQuery.of(context).size.width / 1.31,
                              child: PlaceholderLines(
                                count: 2,
                                animate: true,
                                color: Colors.white,
                              ),
                            );
                          },
                          child: TeXViewColumn(children: [
                            TeXViewInkWell(
                              id: "0",
                              child: TeXViewDocument(
                                  questionDetails.get("question"),
                                  style: TeXViewStyle(
                                    fontStyle: TeXViewFontStyle(
                                        fontFamily: 'Nunito Sans',
                                        fontWeight: TeXViewFontWeight.w400,
                                        fontSize: 9,
                                        sizeUnit: TeXViewSizeUnit.Pt),
                                    padding: TeXViewPadding.all(5),
                                  )),
                            ),
                          ]),
                          style: TeXViewStyle(
                            elevation: 10,
                            backgroundColor:
                                Color.fromRGBO(242, 246, 248, 1),
                          ),
                        ),
                      ),
                    )
                  : SizedBox(),
         ],
      )),
  );
 showDialog(context: context, builder: (_) => alertDialog);
} 

  Widget _toolbar() {
    return Container(
     // alignment: Alignment.bottomCenter,
    height: MediaQuery.of(context).size.height,
  // padding: const EdgeInsets.symmetric(vertical: 48),
  child: Column(
    mainAxisAlignment: MainAxisAlignment.end,
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          RawMaterialButton(
            onPressed: _onToggleMute,
            child: Icon(
              muted ? Icons.mic_off : Icons.mic,
              color: muted ? Colors.white : Colors.blueAccent,
              size: 20.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: muted ? Colors.blueAccent : Colors.white,
            padding: const EdgeInsets.all(12.0),
          ),
          RawMaterialButton(
            onPressed: () => _onCallEnd(context),
            child: Icon(
              Icons.call_end,
              color: Colors.white,
              size: 35.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: Colors.redAccent,
            padding: const EdgeInsets.all(15.0),
          ),
          RawMaterialButton(
            onPressed: _onSwitchCamera,
            child: Icon(
              Icons.switch_camera,
              color: Colors.blueAccent,
              size: 20.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: Colors.white,
            padding: const EdgeInsets.all(12.0),
          )
        ],
      ),
      SizedBox(
        height: 10,
      ),
      _questionExpandable(),
      SizedBox(
        height: 10,
       ),
       ],
     ),
   );
  }

  @override
  Widget build(BuildContext context) {
   return WillPopScope(
    onWillPop: () {
       return new Future(() => false);
    },
     child: Scaffold(backgroundColor: Colors.black, body: _body()));
   }

  _body() {
    databaseReference
    .child("hys_calling_data")
    .child("sm_calls")
    .once()
    .then((DataSnapshot snapshot) {
    setState(() {
    if (mounted) {
        setState(() {
        callData = snapshot;
        });
      }
     });
   });
     if ((callData != null) amp;amp; (questionDetails != null) amp;amp; (callLogs != null)) {
     if ((callData.value[widget.channelName]["iscallcancelledafterReceived"] ==
         true)) {
        FlutterRingtonePlayer.stop();
        // _users.clear();
        // // destroy SDK
         // _engine.leaveChannel();
        // _engine.destroy();
         for (int i = 0; i < callLogs.docs.length; i  ) {
         if (widget.channelName == callLogs.docs[i].id) {
          int timecount = DateTime.now()
              .difference(DateTime.parse(callLogs.docs[i].get("starttime")))
            .inSeconds;
           crudobj.updateCallLogs(widget.channelName, {
            "callstatus": "received",
           "endtime": DateTime.now().toString(),
           "duration": timecount
          } );
         }  
        }
           databaseReference
             .child("hys_calling_data")
             .child("sm_calls")
             .child(widget.channelName)
             .update({
        'iscallreceivedbyReceiver': false,
         'iscallcancelledbycaller': false,
         'iscallrejected': false,
         "message": "NO"
       });
         databaseReference
          .child("hys_calling_data")
          .child("usercallstatus")
         .child(_currentUserId)
          .set({"callstatus": false});

       WidgetsBinding.instance.addPostFrameCallback((_) {
         Navigator.of(context).pop();
         if ((widget.questionid != "")) {
           Navigator.push(
            context,
              MaterialPageRoute(
                 builder: (context) =>
                     CallingFeedBack(widget.questionid, widget.userid)));
          }  else {
         crudobj.addUserCreditPointsData(
            3, "Answered", current_date, comparedate);
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => BottomNavigationBarWidget()));
      }
       });
       }
       return Center(
        child: Stack(
           children: <Widget>[
         _viewRows(),
        _toolbar(),
       ],
     ),
    );
  }
 } 

    List<Widget> _getRenderViews() {
     final List<StatefulWidget> list = [];
     list.add(RtcLocalView.SurfaceView());
     _users.forEach((int uid) => list.add(RtcRemoteView.SurfaceView(uid: uid)));
     return list;
    }

  Widget _videoView(view) {
    return Expanded(child: Container(child: view));
    }

    Widget _expandedVideoRow(List<Widget> views) {
       final wrappedViews = views.map<Widget>(_videoView).toList();
       return Expanded(
       child: Row(
        children: wrappedViews,
       ),
      );
    }

     Widget _viewRows() {
       final views = _getRenderViews();
       switch (views.length) {
      case 1:
        return Container(
          child: Column(
      children: <Widget>[_videoView(views[0])],
    ));
      case 2:
       return Container(
        child: Column(
        children: <Widget>[
        _expandedVideoRow([views[0]]),
        _expandedVideoRow([views[1]])
       ],
    ));
     case 3:
       return Container(
        child: Column(
        children: <Widget>[
         _expandedVideoRow(views.sublist(0, 2)),
        _expandedVideoRow(views.sublist(2, 3))
      ],
    ));
     case 4:
       return Container(
        child: Column(
      children: <Widget>[
        _expandedVideoRow(views.sublist(0, 2)),
        _expandedVideoRow(views.sublist(2, 4))
        ],
       ));
      default:
     }
        return Container();
              }

            void _onCallEnd(BuildContext context) {
     // _users.clear();
    // // destroy SDK
      // _engine.leaveChannel();
      // _engine.destroy();
           for (int i = 0; i < callLogs.docs.length; i  ) {
           If (widget.channelName == callLogs.docs[i].id) {
          int timecount = DateTime.now()
        .difference(DateTime.parse(callLogs.docs[i].get("starttime")))
        .inSeconds;
      crudobj.updateCallLogs(widget.channelName, {
      "callstatus": "received",
      "endtime": DateTime.now().toString(),
      "duration": timecount
       });
     }
   }

    databaseReference
    .child("hys_calling_data")
    .child("sm_calls")
    .child(widget.channelName)
    .update({
  'iscallreceivedbyReceiver': false,
  'iscallcancelledbycaller': false,
  'iscallrejected': false,
  "iscallcancelledafterReceived": true,
  "message": "NO"
     });
      databaseReference
    .child("hys_calling_data")
    .child("usercallstatus")
    .child(_currentUserId)
    .set({"callstatus": false});
     WidgetsBinding.instance.addPostFrameCallback((_) {
       Navigator.of(context).pop();
       if ((widget.questionid != "")) {
         Navigator.push(
         context,
           MaterialPageRoute(
             builder: (context) =>
                CallingFeedBack(widget.questionid, widget.userid)));
        } else {
    crudobj.addUserCreditPointsData(
        3, "Answered", current_date, comparedate);
    Navigator.push(
        context,
        MaterialPageRoute(
            builder: (context) => BottomNavigationBarWidget()));
     }
    });
  }

   void _onToggleMute() {
    setState(() {
    muted = !muted;
     });
    _engine.muteLocalAudioStream(muted);
   }

      void _onSwitchCamera() {
      _engine.switchCamera();
     }
     }
 

Я использую тот же код для видеозвонка, просто включив функцию enableVideo, тогда она работает нормально, но для аудио она не передает звук.

   Future<void> _initAgoraRtcEngine() async {
  RtcEngineConfig config = RtcEngineConfig(appID); 
 _engine = await RtcEngine.createWithConfig(config);
  await _engine.disableVideo();
  await _engine.enableAudio();
  } 
 

используя эту функцию, я определяю часть аудио-видео

Комментарии:

1. Эй, вы можете сослаться на этот код, так как я реализовал голосовой вызов с помощью Agora github.com/champ96k/agora-voice-calling , я надеюсь, что это вам поможет 🙂