远程遥控界面编写

This commit is contained in:
CC
2026-01-19 18:24:24 +08:00
parent 289e706337
commit 32b702fb6d
48 changed files with 1118 additions and 303 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="currentColor">
<path d="M8.688 2a2 2 0 0 0-1.414.586L2.586 7.274A2 2 0 0 0 2 8.688v6.624a2 2 0 0 0 .586 1.414l4.688 4.688A2 2 0 0 0 8.688 22h6.624a2 2 0 0 0 1.414-.586l4.688-4.688A2 2 0 0 0 22 15.312V8.688a2 2 0 0 0-.586-1.414l-4.688-4.688A2 2 0 0 0 15.312 2H8.688z"/>
<path d="M12 7.5c.552 0 1 .448 1 1v4a1 1 0 1 1-2 0v-4c0-.552.448-1 1-1z" fill="currentColor"/>
<circle cx="12" cy="16.5" r="1" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 542 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-left-icon lucide-arrow-left"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>

After

Width:  |  Height:  |  Size: 289 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-battery-charging-icon lucide-battery-charging"><path d="m11 7-3 5h4l-3 5"/><path d="M14.856 6H16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.935"/><path d="M22 14v-4"/><path d="M5.14 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2.936"/></svg>

After

Width:  |  Height:  |  Size: 423 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-battery-full-icon lucide-battery-full"><path d="M10 10v4"/><path d="M14 10v4"/><path d="M22 14v-4"/><path d="M6 10v4"/><rect x="2" y="6" width="16" height="12" rx="2"/></svg>

After

Width:  |  Height:  |  Size: 376 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-battery-low-icon lucide-battery-low"><path d="M22 14v-4"/><path d="M6 14v-4"/><rect x="2" y="6" width="16" height="12" rx="2"/></svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-battery-medium-icon lucide-battery-medium"><path d="M10 14v-4"/><path d="M22 14v-4"/><path d="M6 14v-4"/><rect x="2" y="6" width="16" height="12" rx="2"/></svg>

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-battery-icon lucide-battery"><path d="M 22 14 L 22 10"/><rect x="2" y="6" width="16" height="12" rx="2"/></svg>

After

Width:  |  Height:  |  Size: 313 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-battery-warning-icon lucide-battery-warning"><path d="M10 17h.01"/><path d="M10 7v6"/><path d="M14 6h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2"/><path d="M22 14v-4"/><path d="M6 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2"/></svg>

After

Width:  |  Height:  |  Size: 418 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-from-line-icon lucide-arrow-down-from-line"><path d="M19 3H5"/><path d="M12 21V7"/><path d="m6 15 6 6 6-6"/></svg>

After

Width:  |  Height:  |  Size: 327 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-right-icon lucide-chevron-right"><path d="m9 18 6-6-6-6"/></svg>

After

Width:  |  Height:  |  Size: 274 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-flame-icon lucide-flame"><path d="M12 3q1 4 4 6.5t3 5.5a1 1 0 0 1-14 0 5 5 0 0 1 1-3 1 1 0 0 0 5 0c0-2-1.5-3-1.5-5q0-2 2.5-4"/></svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24">
<!-- 原来的主体图形 -->
<path
d="M12 3
q1 4 4 6.5
t3 5.5
a1 1 0 0 1 -14 0
a5 5 0 0 1 1 -3
a1 1 0 0 0 5 0
c0 -2 -1.5 -3 -1.5 -5
q0 -2 2.5 -4"
fill="black"/>
<!-- 右上角水平减号 -->
<path
d="M16 4 L20 4"
stroke="black"
stroke-width="2"
fill="none"
stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 511 B

View File

@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24">
<!-- 原来的主体图形 -->
<path
d="M12 3
q1 4 4 6.5
t3 5.5
a1 1 0 0 1 -14 0
a5 5 0 0 1 1 -3
a1 1 0 0 0 5 0
c0 -2 -1.5 -3 -1.5 -5
q0 -2 2.5 -4"
fill="black"/>
<!-- 右上角加号:水平线 -->
<path
d="M16 4 L20 4"
stroke="black"
stroke-width="2"
fill="none"
stroke-linecap="round"/>
<!-- 右上角加号:垂直线 -->
<path
d="M18 2 L18 6"
stroke="black"
stroke-width="2"
fill="none"
stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 694 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw-icon lucide-refresh-cw"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/></svg>

After

Width:  |  Height:  |  Size: 411 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-activity-icon lucide-activity"><path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2"/></svg>

After

Width:  |  Height:  |  Size: 373 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-layers-icon lucide-layers"><path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"/><path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"/><path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"/></svg>

After

Width:  |  Height:  |  Size: 525 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-lock-icon lucide-lock"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>

After

Width:  |  Height:  |  Size: 324 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-lock-open-icon lucide-lock-open"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 9.9-1"/></svg>

After

Width:  |  Height:  |  Size: 333 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-minus-icon lucide-minus"><path d="M5 12h14"/></svg>

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-up-icon lucide-arrow-down-up"><path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="m21 8-4-4-4 4"/><path d="M17 4v16"/></svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus-icon lucide-plus"><path d="M5 12h14"/><path d="M12 5v14"/></svg>

After

Width:  |  Height:  |  Size: 271 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-power-icon lucide-power"><path d="M12 2v10"/><path d="M18.4 6.6a9 9 0 1 1-12.77.04"/></svg>

After

Width:  |  Height:  |  Size: 293 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-scissors-icon lucide-scissors"><circle cx="6" cy="6" r="3"/><path d="M8.12 8.12 12 12"/><path d="M20 4 8.12 15.88"/><circle cx="6" cy="18" r="3"/><path d="M14.8 14.8 20 20"/></svg>

After

Width:  |  Height:  |  Size: 382 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-left-icon lucide-chevron-left"><path d="m15 18-6-6 6-6"/></svg>

After

Width:  |  Height:  |  Size: 273 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-from-line-icon lucide-arrow-up-from-line"><path d="m18 9-6-6-6 6"/><path d="M12 3v14"/><path d="M5 21h14"/></svg>

After

Width:  |  Height:  |  Size: 324 B

View File

@@ -0,0 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="black"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round">
<!-- 右侧“播放/视频”形状 -->
<path
d="M16 15
L21.223 18.482
a0.5 0.5 0 0 0 0.777 -0.416
V9.87
a0.5 0.5 0 0 0 -0.752 -0.432
L16 12.5"/>
<!-- 左侧矩形主体 -->
<path
d="M4 8
L14 8
A2 2 0 0 1 16 10
L16 18
A2 2 0 0 1 14 20
L4 20
A2 2 0 0 1 2 18
L2 10
A2 2 0 0 1 4 8
Z"/>
<!-- 左上角“外接/上传”折角 -->
<path
d="M17 1 L17 7 L21 7"/>
</svg>

After

Width:  |  Height:  |  Size: 721 B

View File

@@ -0,0 +1,38 @@
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="black"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round">
<!-- 右侧视频/播放形状 -->
<path
d="M16 15
L21.223 18.482
a0.5 0.5 0 0 0 0.777 -0.416
V9.87
a0.5 0.5 0 0 0 -0.752 -0.432
L16 12.5"/>
<!-- 左侧矩形主体 -->
<path
d="M4 8
L14 8
A2 2 0 0 1 16 10
L16 18
A2 2 0 0 1 14 20
L4 20
A2 2 0 0 1 2 18
L2 10
A2 2 0 0 1 4 8
Z"/>
<!-- 左上角折角 -->
<path d="M17 1 L17 7 L21 7"/>
<!-- 斜杠禁用线 -->
<path d="M22 3 L2 22"/>
</svg>

After

Width:  |  Height:  |  Size: 746 B

View File

@@ -0,0 +1,40 @@
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="black"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round">
<!-- 右侧视频/播放形状 -->
<path
d="m16 15
l5.223 3.482
a0.5 0.5 0 0 0 0.777 -0.416
V9.87
a0.5 0.5 0 0 0 -0.752 -0.432
L16 12.5"/>
<!-- 左侧矩形主体 -->
<path
d="M4 8
L14 8
A2 2 0 0 1 16 10
L16 18
A2 2 0 0 1 14 20
L4 20
A2 2 0 0 1 2 18
L2 10
A2 2 0 0 1 4 8
Z"/>
<!-- 右上角“广播/音量”形状 -->
<path
d="M17 1 L17 8
M17 1
C22 0.5 22 6 17 6
M17 6
L22 8"/>
</svg>

After

Width:  |  Height:  |  Size: 771 B

View File

@@ -0,0 +1,43 @@
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="black"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round">
<!-- 右侧视频/扬声器形状 -->
<path
d="m16 15
l5.223 3.482
a0.5 0.5 0 0 0 0.777 -0.416
V9.87
a0.5 0.5 0 0 0 -0.752 -0.432
L16 12.5"/>
<!-- 左侧矩形主体 -->
<path
d="M4 8
L14 8
A2 2 0 0 1 16 10
L16 18
A2 2 0 0 1 14 20
L4 20
A2 2 0 0 1 2 18
L2 10
A2 2 0 0 1 4 8
Z"/>
<!-- 右上角广播/音量曲线 -->
<path
d="M17 1 L17 8
M17 1
C22 0.5 22 6 17 6
M17 6
L22 8"/>
<!-- 禁用斜线 -->
<path d="M22 3 L2 22"/>
</svg>

After

Width:  |  Height:  |  Size: 823 B

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="currentColor">
<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>
</svg>

After

Width:  |  Height:  |  Size: 299 B

1
assets/www/webrtc/jswebrtc.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -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 = [];
}
}
}

3
devtools_options.yaml Normal file
View File

@@ -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:

View File

@@ -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,

View File

@@ -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<RemoteControlPage> {
@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<RemoteControlPage> {
@override
Widget build(BuildContext context) {
// 1. 监听全局状态 (实现方案二:掉线实时刷新)
// 监听全局状态(这些通常不随摇杆频繁变化)
final userState = context.watch<AppUserCubit>().state;
final deviceState = context.watch<DevicesCubit>().state;
final currentDevice = deviceState.selectedDevice;
// 2. 监听局部遥控状态 (对应 Android Compose 的 collectAsState)
final remoteState = context.watch<RemoteControlCubit>().state;
// 如果设备突然掉线,显示遮罩 (对应 Android 的 finishEvent 逻辑)
if (currentDevice == null /*|| !currentDevice.isOnline*/ ) {
if (currentDevice == null) {
return _buildOfflineScaffold();
}
@@ -61,56 +57,99 @@ class _RemoteControlPageState extends State<RemoteControlPage> {
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<RemoteControlCubit, RemoteControlState>(
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<RemoteControlCubit, RemoteControlState>(
// 只有 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<RemoteControlCubit, RemoteControlState>(
buildWhen: (p, c) => p.showPermissionRequestDialog != c.showPermissionRequestDialog,
builder: (context, state) {
if (state.showPermissionRequestDialog) {
return _buildPermissionDialog(context, state);
}
return const SizedBox.shrink();
},
),
],
),
);

View File

@@ -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<RemoteControlCubit>().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<RemoteControlCubit>().updateChassisLift(1),
onMiddle: () =>
context.read<RemoteControlCubit>().updateChassisLift(0),
onBottom: () =>
context.read<RemoteControlCubit>().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)),
],
);
}
}

View File

@@ -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<EmergencyStopButton> createState() => _EmergencyStopButtonState();
@@ -19,7 +24,6 @@ class _EmergencyStopButtonState extends State<EmergencyStopButton>
@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<EmergencyStopButton>
.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<EmergencyStopButton>
// 对应 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<EmergencyStopButton>
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<EmergencyStopButton>
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,
),

View File

@@ -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),
],
);
}

View File

@@ -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<MyVideoPlayer> {
// 1. 定义本地服务器
InAppLocalhostServer? _localServer;
InAppWebViewController? _webViewController;
bool _isServerRunning = false;
var actualPort;
int? actualPort;
@override
void initState() {
@@ -33,79 +33,73 @@ class _MyVideoPlayerState extends State<MyVideoPlayer> {
_startServer();
}
// 2. 启动服务器 (适用于 Windows, Android, iOS)
Future<void> _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')");
}
}
}

View File

@@ -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,
),

View File

@@ -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 {
),
);
}
}
}

View File

@@ -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';

View File

@@ -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<CCExpandSlider> createState() => _CCExpandSliderState();
}
class _CCExpandSliderState extends State<CCExpandSlider> {
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),
);
}
}

View File

@@ -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,

View File

@@ -11,6 +11,8 @@ dependencies:
flutter:
sdk: flutter
flutter_svg: ^2.2.3
dev_dependencies:
flutter_test:
sdk: flutter

View File

@@ -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