Browse Source

Merge branch 'master' of http://git.qniao.cn/dating-agency/dating_touchme_app

# Conflicts:
#	pubspec.lock
ios
Jolie 4 months ago
parent
commit
0b7fb45235
7 changed files with 737 additions and 413 deletions
  1. 198
      android/.kotlin/errors/errors-1763460366013.log
  2. 49
      lib/controller/discover/room_controller.dart
  3. 212
      lib/pages/discover/discover_page.dart
  4. 15
      lib/pages/discover/live_room_page.dart
  5. 33
      lib/rtc/rtc_manager.dart
  6. 217
      lib/widget/live/live_room_anchor_showcase.dart
  7. 426
      pubspec.lock

198
android/.kotlin/errors/errors-1763460366013.log

@ -0,0 +1,198 @@
kotlin version: 2.1.0
error message: Daemon compilation failed: null
java.lang.Exception
at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:69)
at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:65)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:240)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:31)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.AssertionError: java.lang.Exception: Could not close incremental caches in D:\www\dating_touchme_app\build\app_settings\kotlin\compileDebugKotlin\cacheable\caches-jvm\jvm\kotlin: class-fq-name-to-source.tab, source-to-classes.tab, internal-name-to-source.tab
at org.jetbrains.kotlin.com.google.common.io.Closer.close(Closer.java:236)
at org.jetbrains.kotlin.incremental.IncrementalCachesManager.close(IncrementalCachesManager.kt:55)
at kotlin.io.CloseableKt.closeFinally(Closeable.kt:56)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:293)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:674)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:91)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1659)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
... 3 more
Caused by: java.lang.Exception: Could not close incremental caches in D:\www\dating_touchme_app\build\app_settings\kotlin\compileDebugKotlin\cacheable\caches-jvm\jvm\kotlin: class-fq-name-to-source.tab, source-to-classes.tab, internal-name-to-source.tab
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.forEachMapSafe(BasicMapsOwner.kt:95)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.close(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.com.google.common.io.Closer.close(Closer.java:223)
... 22 more
Suppressed: java.lang.IllegalArgumentException: this and base files have different roots: C:\Users\Administrator\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\app_settings-6.1.1\android\src\main\kotlin\com\spencerccf\app_settings\AppSettingsPlugin.kt and D:\www\dating_touchme_app\android.
at kotlin.io.FilesKt__UtilsKt.toRelativeString(Utils.kt:117)
at kotlin.io.FilesKt__UtilsKt.relativeTo(Utils.kt:128)
at org.jetbrains.kotlin.incremental.storage.RelocatableFileToPathConverter.toPath(RelocatableFileToPathConverter.kt:24)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.save(FileToPathConverter.kt:33)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.save(FileToPathConverter.kt:30)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:443)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:422)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:105)
at org.jetbrains.kotlin.incremental.storage.LazyStorage.set(LazyStorage.kt:80)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.applyChanges(InMemoryStorage.kt:108)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.close(InMemoryStorage.kt:136)
at org.jetbrains.kotlin.incremental.storage.PersistentStorageWrapper.close(PersistentStorage.kt:124)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.forEachMapSafe(BasicMapsOwner.kt:87)
... 24 more
Suppressed: java.lang.IllegalArgumentException: this and base files have different roots: C:\Users\Administrator\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\app_settings-6.1.1\android\src\main\kotlin\com\spencerccf\app_settings\AppSettingsPlugin.kt and D:\www\dating_touchme_app\android.
at kotlin.io.FilesKt__UtilsKt.toRelativeString(Utils.kt:117)
at kotlin.io.FilesKt__UtilsKt.relativeTo(Utils.kt:128)
at org.jetbrains.kotlin.incremental.storage.RelocatableFileToPathConverter.toPath(RelocatableFileToPathConverter.kt:24)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.getHashCode(FileToPathConverter.kt:50)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.getHashCode(FileToPathConverter.kt:30)
at org.jetbrains.kotlin.com.intellij.util.containers.LinkedCustomHashMap.hashKey(LinkedCustomHashMap.java:109)
at org.jetbrains.kotlin.com.intellij.util.containers.LinkedCustomHashMap.remove(LinkedCustomHashMap.java:153)
at org.jetbrains.kotlin.com.intellij.util.containers.SLRUMap.remove(SLRUMap.java:89)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.flushAppendCache(PersistentMapImpl.java:999)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:451)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:422)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:105)
at org.jetbrains.kotlin.incremental.storage.LazyStorage.set(LazyStorage.kt:80)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.applyChanges(InMemoryStorage.kt:108)
at org.jetbrains.kotlin.incremental.storage.AppendableInMemoryStorage.applyChanges(InMemoryStorage.kt:179)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.close(InMemoryStorage.kt:136)
at org.jetbrains.kotlin.incremental.storage.AppendableSetBasicMap.close(BasicMap.kt:157)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.forEachMapSafe(BasicMapsOwner.kt:87)
... 24 more
Suppressed: java.lang.IllegalArgumentException: this and base files have different roots: C:\Users\Administrator\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\app_settings-6.1.1\android\src\main\kotlin\com\spencerccf\app_settings\AppSettingsPlugin.kt and D:\www\dating_touchme_app\android.
at kotlin.io.FilesKt__UtilsKt.toRelativeString(Utils.kt:117)
at kotlin.io.FilesKt__UtilsKt.relativeTo(Utils.kt:128)
at org.jetbrains.kotlin.incremental.storage.RelocatableFileToPathConverter.toPath(RelocatableFileToPathConverter.kt:24)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.save(FileToPathConverter.kt:33)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.save(FileToPathConverter.kt:30)
at org.jetbrains.kotlin.incremental.storage.AppendableCollectionExternalizer.save(LazyStorage.kt:151)
at org.jetbrains.kotlin.incremental.storage.AppendableCollectionExternalizer.save(LazyStorage.kt:142)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:443)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:422)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:105)
at org.jetbrains.kotlin.incremental.storage.LazyStorage.set(LazyStorage.kt:80)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.applyChanges(InMemoryStorage.kt:108)
at org.jetbrains.kotlin.incremental.storage.AppendableInMemoryStorage.applyChanges(InMemoryStorage.kt:179)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.close(InMemoryStorage.kt:136)
at org.jetbrains.kotlin.incremental.storage.PersistentStorageWrapper.close(PersistentStorage.kt:124)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.forEachMapSafe(BasicMapsOwner.kt:87)
... 24 more
Suppressed: java.lang.Exception: Could not close incremental caches in D:\www\dating_touchme_app\build\app_settings\kotlin\compileDebugKotlin\cacheable\caches-jvm\lookups: id-to-file.tab, file-to-id.tab
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.forEachMapSafe(BasicMapsOwner.kt:95)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.close(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.LookupStorage.close(LookupStorage.kt:155)
... 23 more
Suppressed: java.lang.IllegalArgumentException: this and base files have different roots: C:\Users\Administrator\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\app_settings-6.1.1\android\src\main\kotlin\com\spencerccf\app_settings\AppSettingsPlugin.kt and D:\www\dating_touchme_app\android.
at kotlin.io.FilesKt__UtilsKt.toRelativeString(Utils.kt:117)
at kotlin.io.FilesKt__UtilsKt.relativeTo(Utils.kt:128)
at org.jetbrains.kotlin.incremental.storage.RelocatableFileToPathConverter.toPath(RelocatableFileToPathConverter.kt:24)
at org.jetbrains.kotlin.incremental.storage.LegacyFileExternalizer.save(IdToFileMap.kt:51)
at org.jetbrains.kotlin.incremental.storage.LegacyFileExternalizer.save(IdToFileMap.kt:48)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:443)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:422)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:105)
at org.jetbrains.kotlin.incremental.storage.LazyStorage.set(LazyStorage.kt:80)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.applyChanges(InMemoryStorage.kt:108)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.close(InMemoryStorage.kt:136)
at org.jetbrains.kotlin.incremental.storage.PersistentStorageWrapper.close(PersistentStorage.kt:124)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.forEachMapSafe(BasicMapsOwner.kt:87)
... 25 more
Suppressed: java.lang.IllegalArgumentException: this and base files have different roots: C:\Users\Administrator\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\app_settings-6.1.1\android\src\main\kotlin\com\spencerccf\app_settings\AppSettingsPlugin.kt and D:\www\dating_touchme_app\android.
at kotlin.io.FilesKt__UtilsKt.toRelativeString(Utils.kt:117)
at kotlin.io.FilesKt__UtilsKt.relativeTo(Utils.kt:128)
at org.jetbrains.kotlin.incremental.storage.RelocatableFileToPathConverter.toPath(RelocatableFileToPathConverter.kt:24)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.getHashCode(FileToPathConverter.kt:50)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.getHashCode(FileToPathConverter.kt:30)
at org.jetbrains.kotlin.com.intellij.util.containers.LinkedCustomHashMap.hashKey(LinkedCustomHashMap.java:109)
at org.jetbrains.kotlin.com.intellij.util.containers.LinkedCustomHashMap.remove(LinkedCustomHashMap.java:153)
at org.jetbrains.kotlin.com.intellij.util.containers.SLRUMap.remove(SLRUMap.java:89)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.flushAppendCache(PersistentMapImpl.java:999)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:451)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:422)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:105)
at org.jetbrains.kotlin.incremental.storage.LazyStorage.set(LazyStorage.kt:80)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.applyChanges(InMemoryStorage.kt:108)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.close(InMemoryStorage.kt:136)
at org.jetbrains.kotlin.incremental.storage.PersistentStorageWrapper.close(PersistentStorage.kt:124)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.forEachMapSafe(BasicMapsOwner.kt:87)
... 25 more
Suppressed: java.lang.Exception: Could not close incremental caches in D:\www\dating_touchme_app\build\app_settings\kotlin\compileDebugKotlin\cacheable\caches-jvm\inputs: source-to-output.tab
... 25 more
Suppressed: java.lang.IllegalArgumentException: this and base files have different roots: C:\Users\Administrator\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\app_settings-6.1.1\android\src\main\kotlin\com\spencerccf\app_settings\AppSettingsPlugin.kt and D:\www\dating_touchme_app\android.
at kotlin.io.FilesKt__UtilsKt.toRelativeString(Utils.kt:117)
at kotlin.io.FilesKt__UtilsKt.relativeTo(Utils.kt:128)
at org.jetbrains.kotlin.incremental.storage.RelocatableFileToPathConverter.toPath(RelocatableFileToPathConverter.kt:24)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.getHashCode(FileToPathConverter.kt:50)
at org.jetbrains.kotlin.incremental.storage.FileDescriptor.getHashCode(FileToPathConverter.kt:30)
at org.jetbrains.kotlin.com.intellij.util.containers.LinkedCustomHashMap.hashKey(LinkedCustomHashMap.java:109)
at org.jetbrains.kotlin.com.intellij.util.containers.LinkedCustomHashMap.remove(LinkedCustomHashMap.java:153)
at org.jetbrains.kotlin.com.intellij.util.containers.SLRUMap.remove(SLRUMap.java:89)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.flushAppendCache(PersistentMapImpl.java:999)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:451)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:422)
at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:105)
at org.jetbrains.kotlin.incremental.storage.LazyStorage.set(LazyStorage.kt:80)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.applyChanges(InMemoryStorage.kt:108)
at org.jetbrains.kotlin.incremental.storage.AppendableInMemoryStorage.applyChanges(InMemoryStorage.kt:179)
at org.jetbrains.kotlin.incremental.storage.InMemoryStorage.close(InMemoryStorage.kt:136)
at org.jetbrains.kotlin.incremental.storage.AppendableSetBasicMap.close(BasicMap.kt:157)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner$close$1.invoke(BasicMapsOwner.kt:53)
at org.jetbrains.kotlin.incremental.storage.BasicMapsOwner.forEachMapSafe(BasicMapsOwner.kt:87)
... 24 more

49
lib/controller/discover/room_controller.dart

@ -5,8 +5,7 @@ import 'package:dating_touchme_app/rtc/rtc_manager.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import '../../pages/discover/live_room_page.dart';
import 'package:permission_handler/permission_handler.dart';
///
class RoomController extends GetxController {
@ -23,7 +22,9 @@ class RoomController extends GetxController {
/// RTC
Future<void> createRtcChannel() async {
if (isLoading.value) return ;
if (isLoading.value) return;
final granted = await _ensureRtcPermissions();
if (!granted) return;
try {
isLoading.value = true;
@ -31,6 +32,14 @@ class RoomController extends GetxController {
final base = response.data;
if (base.isSuccess && base.data != null) {
rtcChannel.value = base.data;
GetStorage storage = GetStorage();
String userId = storage.read('userId') ?? '';
String tokens = RtmTokenBuilder.buildToken(
appId: '4c2ea9dcb4c5440593a418df0fdd512d',
appCertificate: '16f34b45181a4fae8acdb1a28762fcfa',
userId: userId,
tokenExpireSeconds: 3600,
);
await _joinRtcChannel(base.data!.token, base.data!.channelId, base.data!.uid);
} else {
final message = base.message.isNotEmpty ? base.message : '创建频道失败';
@ -42,16 +51,46 @@ class RoomController extends GetxController {
isLoading.value = false;
}
}
Future<void> _joinRtcChannel(String token, String channelName, int uid) async {
Future<void> _joinRtcChannel(
String token,
String channelName,
int uid,
) async {
try {
await RTCManager.instance.joinChannel(
token: token,
channelId: channelName,
uid: uid
uid: uid,
);
} catch (e) {
SmartDialog.showToast('加入频道失败:$e');
}
}
Future<void> sendMessage(String message) async {
await RTCManager.instance.sendMessage(message);
}
Future<bool> _ensureRtcPermissions() async {
final statuses = await [Permission.camera, Permission.microphone].request();
final allGranted = statuses.values.every((status) => status.isGranted);
if (allGranted) {
return true;
}
final permanentlyDenied =
statuses.values.any((status) => status.isPermanentlyDenied);
if (permanentlyDenied) {
SmartDialog.showToast('请在系统设置中开启摄像头和麦克风权限');
await openAppSettings();
} else {
SmartDialog.showToast('请允许摄像头和麦克风权限以进入房间');
}
return false;
}
Future<void> disposeRtcResources() async {
await RTCManager.instance.dispose();
}
}

212
lib/pages/discover/discover_page.dart

@ -13,7 +13,8 @@ class DiscoverPage extends StatefulWidget {
State<DiscoverPage> createState() => _DiscoverPageState();
}
class _DiscoverPageState extends State<DiscoverPage> with AutomaticKeepAliveClientMixin{
class _DiscoverPageState extends State<DiscoverPage>
with AutomaticKeepAliveClientMixin {
late final RoomController roomController;
List<String> topNav = ["相亲", "聚会脱单"];
@ -31,13 +32,10 @@ class _DiscoverPageState extends State<DiscoverPage> with AutomaticKeepAliveClie
{"isNew": false},
];
List<String> tabList = [
"全部", "同城", "相亲视频", "相亲语音"
];
List<String> tabList = ["全部", "同城", "相亲视频", "相亲语音"];
int active = 0;
void changeNav(int active) {
print("当前项: $active");
}
@ -69,29 +67,33 @@ class _DiscoverPageState extends State<DiscoverPage> with AutomaticKeepAliveClie
child: Column(
children: [
HomeAppbar(topNav: topNav, changeNav: changeNav, right: InkWell(
onTap: () async {
await roomController.createRtcChannel();
},
child: Container(
width: 52.w,
height: 20.w,
decoration: BoxDecoration(
HomeAppbar(
topNav: topNav,
changeNav: changeNav,
right: InkWell(
onTap: () async {
await roomController.createRtcChannel();
},
child: Container(
width: 52.w,
height: 20.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20.w)),
color: const Color.fromRGBO(108, 105, 244, 1)
),
child: Center(
child: Text(
"申请红娘",
style: TextStyle(
color: const Color.fromRGBO(108, 105, 244, 1),
),
child: Center(
child: Text(
"申请红娘",
style: TextStyle(
fontSize: 10.w,
color: Colors.white,
fontWeight: FontWeight.w500
fontWeight: FontWeight.w500,
),
),
),
),
),
),),
),
Container(
width: 351.w,
height: 45.w,
@ -100,30 +102,41 @@ class _DiscoverPageState extends State<DiscoverPage> with AutomaticKeepAliveClie
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
...tabList.asMap().entries.map((entry){
...tabList.asMap().entries.map((entry) {
return Container(
margin: EdgeInsets.only(right: 27.w),
child: InkWell(
onTap: (){
onTap: () {
active = entry.key;
setState(() {
});
setState(() {});
},
child: Container(
height: 21.w,
padding: EdgeInsets.symmetric(horizontal: active == entry.key ? 30.w : 0),
padding: EdgeInsets.symmetric(
horizontal: active == entry.key ? 30.w : 0,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(21.w)),
color: Color.fromRGBO(108, 105, 244, active == entry.key ? 1 : 0)
borderRadius: BorderRadius.all(
Radius.circular(21.w),
),
color: Color.fromRGBO(
108,
105,
244,
active == entry.key ? 1 : 0,
),
),
child: Center(
child: Text(
entry.value,
style: TextStyle(
fontSize: 12.w,
color: active == entry.key ? Colors.white :const Color.fromRGBO(51, 51, 51, .7),
fontWeight: active == entry.key ? FontWeight.w700 : FontWeight.w500
fontSize: 12.w,
color: active == entry.key
? Colors.white
: const Color.fromRGBO(51, 51, 51, .7),
fontWeight: active == entry.key
? FontWeight.w700
: FontWeight.w500,
),
),
),
@ -141,17 +154,16 @@ class _DiscoverPageState extends State<DiscoverPage> with AutomaticKeepAliveClie
spacing: 7.w,
runSpacing: 7.w,
children: [
...liveList.map((e){
return LiveItem(item: e,);
...liveList.map((e) {
return LiveItem(item: e);
}),
],
),
),
)
),
],
),
)
),
],
);
}
@ -160,8 +172,6 @@ class _DiscoverPageState extends State<DiscoverPage> with AutomaticKeepAliveClie
bool get wantKeepAlive => true;
}
class LiveItem extends StatefulWidget {
final Map item;
const LiveItem({super.key, required this.item});
@ -174,7 +184,7 @@ class _LiveItemState extends State<LiveItem> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: (){
onTap: () {
Get.to(() => LiveRoomPage(id: 0));
},
child: ClipRRect(
@ -185,7 +195,7 @@ class _LiveItemState extends State<LiveItem> {
width: 171.w,
height: 171.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.w))
borderRadius: BorderRadius.all(Radius.circular(10.w)),
),
child: Image.network(
"https://picsum.photos/400",
@ -208,62 +218,63 @@ class _LiveItemState extends State<LiveItem> {
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 5.w,),
SizedBox(width: 5.w),
Image.asset(
Assets.imagesLocationIcon,
width: 6.w,
height: 7.w,
),
SizedBox(width: 3.w,),
SizedBox(width: 3.w),
Text(
"49.9km",
style: TextStyle(
fontSize: 8.w,
color: Colors.white,
fontWeight: FontWeight.w500
fontSize: 8.w,
color: Colors.white,
fontWeight: FontWeight.w500,
),
)
),
],
),
)
),
],
),
),
if(widget.item["isNew"]) Positioned(
top: 9.w,
right: 8.w,
child: Container(
width: 39.w,
height: 13.w,
decoration: BoxDecoration(
if (widget.item["isNew"])
Positioned(
top: 9.w,
right: 8.w,
child: Container(
width: 39.w,
height: 13.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(13.w)),
color: const Color.fromRGBO(0, 0, 0, .3)
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 5.w,
height: 5.w,
margin: EdgeInsets.only(right: 3.w),
decoration: BoxDecoration(
color: const Color.fromRGBO(0, 0, 0, .3),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 5.w,
height: 5.w,
margin: EdgeInsets.only(right: 3.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.w)),
color: const Color.fromRGBO(255, 209, 43, 1)
color: const Color.fromRGBO(255, 209, 43, 1),
),
),
),
Text(
"等待",
style: TextStyle(
Text(
"等待",
style: TextStyle(
fontSize: 8.w,
color: Colors.white,
fontWeight: FontWeight.w500
fontWeight: FontWeight.w500,
),
),
)
],
],
),
),
),
),
Positioned(
left: 9.w,
bottom: 6.w,
@ -277,51 +288,54 @@ class _LiveItemState extends State<LiveItem> {
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 8.w,
color: Colors.white,
fontWeight: FontWeight.w500
fontSize: 8.w,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
SizedBox(height: 2.w,),
SizedBox(height: 2.w),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"福州 | 28岁",
style: TextStyle(
fontSize: 11.w,
color: Colors.white,
fontWeight: FontWeight.w500
fontSize: 11.w,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
SizedBox(width: 5.w,),
if(widget.item["isNew"]) Container(
width: 32.w,
height: 10.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.w)),
color: const Color.fromRGBO(255, 206, 28, .8)
),
child: Center(
child: Text(
"新人",
style: TextStyle(
SizedBox(width: 5.w),
if (widget.item["isNew"])
Container(
width: 32.w,
height: 10.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(10.w),
),
color: const Color.fromRGBO(255, 206, 28, .8),
),
child: Center(
child: Text(
"新人",
style: TextStyle(
fontSize: 8.w,
color: Colors.white,
fontWeight: FontWeight.w500
fontWeight: FontWeight.w500,
),
),
),
),
)
],
)
),
],
),
)
),
],
),
),
);
}
}
}

15
lib/pages/discover/live_room_page.dart

@ -1,8 +1,10 @@
import 'package:dating_touchme_app/controller/discover/room_controller.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:dating_touchme_app/widget/live/live_room_user_header.dart';
import 'package:dating_touchme_app/widget/live/live_room_anchor_showcase.dart';
import 'package:dating_touchme_app/widget/live/live_room_seat_list.dart';
@ -21,6 +23,7 @@ class LiveRoomPage extends StatefulWidget {
}
class _LiveRoomPageState extends State<LiveRoomPage> {
late final RoomController _roomController;
String message = '';
final TextEditingController _messageController = TextEditingController();
@ -64,6 +67,16 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
@override
void initState() {
super.initState();
_roomController = Get.isRegistered<RoomController>()
? Get.find<RoomController>()
: Get.put(RoomController());
}
@override
void dispose() {
_roomController.disposeRtcResources();
_messageController.dispose();
super.dispose();
}
void _showGiftPopup() {
@ -135,7 +148,7 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
popularityText: '1263',
),
SizedBox(height: 7.w),
const LiveRoomAnchorShowcase(),
LiveRoomAnchorShowcase(),
SizedBox(height: 5.w),
const LiveRoomSeatList(),
SizedBox(height: 5.w),

33
lib/rtc/rtc_manager.dart

@ -2,12 +2,19 @@ import 'dart:convert';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:dating_touchme_app/rtc/rtm_manager.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import '../pages/discover/live_room_page.dart';
/// RTC
class RTCManager {
/// UI监听
final ValueNotifier<bool> channelJoinedNotifier = ValueNotifier<bool>(false);
RtcEngine? get engine => _engine;
bool get isInChannel => _isInChannel;
int? get currentUid => _currentUid;
//
static final RTCManager _instance = RTCManager._internal();
factory RTCManager() => _instance;
@ -19,6 +26,7 @@ class RTCManager {
bool _isInChannel = false;
String? _currentChannelId;
int? _currentUid;
int? _streamId;
//
Function(RtcConnection connection, int elapsed)? onJoinChannelSuccess;
@ -101,6 +109,7 @@ class RTCManager {
RtcEngineEventHandler(
onJoinChannelSuccess: (RtcConnection connection, int elapsed) async{
_isInChannel = true;
channelJoinedNotifier.value = true;
_currentChannelId = connection.channelId;
print('加入频道成功,频道名:${connection.channelId},耗时:${elapsed}ms');
if(connection.localUid == _currentUid){
@ -121,6 +130,9 @@ class RTCManager {
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,
@ -134,6 +146,7 @@ class RTCManager {
},
onLeaveChannel: (RtcConnection connection, RtcStats stats) {
_isInChannel = false;
channelJoinedNotifier.value = false;
_currentChannelId = null;
print('离开频道,统计信息:${stats.duration}');
if (onLeaveChannel != null) {
@ -347,6 +360,7 @@ class RTCManager {
uid: uid,
options: options ?? const ChannelMediaOptions(),
);
_streamId = await _engine?.createDataStream(DataStreamConfig(syncWithAudio: false, ordered: false));
print('正在加入频道:$channelId,UID:$uid');
}
@ -407,18 +421,20 @@ class RTCManager {
print('客户端角色已设置为:$role');
}
///
bool get isInChannel => _isInChannel;
///
Future<void> 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;
/// ID
int? get currentUid => _currentUid;
/// RTC Engine
RtcEngine? get engine => _engine;
///
Future<void> dispose() async {
try {
@ -433,6 +449,7 @@ class RTCManager {
_isInChannel = false;
_currentChannelId = null;
_currentUid = null;
channelJoinedNotifier.value = false;
print('RTC Engine disposed');
} catch (e) {
print('Failed to dispose RTC Engine: $e');

217
lib/widget/live/live_room_anchor_showcase.dart

@ -1,109 +1,157 @@
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/rtc/rtc_manager.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRoomAnchorShowcase extends StatelessWidget {
class LiveRoomAnchorShowcase extends StatefulWidget {
const LiveRoomAnchorShowcase({super.key});
@override
State<LiveRoomAnchorShowcase> createState() => _LiveRoomAnchorShowcaseState();
}
class _LiveRoomAnchorShowcaseState extends State<LiveRoomAnchorShowcase> {
final RTCManager _rtcManager = RTCManager.instance;
@override
Widget build(BuildContext context) {
return Column(
children: [
Stack(
return ValueListenableBuilder<bool>(
valueListenable: _rtcManager.channelJoinedNotifier,
builder: (context, joined, _) {
return Column(
children: [
Container(
width: 177.w,
height: 175.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(9.w)),
color: const Color.fromRGBO(47, 10, 94, 1),
),
),
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),
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),
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,
),
),
),
),
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,
SizedBox(height: 5.w),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildSideAnchorCard(
isLeft: true,
micIcon: Assets.imagesMicClose,
),
child: Center(
child: Text(
"加好友",
style: TextStyle(
fontSize: 11.w,
color: const Color.fromRGBO(117, 98, 249, 1),
),
),
_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) {
final engine = _rtcManager.engine;
if (!joined || engine == null) {
return _buildWaitingPlaceholder();
}
final localUid = _rtcManager.currentUid ?? 0;
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();
},
),
],
),
);
}
Widget _buildWaitingPlaceholder() {
return Container(
width: 177.w,
height: 175.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(9.w)),
color: const Color.fromRGBO(47, 10, 94, 1),
),
child: Center(
child: Text(
'等待主播',
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 12.w,
),
),
),
);
}
Widget _buildSideAnchorCard({
required bool isLeft,
required String micIcon,
}) {
Widget _buildSideAnchorCard({required bool isLeft, required String micIcon}) {
return Stack(
children: [
Container(
@ -169,11 +217,7 @@ class LiveRoomAnchorShowcase extends StatelessWidget {
color: const Color.fromRGBO(0, 0, 0, .65),
),
child: Center(
child: Image.asset(
micIcon,
width: 10.w,
height: 11.w,
),
child: Image.asset(micIcon, width: 10.w, height: 11.w),
),
),
SizedBox(width: 5.w),
@ -192,4 +236,3 @@ class LiveRoomAnchorShowcase extends StatelessWidget {
);
}
}

426
pubspec.lock
File diff suppressed because it is too large
View File

Loading…
Cancel
Save