diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 77dd3e4..6cf187b 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -55,6 +55,9 @@ android { ndk { abiFilters.clear() abiFilters += "arm64-v8a" + abiFilters += "armeabi-v7a" + abiFilters += "x86" + abiFilters += "x86_64" // 或者:abiFilters.add("arm64-v8a") } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4a34cf8..bf4ea4c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ android:icon="@mipmap/ic_launcher"> + + + + + + + + + TLSv1.2 + + + \ No newline at end of file diff --git a/assets/images/rocket1.svga b/assets/images/rocket1.svga new file mode 100644 index 0000000..c4c6215 Binary files /dev/null and b/assets/images/rocket1.svga differ diff --git a/assets/images/rocket2.svga b/assets/images/rocket2.svga new file mode 100644 index 0000000..e6d24a3 Binary files /dev/null and b/assets/images/rocket2.svga differ diff --git a/assets/images/rocket3.svga b/assets/images/rocket3.svga new file mode 100644 index 0000000..912894a Binary files /dev/null and b/assets/images/rocket3.svga differ diff --git a/lib/controller/discover/room_controller.dart b/lib/controller/discover/room_controller.dart index 9451f99..b7d4329 100644 --- a/lib/controller/discover/room_controller.dart +++ b/lib/controller/discover/room_controller.dart @@ -1,3 +1,5 @@ +import 'package:agora_rtc_engine/agora_rtc_engine.dart'; +import 'package:agora_token_generator/agora_token_generator.dart'; import 'package:dating_touchme_app/model/live/live_chat_message.dart'; import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart'; import 'package:dating_touchme_app/network/network_service.dart'; @@ -17,9 +19,6 @@ class RoomController extends GetxController { /// 当前频道信息 final Rxn rtcChannel = Rxn(); - /// 是否正在创建 - final RxBool isLoading = false.obs; - /// 聊天消息列表 final RxList chatMessages = [].obs; @@ -77,37 +76,49 @@ class RoomController extends GetxController { /// 调用接口创建 RTC 频道 Future createRtcChannel() async { - if (isLoading.value) return; final granted = await _ensureRtcPermissions(); if (!granted) return; try { - isLoading.value = true; final response = await _networkService.rtcApi.createRtcChannel(); final base = response.data; if (base.isSuccess && base.data != null) { rtcChannel.value = base.data; - await _joinRtcChannel(base.data!.token, base.data!.channelId, base.data!.uid); + await _joinRtcChannel(base.data!.token, base.data!.channelId, base.data!.uid, ClientRoleType.clientRoleBroadcaster); } else { final message = base.message.isNotEmpty ? base.message : '创建频道失败'; SmartDialog.showToast(message); } } catch (e) { SmartDialog.showToast('创建频道异常:$e'); - } finally { - isLoading.value = false; } } + + Future joinChannel(String channelName) async { + try { + final response = await _networkService.rtcApi.getSwRtcToken(channelName); + final base = response.data; + if (base.isSuccess && base.data != null) { + rtcChannel.value = base.data; + await _joinRtcChannel(base.data!.token, channelName, base.data!.uid, ClientRoleType.clientRoleAudience); + } + } catch (e) { + SmartDialog.showToast('加入频道异常:$e'); + } + } + Future _joinRtcChannel( String token, String channelName, int uid, + ClientRoleType roleType ) async { try { await RTCManager.instance.joinChannel( token: token, channelId: channelName, uid: uid, + role: roleType, ); } catch (e) { SmartDialog.showToast('加入频道失败:$e'); @@ -117,7 +128,7 @@ class RoomController extends GetxController { /// 发送公屏消息 Future sendChatMessage(String content) async { final channelName = rtcChannel.value?.channelId ?? RTCManager.instance.currentChannelId; - + final result = await _messageService.sendMessage( content: content, channelName: channelName, @@ -152,8 +163,8 @@ class RoomController extends GetxController { return false; } - Future disposeRtcResources() async { - await RTCManager.instance.dispose(); + Future leaveChannel() async { + await RTCManager.instance.leaveChannel(); } } diff --git a/lib/controller/mine/login_controller.dart b/lib/controller/mine/login_controller.dart index e502737..a945bcf 100644 --- a/lib/controller/mine/login_controller.dart +++ b/lib/controller/mine/login_controller.dart @@ -115,7 +115,7 @@ class LoginController extends GetxController { GlobalData().userId = result.userId; GlobalData().qnToken = result.token; await storage.write('token', result.token); - // await storage.write('userId', result.userId); + await storage.write('userId', result.userId); // 登录成功后获取用户信息 await _handleUserInfoRetrieval(result.userId); diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 29125fb..dcd36c9 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -66,8 +66,8 @@ class Assets { static const String emojiEmoji62 = 'assets/images/emoji/emoji_62.png'; static const String emojiEmoji63 = 'assets/images/emoji/emoji_63.png'; static const String emojiEmoji64 = 'assets/images/emoji/emoji_64.png'; - static const String imagesAd = 'assets/images/ad.png'; static const String imagesAcceptCall = 'assets/images/accept_call.png'; + static const String imagesAd = 'assets/images/ad.png'; static const String imagesAdd = 'assets/images/add.png'; static const String imagesAliPay = 'assets/images/ali_pay.png'; static const String imagesArrow = 'assets/images/arrow.png'; @@ -110,6 +110,7 @@ class Assets { static const String imagesGift4 = 'assets/images/gift4.png'; static const String imagesGift5 = 'assets/images/gift5.png'; static const String imagesGiftIcon = 'assets/images/gift_icon.png'; + static const String imagesGiftPic = 'assets/images/gift_pic.png'; static const String imagesHelpBg = 'assets/images/help_bg.png'; static const String imagesHiIcon = 'assets/images/hi_icon.png'; static const String imagesHomeNol = 'assets/images/home_nol.png'; @@ -137,15 +138,18 @@ class Assets { static const String imagesPhoto = 'assets/images/photo.png'; static const String imagesPhotoChecked = 'assets/images/photo_checked.png'; static const String imagesPhotoUncheck = 'assets/images/photo_uncheck.png'; - static const String imagesPlayIcon = 'assets/images/play_icon.png'; - static const String imagesPlayer = 'assets/images/player.png'; static const String imagesPlatVoiceMessage = 'assets/images/plat_voice_message.png'; static const String imagesPlatVoiceMessageSelf = 'assets/images/plat_voice_message_self.png'; + static const String imagesPlayIcon = 'assets/images/play_icon.png'; + static const String imagesPlayer = 'assets/images/player.png'; static const String imagesRealChecked = 'assets/images/real_checked.png'; static const String imagesRealName = 'assets/images/real_name.png'; static const String imagesRealUncheck = 'assets/images/real_uncheck.png'; static const String imagesRealnameHelp = 'assets/images/realname_help.png'; static const String imagesRejectCall = 'assets/images/reject_call.png'; + static const String imagesRocket1 = 'assets/images/rocket1.svga'; + static const String imagesRocket2 = 'assets/images/rocket2.svga'; + static const String imagesRocket3 = 'assets/images/rocket3.svga'; static const String imagesRose = 'assets/images/rose.png'; static const String imagesRoseBanner = 'assets/images/rose_banner.png'; static const String imagesRoseGift = 'assets/images/rose_gift.png'; diff --git a/lib/main.dart b/lib/main.dart index a89fe82..0576363 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -84,10 +84,10 @@ class MyApp extends StatelessWidget { // 判断token是否为空 final storage = GetStorage(); - final userId = storage.read('userId'); + final token = storage.read('token'); // 如果token不为空,显示主页;如果token为空,显示登录页面 - if (userId != null && userId.isNotEmpty) { + if (token != null && token.isNotEmpty) { return MainPage(); } else { return LoginPage(); diff --git a/lib/network/rtc_api.dart b/lib/network/rtc_api.dart index 7e4c4e8..860c19c 100644 --- a/lib/network/rtc_api.dart +++ b/lib/network/rtc_api.dart @@ -13,7 +13,9 @@ abstract class RtcApi { /// 创建实时音视频频道 @GET(ApiUrls.getSwRtcToken) - Future>> getSwRtcToken(); + Future>> getSwRtcToken( + @Query('channelId') String channelId, + ); /// 获取声网 RTM Token @GET(ApiUrls.getSwRtmToken) diff --git a/lib/network/rtc_api.g.dart b/lib/network/rtc_api.g.dart index 2cbdf56..8166434 100644 --- a/lib/network/rtc_api.g.dart +++ b/lib/network/rtc_api.g.dart @@ -20,9 +20,11 @@ class _RtcApi implements RtcApi { final ParseErrorLogger? errorLogger; @override - Future>> getSwRtcToken() async { + Future>> getSwRtcToken( + String channelId, + ) async { final _extra = {}; - final queryParameters = {}; + final queryParameters = {r'channelId': channelId}; final _headers = {}; const Map? _data = null; final _options = _setStreamType>>( diff --git a/lib/pages/discover/discover_page.dart b/lib/pages/discover/discover_page.dart index 0d121dd..e1ae252 100644 --- a/lib/pages/discover/discover_page.dart +++ b/lib/pages/discover/discover_page.dart @@ -149,11 +149,25 @@ class LiveItem extends StatefulWidget { } class _LiveItemState extends State { + + late final RoomController roomController; + + @override + void initState() { + super.initState(); + if (Get.isRegistered()) { + roomController = Get.find(); + } else { + roomController = Get.put(RoomController()); + } + } + @override Widget build(BuildContext context) { return InkWell( - onTap: () { - Get.to(() => LiveRoomPage(id: 0)); + onTap: () async{ + // Get.to(() => LiveRoomPage(id: 0)); + await roomController.joinChannel('1189028638616588288'); }, child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(10.w)), diff --git a/lib/pages/discover/live_room_page.dart b/lib/pages/discover/live_room_page.dart index 631c59e..3b9295b 100644 --- a/lib/pages/discover/live_room_page.dart +++ b/lib/pages/discover/live_room_page.dart @@ -75,7 +75,7 @@ class _LiveRoomPageState extends State { @override void dispose() { - _roomController.disposeRtcResources(); + _roomController.leaveChannel(); _messageController.dispose(); super.dispose(); } diff --git a/lib/pages/main/main_page.dart b/lib/pages/main/main_page.dart index 174e049..cd3f599 100644 --- a/lib/pages/main/main_page.dart +++ b/lib/pages/main/main_page.dart @@ -1,8 +1,11 @@ +import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/pages/main/tabbar/main_tab_bar.dart'; import 'package:dating_touchme_app/pages/message/message_page.dart'; import 'package:dating_touchme_app/pages/mine/mine_page.dart'; import 'package:dating_touchme_app/rtc/rtm_manager.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:flutter_svga/flutter_svga.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; @@ -64,6 +67,18 @@ class _MainPageState extends State { initRTM() async { String? userId = storage.read('userId'); await RTMManager.instance.initialize(appId: '4c2ea9dcb4c5440593a418df0fdd512d', userId: userId ?? ''); + SmartDialog.show( + alignment: Alignment.center, + builder: (context){ + return SVGAEasyPlayer( + assetsName: Assets.imagesRocket2, + fit: BoxFit.contain, + ); + } + ); + Future.delayed(const Duration(seconds: 4), () { + SmartDialog.dismiss(); + }); } @override diff --git a/lib/pages/mine/login_page.dart b/lib/pages/mine/login_page.dart index 1fa50a4..281c697 100644 --- a/lib/pages/mine/login_page.dart +++ b/lib/pages/mine/login_page.dart @@ -7,7 +7,7 @@ import 'package:dating_touchme_app/controller/mine/login_controller.dart'; class LoginPage extends StatelessWidget { LoginPage({super.key}); - + // 是否同意协议 final agreeTerms = Rx(false); @@ -20,13 +20,16 @@ class LoginPage extends StatelessWidget { resizeToAvoidBottomInset: false, body: Stack( children: [ - Image.asset( - Assets.imagesLoginBg, - width: 1.sw, - height: 1.sh, - ), Container( + width: double.infinity, height: 1.sh, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(Assets.imagesLoginBg), + fit: BoxFit.cover, + alignment: Alignment.topCenter, + ), + ), padding: EdgeInsets.symmetric(horizontal: 20), child: Column( children: [ @@ -35,10 +38,7 @@ class LoginPage extends StatelessWidget { Center( child: Column( children: [ - Image.asset( - Assets.imagesLoginLogo, - height: 60, - ), + Image.asset(Assets.imagesLoginLogo, height: 60), const SizedBox(height: 10), const Text( '心动就动我 幸福马上行动', @@ -70,7 +70,10 @@ class LoginPage extends StatelessWidget { decoration: const InputDecoration( hintText: '请输入你的手机号', border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 15, vertical: 14), + contentPadding: EdgeInsets.symmetric( + horizontal: 15, + vertical: 14, + ), counterText: '', ), keyboardType: TextInputType.phone, @@ -101,7 +104,10 @@ class LoginPage extends StatelessWidget { decoration: const InputDecoration( hintText: '请输入验证码', border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 15, vertical: 14), + contentPadding: EdgeInsets.symmetric( + horizontal: 15, + vertical: 14, + ), counterText: '', ), keyboardType: TextInputType.number, @@ -114,18 +120,25 @@ class LoginPage extends StatelessWidget { ), // 获取验证码按钮 GestureDetector( - onTap: controller.isSendingCode.value || controller.countdownSeconds.value > 0 + onTap: + controller.isSendingCode.value || + controller.countdownSeconds.value > 0 ? null : controller.getVerificationCode, child: Container( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 14), + padding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 14, + ), child: Text( controller.countdownSeconds.value > 0 ? '${controller.countdownSeconds.value}秒后重试' : '获取验证码', style: TextStyle( fontSize: 14, - color: (controller.isSendingCode.value || controller.countdownSeconds.value > 0) + color: + (controller.isSendingCode.value || + controller.countdownSeconds.value > 0) ? Colors.grey.shade400 : const Color.fromRGBO(74, 99, 235, 1), ), @@ -141,16 +154,19 @@ class LoginPage extends StatelessWidget { // 协议同意复选框 Row( children: [ - Obx(() => Checkbox( - value: agreeTerms.value, - onChanged: (value) { - agreeTerms.value = value ?? false; - }, - activeColor: Colors.grey, - side: const BorderSide(color: Colors.grey), - shape: const CircleBorder(), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - )), + Obx( + () => Checkbox( + value: agreeTerms.value, + onChanged: (value) { + agreeTerms.value = value ?? false; + }, + activeColor: Colors.grey, + side: const BorderSide(color: Colors.grey), + shape: const CircleBorder(), + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + ), + ), const Text( '我已阅读并同意', style: TextStyle( @@ -199,18 +215,20 @@ class LoginPage extends StatelessWidget { onPressed: controller.isLoggingIn.value ? null : () { - // 登录逻辑 - if (!agreeTerms.value) { - SmartDialog.showToast('请阅读并同意用户协议和隐私政策'); - return; - } + // 登录逻辑 + if (!agreeTerms.value) { + SmartDialog.showToast('请阅读并同意用户协议和隐私政策'); + return; + } - // 调用控制器的登录方法 - controller.login(); - }, + // 调用控制器的登录方法 + controller.login(); + }, style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 50), - backgroundColor: agreeTerms.value ? const Color.fromRGBO(74, 99, 235, 1) : Colors.grey.shade300, + backgroundColor: agreeTerms.value + ? const Color.fromRGBO(74, 99, 235, 1) + : Colors.grey.shade300, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(25), ), @@ -218,26 +236,26 @@ class LoginPage extends StatelessWidget { ), child: controller.isLoggingIn.value ? const SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - color: Colors.white, - strokeWidth: 2, - ), - ) + width: 20, + height: 20, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 2, + ), + ) : const Text( - '注册并登录', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), + '注册并登录', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), ), ), ], ), - ) + ), ], ), ); diff --git a/lib/rtc/rtc_manager.dart b/lib/rtc/rtc_manager.dart index f49fcd1..a88da39 100644 --- a/lib/rtc/rtc_manager.dart +++ b/lib/rtc/rtc_manager.dart @@ -11,6 +11,9 @@ import '../pages/discover/live_room_page.dart'; class RTCManager { /// 频道加入状态通知,用于UI监听 final ValueNotifier channelJoinedNotifier = ValueNotifier(false); + final ValueNotifier> remoteUsersNotifier = ValueNotifier>( + [], + ); RtcEngine? get engine => _engine; bool get isInChannel => _isInChannel; int? get currentUid => _currentUid; @@ -26,8 +29,8 @@ class RTCManager { bool _isInChannel = false; String? _currentChannelId; int? _currentUid; - int? _streamId; - + ClientRoleType _clientRole = ClientRoleType.clientRoleBroadcaster; + final List _remoteUserIds = []; // 事件回调 Function(RtcConnection connection, int elapsed)? onJoinChannelSuccess; Function(RtcConnection connection, int remoteUid, int elapsed)? onUserJoined; @@ -86,9 +89,9 @@ class RTCManager { await _engine!.initialize( RtcEngineContext(appId: appId, channelProfile: channelProfile), ); - await _engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster); + await _engine?.setClientRole(role: _clientRole); await _engine?.enableVideo(); - await _engine?.startPreview(); + // await _engine?.startPreview(); // 注册事件处理器 _registerEventHandlers(); @@ -107,16 +110,19 @@ class RTCManager { _engine!.registerEventHandler( RtcEngineEventHandler( - onJoinChannelSuccess: (RtcConnection connection, int elapsed) async{ + onJoinChannelSuccess: (RtcConnection connection, int elapsed) async { _isInChannel = true; + _remoteUserIds.clear(); + remoteUsersNotifier.value = const []; channelJoinedNotifier.value = true; _currentChannelId = connection.channelId; print('加入频道成功,频道名:${connection.channelId},耗时:${elapsed}ms'); - if(connection.localUid == _currentUid){ + if (connection.localUid == _currentUid) { + await RTMManager.instance.subscribe(_currentChannelId ?? ''); await RTMManager.instance.publishChannelMessage( - channelName: _currentChannelId ?? '', - message: json.encode({'type': 'join_room', 'uid': _currentUid}) + channelName: _currentChannelId ?? '', + message: json.encode({'type': 'join_room', 'uid': _currentUid}), ); Get.to(() => const LiveRoomPage(id: 0)); } @@ -126,13 +132,11 @@ class RTCManager { }, onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) { print('用户加入,UID:$remoteUid'); + _handleRemoteUserJoined(remoteUid); if (onUserJoined != null) { onUserJoined!(connection, remoteUid, elapsed); } }, - onStreamMessage: (RtcConnection connection, int remoteUid, int streamId, Uint8List data, int length, int sentTs){ - print('收到消息,UID:$remoteUid,流ID:$streamId,数据:${utf8.decode(data)}'); - }, onUserOffline: ( RtcConnection connection, @@ -140,12 +144,15 @@ class RTCManager { UserOfflineReasonType reason, ) { print('用户离开,UID:$remoteUid,原因:$reason'); + _handleRemoteUserOffline(remoteUid); if (onUserOffline != null) { onUserOffline!(connection, remoteUid, reason); } }, onLeaveChannel: (RtcConnection connection, RtcStats stats) { _isInChannel = false; + _remoteUserIds.clear(); + remoteUsersNotifier.value = const []; channelJoinedNotifier.value = false; _currentChannelId = null; print('离开频道,统计信息:${stats.duration}秒'); @@ -343,7 +350,7 @@ class RTCManager { String? token, required String channelId, int uid = 0, - ChannelMediaOptions? options, + ClientRoleType role = ClientRoleType.clientRoleBroadcaster, }) async { if (_engine == null) { throw Exception('RTC Engine not initialized'); @@ -352,15 +359,24 @@ class RTCManager { print('已经在频道中,先离开当前频道'); await leaveChannel(); } - + await setClientRole(role: role); _currentUid = uid; + if (role == ClientRoleType.clientRoleBroadcaster) { + await _engine?.startPreview(); + } await _engine!.joinChannel( token: token ?? '', channelId: channelId, uid: uid, - options: options ?? const ChannelMediaOptions(), + options: ChannelMediaOptions( + channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, + clientRoleType: role, + autoSubscribeAudio: true, + autoSubscribeVideo: true, + publishCameraTrack: true, + publishMicrophoneTrack: true, + ), ); - _streamId = await _engine?.createDataStream(DataStreamConfig(syncWithAudio: false, ordered: false)); print('正在加入频道:$channelId,UID:$uid'); } @@ -373,7 +389,7 @@ class RTCManager { print('当前不在频道中'); return; } - + await RTMManager.instance.unsubscribe(_currentChannelId ?? ''); await _engine!.leaveChannel(); _currentUid = null; print('已离开频道'); @@ -417,24 +433,18 @@ class RTCManager { if (_engine == null) { throw Exception('RTC Engine not initialized'); } + _clientRole = role; await _engine!.setClientRole(role: role, options: options); print('客户端角色已设置为:$role'); } - /// 发送消息 - Future sendMessage(String message) async { - Uint8List data = utf8.encode(message); - await _engine!.sendStreamMessage( - streamId: _streamId ?? 0, - data: data, - length: data.length, - ); - print('已发送消息:$message'); - } - /// 获取当前频道ID String? get currentChannelId => _currentChannelId; + ClientRoleType get clientRole => _clientRole; + + List get remoteUserIds => List.unmodifiable(_remoteUserIds); + /// 释放资源 Future dispose() async { try { @@ -445,6 +455,8 @@ class RTCManager { await _engine!.release(); _engine = null; } + _remoteUserIds.clear(); + remoteUsersNotifier.value = const []; _isInitialized = false; _isInChannel = false; _currentChannelId = null; @@ -455,4 +467,17 @@ class RTCManager { print('Failed to dispose RTC Engine: $e'); } } + + void _handleRemoteUserJoined(int remoteUid) { + print('用户已加入频道:$remoteUid'); + if (_remoteUserIds.contains(remoteUid)) return; + _remoteUserIds.add(remoteUid); + remoteUsersNotifier.value = List.unmodifiable(_remoteUserIds); + } + + void _handleRemoteUserOffline(int remoteUid) { + final removed = _remoteUserIds.remove(remoteUid); + if (!removed) return; + remoteUsersNotifier.value = List.unmodifiable(_remoteUserIds); + } } diff --git a/lib/rtc/rtm_manager.dart b/lib/rtc/rtm_manager.dart index a8097d3..2758b5c 100644 --- a/lib/rtc/rtm_manager.dart +++ b/lib/rtc/rtm_manager.dart @@ -74,13 +74,13 @@ class RTMManager { _currentUserId = userId; _isInitialized = true; _registerClientListeners(); - final response = await _networkService.rtcApi.getSwRtmToken(); - // 处理响应 - if (response.data.isSuccess) { - await login(response.data.data?.token ?? ''); - } else { - SmartDialog.showToast(response.data.message); - } + // final response = await _networkService.rtcApi.getSwRtmToken(); + // // 处理响应 + // if (response.data.isSuccess) { + await login('007eJxTYIhoZ/m/gMf0gdWv1PV2R6I/Lpry06W77sOR2BDuFv89JeEKDCbJRqmJlinJSSbJpiYmBqaWxokmhhYpaQZpKSmmhkYpE9/IZgrwMTDYOB4rZ2RgYmAEQhCfkcEcAPTmHg4='); + // } else { + // SmartDialog.showToast(response.data.message); + // } return true; } diff --git a/lib/widget/live/live_room_anchor_showcase.dart b/lib/widget/live/live_room_anchor_showcase.dart index 953182a..90bc023 100644 --- a/lib/widget/live/live_room_anchor_showcase.dart +++ b/lib/widget/live/live_room_anchor_showcase.dart @@ -19,114 +19,132 @@ class _LiveRoomAnchorShowcaseState extends State { return ValueListenableBuilder( valueListenable: _rtcManager.channelJoinedNotifier, builder: (context, joined, _) { - return Column( - children: [ - Stack( + return ValueListenableBuilder>( + valueListenable: _rtcManager.remoteUsersNotifier, + builder: (context, remoteUids, __) { + final int? remoteUid = remoteUids.isNotEmpty + ? remoteUids.first + : null; + return Column( children: [ - _buildAnchorVideo(joined), - Positioned( - top: 5.w, - left: 5.w, - child: Container( - width: 42.w, - height: 13.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(13.w)), - color: const Color.fromRGBO(142, 20, 186, 1), - ), - child: Center( - child: Text( - "主持人", - style: TextStyle(fontSize: 9.w, color: Colors.white), + Stack( + children: [ + _buildAnchorVideo(joined, remoteUid), + Positioned( + top: 5.w, + left: 5.w, + child: Container( + width: 42.w, + height: 13.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(13.w)), + color: const Color.fromRGBO(142, 20, 186, 1), + ), + child: Center( + child: Text( + "主持人", + style: TextStyle( + fontSize: 9.w, + color: Colors.white, + ), + ), + ), ), ), - ), - ), - Positioned( - top: 5.w, - right: 5.w, - child: Container( - width: 20.w, - height: 20.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(20.w)), - color: const Color.fromRGBO(0, 0, 0, .3), - ), - child: Center( - child: Image.asset( - Assets.imagesGiftIcon, - width: 19.w, - height: 19.w, + Positioned( + top: 5.w, + right: 5.w, + child: Container( + width: 20.w, + height: 20.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.w)), + color: const Color.fromRGBO(0, 0, 0, .3), + ), + child: Center( + child: Image.asset( + Assets.imagesGiftIcon, + width: 19.w, + height: 19.w, + ), + ), ), ), - ), - ), - Positioned( - bottom: 5.w, - right: 5.w, - child: Container( - width: 47.w, - height: 20.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(20.w)), - color: Colors.white, - ), - child: Center( - child: Text( - "加好友", - style: TextStyle( - fontSize: 11.w, - color: const Color.fromRGBO(117, 98, 249, 1), + Positioned( + bottom: 5.w, + right: 5.w, + child: Container( + width: 47.w, + height: 20.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(20.w)), + color: Colors.white, + ), + child: Center( + child: Text( + "加好友", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(117, 98, 249, 1), + ), + ), ), ), ), - ), - ), - ], - ), - SizedBox(height: 5.w), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildSideAnchorCard( - isLeft: true, - micIcon: Assets.imagesMicClose, + ], ), - _buildSideAnchorCard( - isLeft: false, - micIcon: Assets.imagesMicOpen, + SizedBox(height: 5.w), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildSideAnchorCard( + isLeft: true, + micIcon: Assets.imagesMicClose, + ), + _buildSideAnchorCard( + isLeft: false, + micIcon: Assets.imagesMicOpen, + ), + ], ), ], - ), - ], + ); + }, ); }, ); } - Widget _buildAnchorVideo(bool joined) { + Widget _buildAnchorVideo(bool joined, int? remoteUid) { final engine = _rtcManager.engine; if (!joined || engine == null) { return _buildWaitingPlaceholder(); } - - final localUid = _rtcManager.currentUid ?? 0; + print('joined: $joined'); + ClientRoleType role = _rtcManager.clientRole; return ClipRRect( borderRadius: BorderRadius.all(Radius.circular(9.w)), child: SizedBox( width: 177.w, height: 175.w, - child: AgoraVideoView( - controller: VideoViewController( - rtcEngine: engine, - canvas: VideoCanvas( - uid: 0, - ), - ), - onAgoraVideoViewCreated: (viewId){ - engine.startPreview(); - }, - ), + child: role == ClientRoleType.clientRoleBroadcaster + ? AgoraVideoView( + controller: VideoViewController( + rtcEngine: engine, + canvas: const VideoCanvas(uid: 0), + ), + ) + : (_rtcManager.currentChannelId == null + ? _buildWaitingPlaceholder() + : AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: engine, + canvas: VideoCanvas(uid: _rtcManager.remoteUserIds.first), + connection: RtcConnection( + channelId: _rtcManager.currentChannelId!, + ), + ), + )), ), ); } diff --git a/pubspec.lock b/pubspec.lock index 71f0980..d15d7d0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -587,6 +587,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.9.8+9" + flutter_svga: + dependency: "direct main" + description: + name: flutter_svga + sha256: "8b96237fd33c80f3e9850245515d41c70520a4bcb9f3415e96b53fb798e6ab0a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.8" flutter_swiper_null_safety: dependency: transitive description: @@ -605,14 +613,6 @@ packages: description: flutter source: sdk version: "0.0.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.flutter-io.cn" - source: hosted - version: "4.0.0" get: dependency: "direct main" description: @@ -645,14 +645,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.3.2" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b - url: "https://pub.flutter-io.cn" - source: hosted - version: "4.3.0" html: dependency: transitive description: @@ -869,14 +861,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" - lean_builder: - dependency: transitive - description: - name: lean_builder - sha256: ef5cd5f907157eb7aa87d1704504b5a6386d2cbff88a3c2b3344477bab323ee9 - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.1.2" lints: dependency: transitive description: @@ -1160,10 +1144,10 @@ packages: dependency: transitive description: name: protobuf - sha256: "2fcc8a202ca7ec17dab7c97d6b6d91cf03aa07fe6f65f8afbb6dfa52cc5bd902" + sha256: de9c9eb2c33f8e933a42932fe1dc504800ca45ebc3d673e6ed7f39754ee4053e url: "https://pub.flutter-io.cn" source: hosted - version: "5.1.0" + version: "4.2.0" provider: dependency: transitive description: @@ -1280,10 +1264,10 @@ packages: dependency: "direct dev" description: name: retrofit_generator - sha256: "56df50afab95199dada9e1afbfe5ec228612d03859b250e5e4846c3ebfe50bf7" + sha256: "5827551e2496f2c9586e4f23eedd6ce0b519286d2f405f91bf70f342a96b48ce" url: "https://pub.flutter-io.cn" source: hosted - version: "10.1.4" + version: "10.0.6" rxdart: dependency: transitive description: @@ -1721,14 +1705,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.6.1" - xxh3: - dependency: transitive - description: - name: xxh3 - sha256: "399a0438f5d426785723c99da6b16e136f4953fb1e9db0bf270bd41dd4619916" - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 28cf110..655924f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,6 +74,7 @@ dependencies: location_plugin: path: location_plugin image_picker_android: ^0.8.12+23 + flutter_svga: ^0.0.8 dev_dependencies: flutter_test: