【问题标题】:Agora in Flutter- navigating to the video chat screen more than one time keeps local video loading foreverFlutter 中的 Agora - 多次导航到视频聊天屏幕,使本地视频永久加载
【发布时间】:2021-12-30 21:02:26
【问题描述】:

我在Flutter 中使用Agora 进行一对一视频聊天。 用户 1 有一个应用要上线,用户 2 有另一个应用要上线。两人上网后,就可以进行视频聊天了。 两个应用的代码库几乎相似

我有一个屏幕或活动(例如 screen1),其中在点击按钮(例如 button1)时会显示一个警报对话框。在警报对话框中点击Continue 按钮时,对话框消失,用户被带到进行视频聊天的屏幕(比如screen2)。但是在成功进入视频聊天屏幕后,如果用户点击手机上的后退按钮,那么他/她将被带到屏幕1,点击按钮1后,如果用户点击弹出窗口中的Continue按钮警告对话框,用户再次被带到 screen2 但这次本地视频(即使用应用程序的用户的视频)一直在加载。显然,我希望本地视频能够像第一次那样加载。

我会把我的代码放在这里,以便你可以轻松运行它。

以下代码适用于 user1。对于 user2,应用程序中没有警报框。来自 user1 的相同代码用于 user2 应用程序,除了 remoteUid 的值设置为 user2 的 2 而此值设置为 user1 的 1。这些只是标识 2 个用户的两个值。

对于用户 1:

ma​​in.dart:

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'livesession1to1.dart';

void main()  {

  runApp(MessagingExampleApp());
}

class NavigationService {
  static GlobalKey<NavigatorState> navigatorKey =
  GlobalKey<NavigatorState>();
}


/// Entry point for the example application.
class MessagingExampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Messaging Example App',
      navigatorKey: NavigationService.navigatorKey, // set property
      theme: ThemeData.dark(),
      routes: {
        '/': (context) => Application(),
        '/liveSession1to1': (context) =>LiveSession1to1(),
      },
    );
  }
}

int _messageCount = 0;

/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
String constructFCMPayload(String? token, String server_key) {
  _messageCount++;
  return jsonEncode({
    'token': token,
    'to':token,
    'data': {
      'via': 'FlutterFire Cloud Messaging!!!',
      'count': _messageCount.toString(),
    },
    'notification': {
      'title': 'Hello FlutterFire!',
      'body': 'This notification (#$_messageCount) was created via FCM! =============',
    },
    "delay_while_idle" : false,
    "priority" : "high",
    "content_available" : true

  });
}

/// Renders the example application.
class Application extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _Application();
}

class _Application extends State<Application> {
  String? _token;

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



  }


  showAlertDialog() {
    BuildContext context=NavigationService.navigatorKey.currentContext!;
    // set up the buttons
    Widget cancelButton = TextButton(
      child: Text("Cancel"),
      onPressed:  () {},
    );
    Widget continueButton = TextButton(
      child: Text("Continue"),
      onPressed:  () {

        Navigator.of(context, rootNavigator: true).pop();

        Navigator.of(context).pushNamed('/liveSession1to1');
      },
    );





    Timer? timer = Timer(Duration(milliseconds: 5000), (){

      Navigator.of(context, rootNavigator: true).pop();
    });

    showDialog(
        context: context,
        builder: (BuildContext builderContext) {

          return AlertDialog(
            backgroundColor: Colors.black26,
            title: Text('One to one live session'),
            content: SingleChildScrollView(
              child: Text('Do you want to connect for a live session ?'),
            ),

            actions: [
              cancelButton,
              continueButton,
            ],
          );
        }
    ).then((value){
      // dispose the timer in case something else has triggered the dismiss.
      timer?.cancel();
      timer = null;
    });


  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('My App'),

      ),
      floatingActionButton: Builder(
        builder: (context) => FloatingActionButton(
          onPressed: showAlertDialog,

          backgroundColor: Colors.white,
          child: const Icon(Icons.send),
        ),
      ),
      body: SingleChildScrollView(
        child: Text(
            'Trigger Alert'

        ),
      ),
    );
  }


}

livesession1to1.dart:

import 'dart:async';
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:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';

// const appId = "<-- Insert App Id -->";
// const token = "<-- Insert Token -->";


const appId = "......";// Put Agora App ID from Agora site here
const token = "....";// Put token ( temporary token avilable from Agora site)


void main() => runApp(MaterialApp(home: LiveSession1to1()));

class LiveSession1to1 extends StatefulWidget {
  @override
  _LiveSession1to1State createState() => _LiveSession1to1State();
}

class _LiveSession1to1State extends State<LiveSession1to1> {

  int _remoteUid=1;
  bool _localUserJoined = false;
  late RtcEngine _engine;

  @override
  void initState() {
    super.initState();
    setState(() {});
    initAgora();
  }





  Future<void> initAgora() async {
    // retrieve permissions
    await [Permission.microphone, Permission.camera].request();




    // Create RTC client instance
    RtcEngineContext context = RtcEngineContext(appId);
    _engine = await RtcEngine.createWithContext(context);


    await _engine.enableVideo();

    _engine.setEventHandler(
      RtcEngineEventHandler(
        joinChannelSuccess: (String channel, int uid, int elapsed) {
          print("local user $uid joined");
          setState(() {
            _localUserJoined = true;
          });
        },
        userJoined: (int uid, int elapsed) {
          print("remote user $uid joined");
          setState(() {
            _remoteUid = uid;
          });
        },
        userOffline: (int uid, UserOfflineReason reason) {
          print("remote user $uid left channel");
          setState(() {
            // _remoteUid = null;
            _remoteUid = 0;
          });
        },
      ),
    );

    try {
      await _engine.joinChannel(token, "InstaClass", null, 0);

    } catch (e) {
      print("error with agora = ");
      print("$e");
      print("error printeddddd");
    }
  }

  // Create UI with local view and remote view
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Agora Video Call'),
      ),
      body: Stack(
        children: [
          Center(
            child: _remoteVideo(),
          ),


          Align(
            alignment: Alignment.topLeft,
            child: Container(
              width: 100,
              height: 150,
              child: Center(
                child: _localUserJoined
                    ? RtcLocalView.SurfaceView()
                    : CircularProgressIndicator(),
              ),
            ),
          ),


        ],
      ),
    );
  }

  // Display remote user's video
  Widget _remoteVideo() {

    if (_remoteUid != 0) {
      return RtcRemoteView.SurfaceView(
        uid: _remoteUid,
        channelId: "InstaClass",
      );
    }else {
      print("'Please wait for remote user to join',");
      return Text(
        'Please wait for remote user to join',
        textAlign: TextAlign.center,
      );
    }
  }
}

对于用户 2:

ma​​in.dart

import 'dart:async';
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:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';


const appId = "....."; // Same as user1 app
const token = "....."; // same as user1 app

void main() => runApp(MaterialApp(home: MyApp()));

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // int? _remoteUid=1;

  int _remoteUid=2;
  bool _localUserJoined = false;
  late RtcEngine _engine;

  @override
  void initState() {
    super.initState();
    initAgora();
  }

  Future<void> initAgora() async {
    // retrieve permissions
    await [Permission.microphone, Permission.camera].request();

    //create the engine
    _engine = await RtcEngine.create(appId);
    await _engine.enableVideo();
    _engine.setEventHandler(
      RtcEngineEventHandler(
        joinChannelSuccess: (String channel, int uid, int elapsed) {
          print("local user $uid joined");
          setState(() {
            _localUserJoined = true;
          });
        },
        userJoined: (int uid, int elapsed) {
          print("remote user $uid joined");
          setState(() {
            _remoteUid = uid;
          });
        },
        userOffline: (int uid, UserOfflineReason reason) {
          print("remote user $uid left channel");
          setState(() {
            // _remoteUid = null;
            _remoteUid = 0;
          });
        },
      ),
    );

    // await _engine.joinChannel(token, "test", null, 0);
    await _engine.joinChannel(token, "InstaClass", null, 0);

  }

  // Create UI with local view and remote view
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Agora Video Call'),
      ),
      body: Stack(
        children: [
          Center(
            child: _remoteVideo(),
          ),
          Align(
            alignment: Alignment.topLeft,
            child: Container(
              width: 100,
              height: 150,
              child: Center(
                child: _localUserJoined
                    ? RtcLocalView.SurfaceView()
                    : CircularProgressIndicator(),
              ),
            ),
          ),
        ],
      ),
    );
  }

  // Display remote user's video
  Widget _remoteVideo() {
    /*if (_remoteUid != null) {
          return RtcRemoteView.SurfaceView(uid: _remoteUid!);
        }*/

    if (_remoteUid != 0) {
      return RtcRemoteView.SurfaceView(
        uid: _remoteUid,
        channelId: "InstaClass",
      );
    }else {
      return Text(
        'Please wait for remote user to join',
        textAlign: TextAlign.center,
      );
    }
  }
}

为了获取应用 ID 和令牌,请登录 Agora 网站。登录后,转到“项目管理”部分以查看已在此处创建的项目。在Functions 列下,单击密钥符号,您将被带到可以生成临时令牌的页面。在该页面上,为通道名称输入值“InstaClass”,因为我在代码中使用了此名称。

如何让视频聊天在第一次运行良好后顺利运行?

【问题讨论】:

  • 按返回键时你要离开频道吗?
  • @Diwyansh,AFAIK,如果我离开屏幕,Agora 会自动结束会话。那我按后退键不应该离开频道吗?
  • 不是这样的,当你按下返回时,你只是去上一个屏幕而不是结束通话。
  • @Diwyansh,我在这里发布了另一个与 Agora 视频聊天相关的问题 stackoverflow.com/questions/70541005/…?我可以请你看看那个问题吗?
  • 当然,一旦这个问题解决了,我会继续这样做。

标签: android flutter agora.io videochat


【解决方案1】:

我认为问题在于当按下back 按钮时,您只是被带到上一个屏幕并且呼叫session 没有结束。您可以在按下返回按钮时通过leaving频道尝试:

_engine.leaveChannel();

End Call按钮示例

             ElevatedButton(
                    onPressed: () {
                      _rtcEngine.leaveChannel();
                      Navigator.pop(context);
                    },
                    style: ButtonStyle(
                      shape: MaterialStateProperty.all(CircleBorder()),
                      backgroundColor: MaterialStateProperty.all(Colors.red),
                      padding: MaterialStateProperty.all(
                          EdgeInsets.fromLTRB(15, 15, 15, 12)),
                    ),
                    child: Icon(
                      Icons.phone,
                      size: 30,
                    ),
                  )

Back Button 使用 WillPopScope

覆盖
return WillPopScope(
      onWillPop: () async {
        _rtcEngine.leaveChannel();
        return true;
      },
      child: Scaffold(
        body: Container(),
      ),
    );

【讨论】:

  • 如何检测 Flutter 中的后退按钮按下事件?
  • 为此,您可以使用自定义结束通话按钮或使用 WillPopScope 小部件。
  • 我正在寻找一种使用自定义结束通话按钮的方法。有什么建议吗?
  • 如您所知,我们可以自定义通话页面的完整 UI,因此只需放置一个按钮即可结束通话,我将其添加到我的答案中。
  • 请通过这个docs.agora.io/en/Voice/API%20Reference/flutter/index.html,我也会添加一个WillPopScope函数。
猜你喜欢
  • 2020-05-01
  • 1970-01-01
  • 2022-11-09
  • 1970-01-01
  • 1970-01-01
  • 2018-12-03
  • 1970-01-01
  • 2021-05-12
  • 1970-01-01
相关资源
最近更新 更多