diff --git a/android/.kotlin/errors/errors-1768801901551.log b/android/.kotlin/errors/errors-1768801901551.log new file mode 100644 index 00000000..72d1a39d --- /dev/null +++ b/android/.kotlin/errors/errors-1768801901551.log @@ -0,0 +1,198 @@ +kotlin version: 2.2.20 +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:74) + 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:210) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54) + 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:\Code\Flutter\FlutterApp\build\shared_preferences_android\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:218) + at org.jetbrains.kotlin.incremental.IncrementalCachesManager.close(IncrementalCachesManager.kt:55) + at kotlin.io.CloseableKt.closeFinally(Closeable.kt:46) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:293) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:128) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:684) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:94) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1810) + 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:\Code\Flutter\FlutterApp\build\shared_preferences_android\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:205) + ... 22 more + Suppressed: java.lang.IllegalArgumentException: this and base files have different roots: C:\Users\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:447) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:1007) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:455) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:447) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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:\Code\Flutter\FlutterApp\build\shared_preferences_android\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\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:447) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:1007) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:455) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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:\Code\Flutter\FlutterApp\build\shared_preferences_android\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\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:1007) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:455) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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 + + diff --git a/android/.kotlin/errors/errors-1768812536616.log b/android/.kotlin/errors/errors-1768812536616.log new file mode 100644 index 00000000..53eba616 --- /dev/null +++ b/android/.kotlin/errors/errors-1768812536616.log @@ -0,0 +1,198 @@ +kotlin version: 2.2.20 +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:74) + 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:210) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60) + at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54) + 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:\Code\Flutter\FlutterApp\build\shared_preferences_android\kotlin\compileReleaseKotlin\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:218) + at org.jetbrains.kotlin.incremental.IncrementalCachesManager.close(IncrementalCachesManager.kt:55) + at kotlin.io.CloseableKt.closeFinally(Closeable.kt:46) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:293) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:128) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:684) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:94) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1810) + 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:\Code\Flutter\FlutterApp\build\shared_preferences_android\kotlin\compileReleaseKotlin\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:205) + ... 22 more + Suppressed: java.lang.IllegalArgumentException: this and base files have different roots: C:\Users\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:447) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:1007) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:455) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:447) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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:\Code\Flutter\FlutterApp\build\shared_preferences_android\kotlin\compileReleaseKotlin\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\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:447) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:1007) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:455) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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:\Code\Flutter\FlutterApp\build\shared_preferences_android\kotlin\compileReleaseKotlin\cacheable\caches-jvm\inputs: source-to-output.tab + ... 25 more + Suppressed: java.lang.IllegalArgumentException: this and base files have different roots: C:\Users\CC\AppData\Local\Pub\Cache\hosted\pub.flutter-io.cn\shared_preferences_android-2.4.18\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\MessagesAsync.g.kt and D:\Code\Flutter\FlutterApp\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:1007) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.doPut(PersistentMapImpl.java:455) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentMapImpl.put(PersistentMapImpl.java:426) + at org.jetbrains.kotlin.com.intellij.util.io.PersistentHashMap.put(PersistentHashMap.java:106) + 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 + + diff --git a/assets/svgs/remote_alert.svg b/assets/svgs/remote_alert.svg new file mode 100644 index 00000000..ceb6d434 --- /dev/null +++ b/assets/svgs/remote_alert.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/assets/svgs/remote_back.svg b/assets/svgs/remote_back.svg new file mode 100644 index 00000000..d3160964 --- /dev/null +++ b/assets/svgs/remote_back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_battery_charging.svg b/assets/svgs/remote_battery_charging.svg new file mode 100644 index 00000000..3533dc67 --- /dev/null +++ b/assets/svgs/remote_battery_charging.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_battery_full.svg b/assets/svgs/remote_battery_full.svg new file mode 100644 index 00000000..61af371b --- /dev/null +++ b/assets/svgs/remote_battery_full.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_battery_low.svg b/assets/svgs/remote_battery_low.svg new file mode 100644 index 00000000..4ca262e8 --- /dev/null +++ b/assets/svgs/remote_battery_low.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_battery_middle.svg b/assets/svgs/remote_battery_middle.svg new file mode 100644 index 00000000..0e09af1f --- /dev/null +++ b/assets/svgs/remote_battery_middle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_battery_none.svg b/assets/svgs/remote_battery_none.svg new file mode 100644 index 00000000..11b82f55 --- /dev/null +++ b/assets/svgs/remote_battery_none.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_battery_warning.svg b/assets/svgs/remote_battery_warning.svg new file mode 100644 index 00000000..974d81a0 --- /dev/null +++ b/assets/svgs/remote_battery_warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_down.svg b/assets/svgs/remote_down.svg new file mode 100644 index 00000000..1b7db61a --- /dev/null +++ b/assets/svgs/remote_down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_expand.svg b/assets/svgs/remote_expand.svg new file mode 100644 index 00000000..5db5d6ee --- /dev/null +++ b/assets/svgs/remote_expand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_flame.svg b/assets/svgs/remote_flame.svg new file mode 100644 index 00000000..7772efda --- /dev/null +++ b/assets/svgs/remote_flame.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_flame_minus.svg b/assets/svgs/remote_flame_minus.svg new file mode 100644 index 00000000..d9fa11d0 --- /dev/null +++ b/assets/svgs/remote_flame_minus.svg @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/assets/svgs/remote_flame_plus.svg b/assets/svgs/remote_flame_plus.svg new file mode 100644 index 00000000..ac7ee1ad --- /dev/null +++ b/assets/svgs/remote_flame_plus.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/assets/svgs/remote_fresh.svg b/assets/svgs/remote_fresh.svg new file mode 100644 index 00000000..b0edef94 --- /dev/null +++ b/assets/svgs/remote_fresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_is_controlling.svg b/assets/svgs/remote_is_controlling.svg new file mode 100644 index 00000000..a4fe22d0 --- /dev/null +++ b/assets/svgs/remote_is_controlling.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_layers.svg b/assets/svgs/remote_layers.svg new file mode 100644 index 00000000..0aa0fa81 --- /dev/null +++ b/assets/svgs/remote_layers.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_lock.svg b/assets/svgs/remote_lock.svg new file mode 100644 index 00000000..207dd2fa --- /dev/null +++ b/assets/svgs/remote_lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_lock_open.svg b/assets/svgs/remote_lock_open.svg new file mode 100644 index 00000000..dcdf5b0e --- /dev/null +++ b/assets/svgs/remote_lock_open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_minus.svg b/assets/svgs/remote_minus.svg new file mode 100644 index 00000000..3dedcc64 --- /dev/null +++ b/assets/svgs/remote_minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_net.svg b/assets/svgs/remote_net.svg new file mode 100644 index 00000000..30b39244 --- /dev/null +++ b/assets/svgs/remote_net.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_plus.svg b/assets/svgs/remote_plus.svg new file mode 100644 index 00000000..12fd70c5 --- /dev/null +++ b/assets/svgs/remote_plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_power.svg b/assets/svgs/remote_power.svg new file mode 100644 index 00000000..5af32604 --- /dev/null +++ b/assets/svgs/remote_power.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_scissors.svg b/assets/svgs/remote_scissors.svg new file mode 100644 index 00000000..1c4e26ed --- /dev/null +++ b/assets/svgs/remote_scissors.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_unexpand.svg b/assets/svgs/remote_unexpand.svg new file mode 100644 index 00000000..4828b06a --- /dev/null +++ b/assets/svgs/remote_unexpand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_up.svg b/assets/svgs/remote_up.svg new file mode 100644 index 00000000..f270841d --- /dev/null +++ b/assets/svgs/remote_up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svgs/remote_video_left.svg b/assets/svgs/remote_video_left.svg new file mode 100644 index 00000000..20ecb9ed --- /dev/null +++ b/assets/svgs/remote_video_left.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/assets/svgs/remote_video_left_off.svg b/assets/svgs/remote_video_left_off.svg new file mode 100644 index 00000000..696e3ed6 --- /dev/null +++ b/assets/svgs/remote_video_left_off.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + diff --git a/assets/svgs/remote_video_right.svg b/assets/svgs/remote_video_right.svg new file mode 100644 index 00000000..846318ef --- /dev/null +++ b/assets/svgs/remote_video_right.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + diff --git a/assets/svgs/remote_video_right_off.svg b/assets/svgs/remote_video_right_off.svg new file mode 100644 index 00000000..e2cd2bea --- /dev/null +++ b/assets/svgs/remote_video_right_off.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + diff --git a/assets/svgs/remote_zap.svg b/assets/svgs/remote_zap.svg new file mode 100644 index 00000000..41b6dfe8 --- /dev/null +++ b/assets/svgs/remote_zap.svg @@ -0,0 +1,7 @@ + + + diff --git a/assets/www/webrtc/jswebrtc.min.js b/assets/www/webrtc/jswebrtc.min.js new file mode 100644 index 00000000..41d6b412 --- /dev/null +++ b/assets/www/webrtc/jswebrtc.min.js @@ -0,0 +1 @@ +var JSWebrtc={Player:null,VideoElement:null,CreateVideoElements:function(){var elements=document.querySelectorAll(".jswebrtc");for(var i=0;i=0)query_string=query_string.split("?")[1];var queries=query_string.split("&");for(var i=0;i=0){var params=app.substr(app.indexOf("?"));app=app.substr(0,app.indexOf("?"));if(params.indexOf("vhost=")>0){vhost=params.substr(params.indexOf("vhost=")+"vhost=".length);if(vhost.indexOf("&")>0){vhost=vhost.substr(0,vhost.indexOf("&"))}}}if(a.hostname==vhost){var re=/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;if(re.test(a.hostname))vhost="__defaultVhost__"}var schema="rtmp";if(rtmp_url.indexOf("://")>0)schema=rtmp_url.substr(0,rtmp_url.indexOf("://"));var port=a.port;if(!port){if(schema==="http"){port=80}else if(schema==="https"){port=443}else if(schema==="rtmp"){port=1935}else if(schema==="webrtc"||schema==="rtc"){port=1985}}var ret={url:rtmp_url,schema:schema,server:a.hostname,port:port,vhost:vhost,app:app,stream:stream};JSWebrtc.FillQuery(a.search,ret);return ret},HttpPost:function(url,data){return new Promise(function(resolve,reject){var xhr=new XMLHttpRequest;xhr.onreadystatechange=function(){if(xhr.readyState===4&&(xhr.status>=200&&xhr.status<300)){var respone=JSON.parse(xhr.responseText);xhr.onreadystatechange=new Function;xhr=null;resolve(respone)}};xhr.open("POST",url,true);xhr.timeout=5e3;xhr.responseType="text";xhr.setRequestHeader("Content-Type","application/json");xhr.send(data)})}};if(document.readyState==="complete"){JSWebrtc.CreateVideoElements()}else{document.addEventListener("DOMContentLoaded",JSWebrtc.CreateVideoElements)}JSWebrtc.VideoElement=function(){"use strict";var VideoElement=function(element){var url=element.dataset.url;if(!url){throw"VideoElement has no `data-url` attribute"}var addStyles=function(element,styles){for(var name in styles){element.style[name]=styles[name]}};this.container=element;addStyles(this.container,{display:"inline-block",position:"relative",minWidth:"80px",minHeight:"80px"});this.video=document.createElement("video");this.video.width=960;this.video.height=540;addStyles(this.video,{display:"block",width:"100%"});this.container.appendChild(this.video);this.playButton=document.createElement("div");this.playButton.innerHTML=VideoElement.PLAY_BUTTON;addStyles(this.playButton,{zIndex:2,position:"absolute",top:"0",bottom:"0",left:"0",right:"0",maxWidth:"75px",maxHeight:"75px",margin:"auto",opacity:"0.7",cursor:"pointer"});this.container.appendChild(this.playButton);var options={video:this.video};for(var option in element.dataset){try{options[option]=JSON.parse(element.dataset[option])}catch(err){options[option]=element.dataset[option]}}this.player=new JSWebrtc.Player(url,options);element.playerInstance=this.player;if(options.poster&&!options.autoplay){options.decodeFirstFrame=false;this.poster=new Image;this.poster.src=options.poster;this.poster.addEventListener("load",this.posterLoaded);addStyles(this.poster,{display:"block",zIndex:1,position:"absolute",top:0,left:0,bottom:0,right:0});this.container.appendChild(this.poster)}if(!this.player.options.streaming){this.container.addEventListener("click",this.onClick.bind(this))}if(options.autoplay){this.playButton.style.display="none"}if(this.player.audioOut&&!this.player.audioOut.unlocked){var unlockAudioElement=this.container;if(options.autoplay){this.unmuteButton=document.createElement("div");this.unmuteButton.innerHTML=VideoElement.UNMUTE_BUTTON;addStyles(this.unmuteButton,{zIndex:2,position:"absolute",bottom:"10px",right:"20px",width:"75px",height:"75px",margin:"auto",opacity:"0.7",cursor:"pointer"});this.container.appendChild(this.unmuteButton);unlockAudioElement=this.unmuteButton}this.unlockAudioBound=this.onUnlockAudio.bind(this,unlockAudioElement);unlockAudioElement.addEventListener("touchstart",this.unlockAudioBound,false);unlockAudioElement.addEventListener("click",this.unlockAudioBound,true)}};VideoElement.prototype.onUnlockAudio=function(element,ev){if(this.unmuteButton){ev.preventDefault();ev.stopPropagation()}this.player.audioOut.unlock(function(){if(this.unmuteButton){this.unmuteButton.style.display="none"}element.removeEventListener("touchstart",this.unlockAudioBound);element.removeEventListener("click",this.unlockAudioBound)}.bind(this))};VideoElement.prototype.onClick=function(ev){if(this.player.isPlaying){this.player.pause();this.playButton.style.display="block"}else{this.player.play();this.playButton.style.display="none";if(this.poster){this.poster.style.display="none"}}};VideoElement.PLAY_BUTTON=''+''+''+"";VideoElement.UNMUTE_BUTTON=''+''+''+''+''+""+"";return VideoElement}();JSWebrtc.Player=function(){"use strict";var Player=function(url,options){this.options=options||{};if(!url.match(/^webrtc?:\/\//)){throw"JSWebrtc just work with webrtc"}if(!this.options.video){throw"VideoElement is null"}this.urlParams=JSWebrtc.ParseUrl(url);this.pc=null;this.autoplay=!!options.autoplay||false;this.paused=true;if(this.autoplay)this.options.video.muted=true;this.startLoading()};Player.prototype.startLoading=function(){var _self=this;if(_self.pc){_self.pc.close()}_self.pc=new RTCPeerConnection(null);_self.pc.ontrack=function(event){_self.options.video["srcObject"]=event.streams[0]};_self.pc.addTransceiver("audio",{direction:"recvonly"});_self.pc.addTransceiver("video",{direction:"recvonly"});_self.pc.createOffer().then(function(offer){return _self.pc.setLocalDescription(offer).then(function(){return offer})}).then(function(offer){return new Promise(function(resolve,reject){var port=_self.urlParams.port||1985;var api=_self.urlParams.user_query.play||"/rtc/v1/play/";if(api.lastIndexOf("/")!=api.length-1){api+="/"}var url="http://"+_self.urlParams.server+":"+port+api;for(var key in _self.urlParams.user_query){if(key!="api"&&key!="play"){url+="&"+key+"="+_self.urlParams.user_query[key]}}var data={api:url,streamurl:_self.urlParams.url,clientip:null,sdp:offer.sdp};console.log("offer: "+JSON.stringify(data));JSWebrtc.HttpPost(url,JSON.stringify(data)).then(function(res){console.log("answer: "+JSON.stringify(res));resolve(res.sdp)},function(rej){reject(rej)})})}).then(function(answer){return _self.pc.setRemoteDescription(new RTCSessionDescription({type:"answer",sdp:answer}))}).catch(function(reason){throw reason});if(this.autoplay){this.play()}};Player.prototype.play=function(ev){if(this.animationId){return}this.animationId=requestAnimationFrame(this.update.bind(this));this.paused=false};Player.prototype.pause=function(ev){if(this.paused){return}cancelAnimationFrame(this.animationId);this.animationId=null;this.isPlaying=false;this.paused=true;this.options.video.pause();if(this.options.onPause){this.options.onPause(this)}};Player.prototype.stop=function(ev){this.pause()};Player.prototype.destroy=function(){this.pause();this.pc&&this.pc.close()&&this.pc.destroy();this.audioOut&&this.audioOut.destroy()};Player.prototype.update=function(){this.animationId=requestAnimationFrame(this.update.bind(this));if(this.options.video.readyState<4){return}if(!this.isPlaying){this.isPlaying=true;this.options.video.play();if(this.options.onPlay){this.options.onPlay(this)}}};return Player}(); \ No newline at end of file diff --git a/assets/www/webrtc/websocket.js b/assets/www/webrtc/websocket.js deleted file mode 100644 index 782351c7..00000000 --- a/assets/www/webrtc/websocket.js +++ /dev/null @@ -1,117 +0,0 @@ -class WebSocketClient { - constructor(url) { - this.url = url; - this.socket = null; - this.connected = false; - this.reconnecting = false; - this.reconnectInterval = 3000; // 重连间隔(ms) - this.maxReconnectAttempts = 10; // 最大重连次数 - this.reconnectAttempts = 0; - this.messageQueue = []; // 消息队列 - - // 事件回调 - this.onConnect = null; - this.onMessage = null; - this.onClose = null; - this.onError = null; - } - - // 连接WebSocket服务器 - connect() { - if (this.socket && (this.socket.readyState === WebSocket.CONNECTING || this.socket.readyState === WebSocket.OPEN)) { - return; - } - - this.socket = new WebSocket(this.url); - - this.socket.onopen = (event) => { - this.connected = true; - this.reconnecting = false; - this.reconnectAttempts = 0; - console.log('WebSocket连接已建立'); - - // 发送队列中的所有消息 - this._sendQueuedMessages(); - - if (typeof this.onConnect === 'function') { - this.onConnect(event); - } - }; - - this.socket.onmessage = (event) => { - console.log('收到消息:', event.data); - this.onMessage(event.data); - }; - - this.socket.onclose = (event) => { - this.connected = false; - console.log('WebSocket连接已关闭,代码:', event.code, '原因:', event.reason); - - if (typeof this.onClose === 'function') { - this.onClose(event); - } - - // 非主动关闭时尝试重连 - if (!this.reconnecting && event.code !== 1000) { - this._scheduleReconnect(); - } - }; - - this.socket.onerror = (error) => { - console.error('WebSocket错误:', error); - if (typeof this.onError === 'function') { - this.onError(error); - } - }; - } - - // 发送消息 - send(message) { - if (this.connected && this.socket.readyState === WebSocket.OPEN) { - this.socket.send(message); - } else { - // 连接未建立,将消息加入队列 - this.messageQueue.push(message); - if (!this.reconnecting) { - this._scheduleReconnect(); - } - } - } - - // 关闭连接 - close(code = 1000, reason = '') { - this.reconnecting = false; - if (this.socket) { - this.socket.close(code, reason); - } - } - - // 安排重连 - _scheduleReconnect() { - if (this.reconnectAttempts < this.maxReconnectAttempts) { - this.reconnecting = true; - this.reconnectAttempts++; - const delay = this.reconnectInterval * Math.min(1, this.reconnectAttempts / 3); // 指数退避 - - console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts}),${delay/1000}秒后...`); - setTimeout(() => { - console.log('正在重连...'); - this.connect(); - }, delay); - } else { - console.error('达到最大重连次数,停止重连'); - this.reconnecting = false; - } - } - - // 发送队列中的消息 - _sendQueuedMessages() { - if (this.messageQueue.length > 0) { - console.log(`发送队列中的${this.messageQueue.length}条消息`); - this.messageQueue.forEach(message => { - this.socket.send(message); - }); - this.messageQueue = []; - } - } - } \ No newline at end of file diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 00000000..fa0b357c --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/core/theme/AppTheme.dart b/lib/core/theme/AppTheme.dart index d25acfb7..ce4ad355 100644 --- a/lib/core/theme/AppTheme.dart +++ b/lib/core/theme/AppTheme.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; class AppTheme { @@ -11,6 +13,7 @@ class AppTheme { return ThemeData( useMaterial3: true, brightness: Brightness.light, + fontFamily: (Platform.isWindows) ? 'Microsoft YaHei' : null, // 1. 整体背景色 scaffoldBackgroundColor: _offWhite, diff --git a/lib/features/remote_control/presentation/pages/remote_control_page.dart b/lib/features/remote_control/presentation/pages/remote_control_page.dart index 9b10ff0f..55930ca5 100644 --- a/lib/features/remote_control/presentation/pages/remote_control_page.dart +++ b/lib/features/remote_control/presentation/pages/remote_control_page.dart @@ -13,7 +13,7 @@ import '../widgets/emergency_overlay.dart'; import '../widgets/left_joystick_area.dart'; import '../widgets/my_video_player.dart'; import '../widgets/right_joystick_area.dart'; -import '../widgets/top_status_bar.dart'; // 假设路径 +import '../widgets/top_status_bar.dart'; class RemoteControlPage extends StatefulWidget { const RemoteControlPage({super.key}); @@ -26,17 +26,17 @@ class _RemoteControlPageState extends State { @override void initState() { super.initState(); - // 强制横屏与沉浸式 + // 1. 锁定横屏 SystemChrome.setPreferredOrientations([ DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight, ]); + // 2. 隐藏状态栏和虚拟按键 SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); } @override void dispose() { - // 恢复竖屏 SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); super.dispose(); @@ -44,16 +44,12 @@ class _RemoteControlPageState extends State { @override Widget build(BuildContext context) { - // 1. 监听全局状态 (实现方案二:掉线实时刷新) + // 监听全局状态(这些通常不随摇杆频繁变化) final userState = context.watch().state; final deviceState = context.watch().state; final currentDevice = deviceState.selectedDevice; - // 2. 监听局部遥控状态 (对应 Android Compose 的 collectAsState) - final remoteState = context.watch().state; - - // 如果设备突然掉线,显示遮罩 (对应 Android 的 finishEvent 逻辑) - if (currentDevice == null /*|| !currentDevice.isOnline*/ ) { + if (currentDevice == null) { return _buildOfflineScaffold(); } @@ -61,56 +57,99 @@ class _RemoteControlPageState extends State { backgroundColor: Colors.black, body: Stack( children: [ - // 底层:视频流 (传入 device 和 user) + // 【优化层 1】底层视频:传入固定参数,加上 RepaintBoundary Positioned.fill( child: MyVideoPlayer( deviceIp: TCPConsts.TCP_IP, device: currentDevice, - user: userState.user!, // 假设 AppUserCubit 存有 user 实体 + user: userState.user!, ), ), - // 中层:急停呼吸灯光晕 (对应 Android 的 EmergencyBreathingOverlay) - if (remoteState.isEmergency) - const Positioned.fill(child: EmergencyOverlay()), + // 【优化层 2】中层:急停效果(只在 isEmergency 变化时才触发显示/隐藏) + BlocBuilder( + buildWhen: (p, c) => p.isEmergency != c.isEmergency, + builder: (context, state) { + return state.isEmergency ? const Positioned.fill(child: EmergencyOverlay()) : const SizedBox.shrink(); + }, + ), - // 顶层:UI 控制层 + // 【优化层 3】顶层:UI 控制层 SafeArea( child: Column( children: [ - // 顶部胶囊状态条 const TopStatusBar(), - - // 左右摇杆及中间控制区 Expanded( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 10, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - // 左摇杆:前后控制 - LeftJoystickArea(isLocked: remoteState.isLocked), + child: LayoutBuilder( + builder: (context, constraints) { + final double sideAreaWidth = constraints.maxWidth * 0.25; + final double centerAreaWidth = constraints.maxWidth * 0.5; - // 中间区:底盘升降/急停按钮 - const CenterControlArea(), + // 关键:使用 BlocBuilder 局部刷新摇杆,不要 watch 整个 Page + return BlocBuilder( + // 只有 isLocked 改变时才重构摇杆区域,摇杆坐标改变由内部处理 + buildWhen: (p, c) => p.isLocked != c.isLocked, + builder: (context, remoteState) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + // 左摇杆 + SizedBox( + width: sideAreaWidth, + child: Align( + alignment: Alignment.bottomCenter, + child: LeftJoystickArea( + isLocked: remoteState.isLocked, + width: sideAreaWidth * 0.5 * 0.7, + ), + ), + ), - // 右摇杆:左右控制 - RightJoystickArea(isLocked: remoteState.isLocked), - ], - ), + // 中间区 + SizedBox( + width: centerAreaWidth, + child: Align( + alignment: Alignment.bottomCenter, + child: CenterControlArea(totalWidth: centerAreaWidth), + ), + ), + + // 右摇杆 + SizedBox( + width: sideAreaWidth, + child: Align( + alignment: Alignment.bottomCenter, + child: RightJoystickArea( + isLocked: remoteState.isLocked, + width: sideAreaWidth * 0.5 * 0.7, + ), + ), + ), + ], + ), + ); + }, + ); + }, ), ), ], ), ), - // 权限请求弹窗 (对应 Android 的 PermissionRequestDialog) - if (remoteState.showPermissionRequestDialog) - _buildPermissionDialog(context, remoteState), + // 权限弹窗 + BlocBuilder( + buildWhen: (p, c) => p.showPermissionRequestDialog != c.showPermissionRequestDialog, + builder: (context, state) { + if (state.showPermissionRequestDialog) { + return _buildPermissionDialog(context, state); + } + return const SizedBox.shrink(); + }, + ), ], ), ); diff --git a/lib/features/remote_control/presentation/widgets/center_control_area.dart b/lib/features/remote_control/presentation/widgets/center_control_area.dart index 74e45a08..0ce1c3aa 100644 --- a/lib/features/remote_control/presentation/widgets/center_control_area.dart +++ b/lib/features/remote_control/presentation/widgets/center_control_area.dart @@ -1,93 +1,90 @@ +import 'package:cc_ui_kit/cc_ui_kit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../bloc/remote_control_cubit.dart'; import 'emergency_stop_button.dart'; -import 'middle_expandslider.dart'; class CenterControlArea extends StatelessWidget { - const CenterControlArea({super.key}); + final double totalWidth; + + const CenterControlArea({super.key, required this.totalWidth}); @override Widget build(BuildContext context) { final remoteState = context.watch().state; - // 如果处于急停激活状态,根据 Compose 逻辑,中间只显示急停状态 if (remoteState.isEmergency) { return const Column( mainAxisSize: MainAxisSize.min, - children: [ - EmergencyStopButton(), // 刚才写的带 3 秒解除逻辑的按钮 - SizedBox(height: 16), - ], + children: [EmergencyStopButton(), SizedBox(height: 16)], ); } - return Container( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - // 左推杆:底盘升降 (对应 Compose 的第一个 MiddleExpandSlider) - _buildVerticalSlider( - label: "底盘", - onTop: () => - context.read().updateChassisLift(1), - onMiddle: () => - context.read().updateChassisLift(0), - onBottom: () => - context.read().updateChassisLift(2), - iconTop: Icons.expand_less, - iconMiddle: Icons.layers, - iconBottom: Icons.expand_more, + return LayoutBuilder( + builder: (context, constraints) { + final double expandedWidth = totalWidth * 0.25; + final double emergencyStopButtonWidth = constraints.maxWidth * 0.5; + + return Container( + height: 180, + // 这里不再使用 Row 包装三个 Expanded,而是直接固定宽度 + child: Row( + //mainAxisSize: MainAxisSize.min, // 极其重要:防止在 spaceBetween 下挤压 + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // 左摇杆 (3份) + _buildSliderBox(expandedWidth, "底盘", true), + + // 急停 (2份) + SizedBox( + width: emergencyStopButtonWidth, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + EmergencyStopButton(width: emergencyStopButtonWidth), + + SizedBox(height: totalWidth * 0.05), + + const Text( + '双击空白处切换视角', + style: TextStyle( + fontSize: 8, + color: Colors.grey, + ), + ), + ], + ), + ), + + // 右摇杆 (3份) + _buildSliderBox(expandedWidth, "备用", false), + ], ), + ); + }, + ); + } - const SizedBox(width: 24), - - // 中间:急停按钮 - const EmergencyStopButton(), - - const SizedBox(width: 24), - - // 右推杆:备用/其他 (对应 Compose 的第二个 MiddleExpandSlider) - _buildVerticalSlider( - label: "云台", - onTop: () {}, // 预留接口 - onMiddle: () {}, - onBottom: () {}, - iconTop: Icons.keyboard_arrow_up, - iconMiddle: Icons.videocam, - iconBottom: Icons.keyboard_arrow_down, + Widget _buildSliderBox(double boxWidth, String label, bool isLeft) { + return SizedBox( + width: boxWidth, + child: Center( + child: SizedBox( + width: boxWidth * 0.6, + height: boxWidth * 0.6 * 3, + child: CCExpandSlider( + label: label, + svgStart: "assets/svgs/remote_up.svg", + svgCenter: "assets/svgs/remote_layers.svg", + svgEnd: "assets/svgs/remote_down.svg", + onStart: () => print("$label start"), + onCenter: () => print("$label center"), + onEnd: () => print("$label end"), + iconSize: boxWidth * 0.6 * 0.35, ), - ], + ), ), ); } - - // 快捷构建推杆的方法 - Widget _buildVerticalSlider({ - required String label, - required VoidCallback onTop, - required VoidCallback onMiddle, - required VoidCallback onBottom, - required IconData iconTop, - required IconData iconMiddle, - required IconData iconBottom, - }) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - MiddleExpandSlider( - onTop: onTop, - onMiddle: onMiddle, - onBottom: onBottom, - iconTop: iconTop, - iconMiddle: iconMiddle, - iconBottom: iconBottom, - ), - const SizedBox(height: 8), - Text(label, style: const TextStyle(color: Colors.grey, fontSize: 12)), - ], - ); - } } diff --git a/lib/features/remote_control/presentation/widgets/emergency_stop_button.dart b/lib/features/remote_control/presentation/widgets/emergency_stop_button.dart index c2f38383..4626ebee 100644 --- a/lib/features/remote_control/presentation/widgets/emergency_stop_button.dart +++ b/lib/features/remote_control/presentation/widgets/emergency_stop_button.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; import '../bloc/remote_control_cubit.dart'; class EmergencyStopButton extends StatefulWidget { - const EmergencyStopButton({super.key}); + final double width; + const EmergencyStopButton({ + Key? key, + this.width = 120, + }) : super(key: key); @override State createState() => _EmergencyStopButtonState(); @@ -19,7 +24,6 @@ class _EmergencyStopButtonState extends State @override void initState() { super.initState(); - // 对应 Compose 中的 durationMs = 3000L _progressController = AnimationController( vsync: this, duration: const Duration(seconds: 3), @@ -40,6 +44,9 @@ class _EmergencyStopButtonState extends State .state .isEmergency; + final width = widget.width * 0.65; + final double indicatorSize = width + 10; + return GestureDetector( // 1. 处理点击:仅在未急停时触发 onTap: () { @@ -75,20 +82,19 @@ class _EmergencyStopButtonState extends State // 对应 Compose 的 Canvas 绘制进度环 if (_isPressing) SizedBox( - width: 130, - height: 130, + width: indicatorSize, + height: indicatorSize, child: CircularProgressIndicator( value: _progressController.value, strokeWidth: 10, color: Colors.red.withOpacity(0.8), - backgroundColor: Colors.red.withOpacity(0.2), + backgroundColor: Colors.red.withValues(alpha: 0.2), ), ), - // 按钮本体 (对应 Android 的 120.dp Box) Container( - width: 120, - height: 120, + width: width, + height: width, decoration: BoxDecoration( shape: BoxShape.circle, // 根据状态切换颜色,对应 Color(0x80C53030) @@ -98,7 +104,7 @@ class _EmergencyStopButtonState extends State boxShadow: isEmergencyActive ? [ BoxShadow( - color: Colors.red.withOpacity(0.5), + color: Colors.red.withValues(alpha: 0.5), blurRadius: 20, ), ] @@ -107,16 +113,12 @@ class _EmergencyStopButtonState extends State child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.report_problem_outlined, // 对应 remote_alert 图标 - color: Colors.white, - size: 45, - ), + SvgPicture.asset('assets/svgs/remote_alert.svg',width: width * 0.4,color: Colors.white.withValues(alpha: 0.5),), const SizedBox(height: 6), Text( isEmergencyActive ? "急停中" : "急停", - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: Colors.white.withValues(alpha: 0.5), fontSize: 14, fontWeight: FontWeight.w900, ), diff --git a/lib/features/remote_control/presentation/widgets/left_joystick_area.dart b/lib/features/remote_control/presentation/widgets/left_joystick_area.dart index d920e3af..04653f8e 100644 --- a/lib/features/remote_control/presentation/widgets/left_joystick_area.dart +++ b/lib/features/remote_control/presentation/widgets/left_joystick_area.dart @@ -7,8 +7,13 @@ import '../bloc/remote_control_cubit.dart'; class LeftJoystickArea extends StatelessWidget { final bool isLocked; + final double width; - const LeftJoystickArea({super.key, required this.isLocked}); + const LeftJoystickArea({ + Key? key, + required this.isLocked, + required this.width, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -22,7 +27,7 @@ class LeftJoystickArea extends StatelessWidget { duration: const Duration(milliseconds: 300), opacity: isLocked ? 0.3 : 1.0, // 锁定后变透明 child: CCJoystick( - radius: 100, // 对应 size(200.dp) + radius: width, // 对应 size(200.dp) axisHint: AxisHint.forwardBackward, onValueChanged: (value) { // 对应 viewModel.updateOriginY(y) @@ -35,15 +40,16 @@ class LeftJoystickArea extends StatelessWidget { ), ), ), - const SizedBox(height: 8), - const Text( + const SizedBox(height: 18), + Text( "前后控制", style: TextStyle( - color: Colors.grey, + color: Colors.white.withValues(alpha: 0.5), fontSize: 12, fontWeight: FontWeight.w500, ), ), + const SizedBox(height: 2), ], ); } diff --git a/lib/features/remote_control/presentation/widgets/my_video_player.dart b/lib/features/remote_control/presentation/widgets/my_video_player.dart index 5bc74bd4..d1a849da 100644 --- a/lib/features/remote_control/presentation/widgets/my_video_player.dart +++ b/lib/features/remote_control/presentation/widgets/my_video_player.dart @@ -1,5 +1,5 @@ import 'dart:io'; - +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:maibu_satabot_v2/core/domain/entities/user_entity.dart'; @@ -9,6 +9,7 @@ class MyVideoPlayer extends StatefulWidget { final String deviceIp; final DeviceEntity device; final UserEntity user; + const MyVideoPlayer({ Key? key, required this.deviceIp, @@ -21,11 +22,10 @@ class MyVideoPlayer extends StatefulWidget { } class _MyVideoPlayerState extends State { - // 1. 定义本地服务器 InAppLocalhostServer? _localServer; InAppWebViewController? _webViewController; bool _isServerRunning = false; - var actualPort; + int? actualPort; @override void initState() { @@ -33,79 +33,73 @@ class _MyVideoPlayerState extends State { _startServer(); } - // 2. 启动服务器 (适用于 Windows, Android, iOS) Future _startServer() async { - // 1. 手动找一个系统分配的空闲端口 int availablePort = 0; try { - // 绑定到端口 0,系统会随机分配一个 var socket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0); availablePort = socket.port; - await socket.close(); // 立即释放,给 LocalhostServer 用 + await socket.close(); } catch (e) { - availablePort = 8080; // 万一失败,给个保底 + availablePort = 8080; } - // 2. 使用找到的明确端口启动 _localServer = InAppLocalhostServer(port: availablePort); await _localServer!.start(); - - // 确认端口(有些版本需要通过这种方式确认) actualPort = availablePort; - debugPrint("服务器启动在端口: $actualPort"); - - setState(() { - _isServerRunning = true; - }); + if (mounted) { + setState(() { + _isServerRunning = true; + }); + } } @override void dispose() { - _localServer?.close(); // 页面销毁时关闭服务器 + _localServer?.close(); super.dispose(); } @override Widget build(BuildContext context) { + // 防止父级因其他状态 setState 导致视频闪烁 if (!_isServerRunning) { - return const Center(child: CircularProgressIndicator()); + return const Center(child: CircularProgressIndicator(color: Colors.white)); } - return InAppWebView( - // 3. 通过 localhost 地址访问,而不是 file:// - initialUrlRequest: URLRequest( - url: WebUri("http://localhost:$actualPort/assets/www/playwebrtc.html"), + // [性能关键] RepaintBoundary 必须包裹渲染密集型的 WebView + return RepaintBoundary( + child: InAppWebView( + initialUrlRequest: URLRequest( + url: WebUri("http://localhost:$actualPort/assets/www/webrtc/playwebrtc.html"), + ), + initialSettings: InAppWebViewSettings( + javaScriptEnabled: true, + mediaPlaybackRequiresUserGesture: false, + allowsInlineMediaPlayback: true, + preferredContentMode: UserPreferredContentMode.MOBILE, + // 优化性能:禁用滚动,开启硬件加速 + supportZoom: false, + disableHorizontalScroll: true, + disableVerticalScroll: true, + hardwareAcceleration: true, + ), + onWebViewCreated: (controller) => _webViewController = controller, + onPermissionRequest: (controller, request) async { + return PermissionResponse( + resources: request.resources, + action: PermissionResponseAction.GRANT, + ); + }, + onLoadStop: (controller, url) { + _initWebRTC(widget.deviceIp, widget.device.deviceName, widget.user.token); + }, ), - initialSettings: InAppWebViewSettings( - javaScriptEnabled: true, - mediaPlaybackRequiresUserGesture: false, - allowsInlineMediaPlayback: true, - // Windows/Android 开启硬件加速优化图传 - preferredContentMode: UserPreferredContentMode.DESKTOP, - ), - onWebViewCreated: (controller) => _webViewController = controller, - onPermissionRequest: (controller, request) async { - return PermissionResponse( - resources: request.resources, - action: PermissionResponseAction.GRANT, - ); - }, - onLoadStop: (controller, url) { - _initWebRTC( - widget.deviceIp, - widget.device.deviceName, - widget.user.token, - ); - }, ); } void _initWebRTC(String deviceIp, String deviceId, String token) { - final streamUrl = - "webrtc://$deviceIp/live/livestream/$deviceId?token=$token"; - _webViewController?.evaluateJavascript( - source: "setStreamUrl('$streamUrl')", - ); + final streamUrl = "webrtc://$deviceIp/live/livestream/$deviceId?token=$token"; + _webViewController?.evaluateJavascript(source: "setStreamUrl('$streamUrl')"); } -} +} \ No newline at end of file diff --git a/lib/features/remote_control/presentation/widgets/right_joystick_area.dart b/lib/features/remote_control/presentation/widgets/right_joystick_area.dart index 6c92c3bf..011a04a6 100644 --- a/lib/features/remote_control/presentation/widgets/right_joystick_area.dart +++ b/lib/features/remote_control/presentation/widgets/right_joystick_area.dart @@ -7,8 +7,13 @@ import '../bloc/remote_control_cubit.dart'; class RightJoystickArea extends StatelessWidget { final bool isLocked; + final double width; - const RightJoystickArea({super.key, required this.isLocked}); + const RightJoystickArea({ + Key? key, + required this.isLocked, + required this.width, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -22,7 +27,7 @@ class RightJoystickArea extends StatelessWidget { duration: const Duration(milliseconds: 300), opacity: isLocked ? 0.3 : 1.0, // 锁定后变透明/灰色 child: CCJoystick( - radius: 100, // 对应 size(200.dp) + radius: width, // 对应 size(200.dp) axisHint: AxisHint.leftRight, // 关键:指定为左右控制 onValueChanged: (value) { // 对应 viewModel.updateOriginX(x) @@ -35,12 +40,12 @@ class RightJoystickArea extends StatelessWidget { ), ), ), - const SizedBox(height: 8), + const SizedBox(height: 18), // 2. 底部文字:对应 Text("左右控制", color = Color.Gray) - const Text( + Text( "左右控制", style: TextStyle( - color: Colors.grey, + color: Colors.white.withValues(alpha: 0.5), fontSize: 12, fontWeight: FontWeight.w500, ), diff --git a/lib/main.dart b/lib/main.dart index e7201b37..636173db 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; @@ -39,4 +41,5 @@ class MyApp extends StatelessWidget { ), ); } -} + +} \ No newline at end of file diff --git a/packages/cc_ui_kit/lib/cc_ui_kit.dart b/packages/cc_ui_kit/lib/cc_ui_kit.dart index 2a27a7fd..77aa68ce 100644 --- a/packages/cc_ui_kit/lib/cc_ui_kit.dart +++ b/packages/cc_ui_kit/lib/cc_ui_kit.dart @@ -3,3 +3,4 @@ library cc_ui_kit; export 'src/cc_joystick.dart'; export 'src/cc_primary_button.dart'; export 'src/cc_primary_image_button.dart'; +export 'src/cc_expand_slider.dart'; diff --git a/packages/cc_ui_kit/lib/src/cc_expand_slider.dart b/packages/cc_ui_kit/lib/src/cc_expand_slider.dart new file mode 100644 index 00000000..23a231a6 --- /dev/null +++ b/packages/cc_ui_kit/lib/src/cc_expand_slider.dart @@ -0,0 +1,223 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +enum SliderAxis { horizontal, vertical } + +class CCExpandSlider extends StatefulWidget { + final SliderAxis axis; + final bool isSpring; + final String label; + final String svgStart; + final String svgCenter; + final String svgEnd; + final double iconSize; + final double borderRadius; + final VoidCallback? onStart; + final VoidCallback? onCenter; + final VoidCallback? onEnd; + + const CCExpandSlider({ + super.key, + this.axis = SliderAxis.vertical, + this.isSpring = true, + required this.svgStart, + required this.svgCenter, + required this.svgEnd, + required this.label, + this.iconSize = 22.0, + this.borderRadius = 12.0, + this.onStart, + this.onCenter, + this.onEnd, + }); + + @override + State createState() => _CCExpandSliderState(); +} + +class _CCExpandSliderState extends State { + double _alignmentValue = 0.0; + bool _isExpanded = false; + Timer? _tickTimer; + + @override + void dispose() { + _tickTimer?.cancel(); + super.dispose(); + } + + void _startTicking() { + _tickTimer?.cancel(); + _tickTimer = Timer.periodic(const Duration(milliseconds: 100), (timer) { + if (!_isExpanded) return; + if (_alignmentValue < -0.5) { + widget.onStart?.call(); + } else if (_alignmentValue > 0.5) { + widget.onEnd?.call(); + } else { + widget.onCenter?.call(); + } + }); + } + + void _safeSetExpanded(bool expanded) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + setState(() { + _isExpanded = expanded; + if (!expanded) { + _alignmentValue = 0.0; + _tickTimer?.cancel(); + } + }); + if (expanded) _startTicking(); + } + }); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + final isVertical = widget.axis == SliderAxis.vertical; + final double maxLength = isVertical ? constraints.maxHeight : constraints.maxWidth; + final double minLength = isVertical ? constraints.maxWidth : constraints.maxHeight; + final double currentLength = _isExpanded ? maxLength : minLength; + final double dragArea = (maxLength - minLength) / 2; + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onPanDown: (_) => _safeSetExpanded(true), + onPanUpdate: (details) { + if (!_isExpanded) return; + setState(() { + final delta = isVertical ? details.delta.dy : details.delta.dx; + _alignmentValue = (_alignmentValue + delta / (dragArea > 0 ? dragArea : 1)).clamp(-1.0, 1.0); + }); + }, + onPanEnd: (_) { + if (widget.isSpring) widget.onCenter?.call(); + HapticFeedback.lightImpact(); + _safeSetExpanded(false); + }, + onPanCancel: () => _safeSetExpanded(false), + child: Stack( + alignment: Alignment.center, + clipBehavior: Clip.none, + children: [ + // 1. 轨道背景:从中向外展开 + AnimatedContainer( + duration: const Duration(milliseconds: 200), + curve: Curves.easeOutCubic, + width: isVertical ? minLength : currentLength, + height: isVertical ? currentLength : minLength, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0), + borderRadius: BorderRadius.circular(widget.borderRadius), + border: Border.all(color: Colors.white.withOpacity(0.3)), + ), + child: _buildSegmentedContent(isVertical, maxLength, minLength), + ), + + // 2. 核心修改:文字位置固定逻辑 + // 我们用一个和“缩起状态”一样大的 SizedBox 作为参考 + if (!_isExpanded) + IgnorePointer( // 防止文字遮挡点击 + child: SizedBox( + width: minLength, + height: minLength, + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Positioned( + bottom: -22, // 这里的 -22 是相对于“缩起时正方形”的底部 + child: Text( + widget.label, + style: TextStyle( + color: Colors.white.withValues(alpha: 0.5), + fontSize: 10, + fontWeight: FontWeight.w500 + ), + ), + ), + ], + ), + ), + ), + ], + ), + ); + }); + } + + Widget _buildSegmentedContent(bool isVertical, double maxLength, double minLength) { + final double segmentHeight = maxLength / 3; + + return Stack( + alignment: Alignment.center, + children: [ + if (_isExpanded) ...[ + Positioned( + top: 0, + left: 0, + right: 0, + height: segmentHeight, + child: Center(child: _svgItem(widget.svgStart, -1)), + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + height: segmentHeight, + child: Center(child: _svgItem(widget.svgEnd, 1)), + ), + ], + + Center( + child: SizedBox( + width: minLength, + height: minLength, + child: Center(child: _svgItem(widget.svgCenter, 0)), + ), + ), + + if (_isExpanded) + Padding( + padding: const EdgeInsets.all(4.0), + child: Align( + alignment: isVertical ? Alignment(0, _alignmentValue) : Alignment(_alignmentValue, 0), + child: Container( + width: minLength * 0.82, + height: minLength * 0.82, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.28), + borderRadius: BorderRadius.circular(widget.borderRadius * 0.7), + ), + ), + ), + ), + ], + ); + } + + Widget _svgItem(String assetPath, int type) { + Color color = Colors.white30; + if (_isExpanded) { + if (type == -1 && _alignmentValue < -0.5) color = const Color(0xFF0076F9); + else if (type == 1 && _alignmentValue > 0.5) color = const Color(0xFF0076F9); + else if (type == 0 && _alignmentValue.abs() <= 0.5) color = const Color(0xFF0076F9); + else color = Colors.white; + } else { + color = Colors.white; + } + return SvgPicture.asset( + assetPath, + width: widget.iconSize, + height: widget.iconSize, + fit: BoxFit.contain, + colorFilter: ColorFilter.mode(color, BlendMode.srcIn), + ); + } +} \ No newline at end of file diff --git a/packages/cc_ui_kit/lib/src/cc_joystick.dart b/packages/cc_ui_kit/lib/src/cc_joystick.dart index a6f90ffd..28998b59 100644 --- a/packages/cc_ui_kit/lib/src/cc_joystick.dart +++ b/packages/cc_ui_kit/lib/src/cc_joystick.dart @@ -132,16 +132,16 @@ class JoystickPainter extends CustomPainter { paint ..color = const Color(0x6600BFFF) ..style = PaintingStyle.stroke - ..strokeWidth = 2; + ..strokeWidth = 1; canvas.drawCircle(center, radius + 8, paint); // 2. 绘制箭头 (对应 drawArrow 逻辑) paint ..color = Colors.cyan.withOpacity(0.5) ..strokeCap = StrokeCap.round - ..strokeWidth = 4; + ..strokeWidth = 2; - const arrowSize = 20.0; + const arrowSize = 10.0; if (axisHint == AxisHint.forwardBackward) { _drawArrow( canvas, diff --git a/packages/cc_ui_kit/pubspec.yaml b/packages/cc_ui_kit/pubspec.yaml index e41ecc92..601e30d9 100644 --- a/packages/cc_ui_kit/pubspec.yaml +++ b/packages/cc_ui_kit/pubspec.yaml @@ -11,6 +11,8 @@ dependencies: flutter: sdk: flutter + flutter_svg: ^2.2.3 + dev_dependencies: flutter_test: sdk: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 2c86d3d1..c80ee226 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -84,6 +84,9 @@ dependencies: # ===== 图片 ===== flutter_svg: ^2.2.3 + # ===== 屏幕适配,高刷等 ===== +# flutter_displaymode: ^0.7.0 + dev_dependencies: flutter_test: @@ -122,7 +125,8 @@ flutter: # - images/a_dot_ham.jpeg assets: - assets/images/ - - assets/www/ + - assets/www/webrtc/ + - assets/svgs/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images