diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 811494d..5f0562c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ on: jobs: build-windows: - runs-on: windows-latest + runs-on: windows-2022 steps: - name: Clone repository uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cd40a3..1cb85ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## v1.5.0 + +### Changelog + +* Updated app icon +* Added a stop and an exit button +* Added a playback-speed selector that activates with a touch-and-hold gesture +* Fixed URI handling +* Improved stability and performance + +### 更新日志 + +* 更换应用图标 +* 添加停止按钮和退出按钮 +* 添加触控长按激活的播放速度选择器 +* 修复 uri 处理 +* 优化了稳定性和性能 + ## v1.4.2 ### Changelog @@ -10,7 +28,6 @@ * 修复音频封面问题 * 改进 uri 处理 - ## v1.4.1 ### Changelog @@ -21,7 +38,6 @@ * FTP 串流使用动态 url - ## v1.4.0 ### Changelog @@ -38,7 +54,6 @@ * Windows 版本存储列表支持远程磁盘和网络快捷方式 * 添加 Windows 版本安装器 - ## v1.3.4 ### Changelog @@ -53,7 +68,6 @@ * 添加播放速度按钮。 * 添加快捷键:帧进 `+`,帧退 `-`。 - ## v1.3.3 ### Changelog @@ -64,7 +78,6 @@ * 修复启动后无法继续播放的问题 - ## v1.3.2 ### Changelog @@ -75,7 +88,6 @@ * 添加 WebDAV 存储时支持自定义 https 端口 - ## v1.3.1 ### Changelog @@ -88,7 +100,6 @@ * Windows 版本数据保存位置已修改为 `C:\Users\\AppData\Roaming\nini22P\iris` * 更新上游依赖,修复 FVP 播放器后端切换字幕的问题 - ## v1.3.0 ### Changelog @@ -100,25 +111,27 @@ * Improved some visual effects ### 更新日志 + * 添加 [FVP](https://github.com/wang-bin/fvp) 播放器后端(实验性,有未知bug) * 添加音量调整 * 添加文件排序 * 添加快捷键:提升音量( `Arrow Up` )、降低音量( `Arrow Down` )、静音(`Ctrl + M`)、切换窗口置顶( `F10` )、关闭当前媒体文件( `Ctrl + C` )、退出应用( `Alt + X` ) * 改进了部分视觉效果 - ## v1.2.1 ### Changelog + * Split APKs by architecture to reduce installation size. ### 更新日志 -* 拆分不同架构的 APK 以减小安装包大小 +* 拆分不同架构的 APK 以减小安装包大小 ## v1.2.0 ### Changelog + * Support jumping to video playback from external clicks (Windows version can play by command line or dragging files to the window) * Support adjusting brightness and volume gestures (Brightness gestures are not available on Windows version) * Support playing online links @@ -128,6 +141,7 @@ * Improved some visual effects ### 更新日志 + * 支持从外部点击视频跳转播放(Windows 版本可以通过命令行或者拖拽文件到窗口播放) * 支持调整亮度和音量手势(Windows 版本调整亮度手势不可用) * 支持播放在线链接 @@ -136,22 +150,24 @@ * 改进 WebDAV 测试连接功能 * 改进了部分视觉效果 - ## v1.1.1 ### Changelog + * Restore old update method for windows version (Double-click the `iris-updater.bat` in the same directory as the executable file to upgrade if you have problems updating.) ### 更新日志 -* windows 版本恢复为旧的更新方式(更新出问题的可双击打开可执行文件同级目录下的 `iris-updater.bat` 升级) +* windows 版本恢复为旧的更新方式(更新出问题的可双击打开可执行文件同级目录下的 `iris-updater.bat` 升级) ## v1.1.0 ### Breaking Changes + * All configurations will be cleared. Please reconfigure ### Changlog + * Display all local storage * Support playback history * Support random playback @@ -159,47 +175,57 @@ * Support video zoom ### 重大变更 + * 所有配置将被清空,请重新配置 ### 更新日志 + * 显示所有本地存储 * 支持播放历史 * 支持随机播放 * 支持循环播放 * 支持视频缩放 - ## v1.0.3 + ### Changelog + * Improve Windows version installation updates * Fixes an issue where subtitles may not be found ### 更新日志 + * 改进 Windows 版本安装更新 * 修复可能无法找到字幕的问题 - ## v1.0.2 + ### Changelog + * Support for switching built-in audio tracks * Reduce package size for Windows version ### 更新日志 + * 支持切换内置音轨 * 减小 Windows 版本包体大小 - ## v1.0.1 + ### Changelog + * Windows version support auto update ### 更新日志 -* Windows 版本支持自动更新 +* Windows 版本支持自动更新 ## v1.0.0 + ### Changelog + * Supports WebDAV and local storage video playback ### 更新日志 + * 支持 WebDAV 和本地存储视频播放 diff --git a/README.md b/README.md index 2eb4a01..cfffbeb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -icon +logo # IRIS - A lightweight video player diff --git a/README_CN.md b/README_CN.md index 977567c..de90e2d 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,4 +1,4 @@ -icon +logo # IRIS - 轻量级视频播放器 diff --git a/analysis_options.yaml b/analysis_options.yaml index d54b87e..8d46a96 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -27,5 +27,7 @@ linter: # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options analyzer: + plugins: + # - custom_lint errors: invalid_annotation_target: ignore \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore index 2a7ed28..8d1b247 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -14,4 +14,7 @@ key.properties **/*.keystore **/*.jks -/app/src/main/assets/flutter_assets \ No newline at end of file +/app/src/main/assets/flutter_assets + +.kotlin +build \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index d2b9f78..60999ad 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,4 +1,3 @@ -import java.io.File import java.nio.file.Files import java.security.MessageDigest @@ -21,12 +20,12 @@ android { ndkVersion = "27.0.12077973" compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 + jvmTarget = JavaVersion.VERSION_17 } defaultConfig { diff --git a/android/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png index ba9959f..0e3c867 100644 Binary files a/android/app/src/main/ic_launcher-playstore.png and b/android/app/src/main/ic_launcher-playstore.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 729ec34..0000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..22ca961 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 9bf06ee..0000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..865c831 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index fb086d2..0000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..a6f9f1b Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 03a1a7e..0000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..cacaf6e Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png deleted file mode 100644 index 265fc24..0000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..298e789 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 3749153..0000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..c830339 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index f13eac4..0000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..3962803 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png deleted file mode 100644 index 11d69e6..0000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..3c3bc1e Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index cf8317d..0000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..d1c8733 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 561f071..0000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..694b0fa Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 39d7985..0000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..e881a19 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 559d9e9..0000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..8550dd7 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 50fbc59..0000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..c6cbaa1 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 5f2efc8..0000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..cf8a0e8 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 260cf02..0000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..ec25e57 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/android/app/src/main/res/values/ic_launcher_background.xml index ab200ff..21fa9fa 100644 --- a/android/app/src/main/res/values/ic_launcher_background.xml +++ b/android/app/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - - #ffffff + + #2F2F2F \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 348c409..02767eb 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 663f60a..0fff400 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -18,8 +18,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.7.1" apply false - id "org.jetbrains.kotlin.android" version "1.8.22" apply false + id "com.android.application" version "8.12.0" apply false + id "org.jetbrains.kotlin.android" version "2.2.20" apply false } include ":app" diff --git a/assets/images/icon.png b/assets/images/icon.png deleted file mode 100644 index d8b3a45..0000000 Binary files a/assets/images/icon.png and /dev/null differ diff --git a/assets/images/icon.svg b/assets/images/icon.svg deleted file mode 100644 index 486af68..0000000 --- a/assets/images/icon.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000..b12f019 Binary files /dev/null and b/assets/images/logo.png differ diff --git a/assets/images/logo_transparent.png b/assets/images/logo_transparent.png new file mode 100644 index 0000000..74a0db0 Binary files /dev/null and b/assets/images/logo_transparent.png differ diff --git a/inno.iss b/inno.iss index ccff928..f0be132 100644 --- a/inno.iss +++ b/inno.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "IRIS" -#define MyAppVersion "1.4.1" +#define MyAppVersion "1.5.0" #define MyAppPublisher "nini22P" #define MyAppURL "https://github.com/nini22P/iris" #define MyAppExeName "iris.exe" diff --git a/lib/globals.dart b/lib/globals.dart index f4fc43a..bbb5fd7 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -9,3 +9,22 @@ String? initUri; PermissionStatus? storagePermissionStatus; final moreMenuKey = GlobalKey(); final rateMenuKey = GlobalKey(); +const double speedSelectorItemWidth = 64.0; +const List speedStops = [ + 0.25, + 0.5, + 0.75, + 1.0, + 1.25, + 1.5, + 1.75, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0, +]; diff --git a/lib/hooks/player/use_fvp_player.dart b/lib/hooks/player/use_fvp_player.dart new file mode 100644 index 0000000..9004666 --- /dev/null +++ b/lib/hooks/player/use_fvp_player.dart @@ -0,0 +1,407 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_zustand/flutter_zustand.dart'; +import 'package:fvp/fvp.dart'; +import 'package:iris/globals.dart' as globals; +import 'package:iris/models/file.dart'; +import 'package:iris/models/player.dart'; +import 'package:iris/models/progress.dart'; +import 'package:iris/models/storages/storage.dart'; +import 'package:iris/models/store/app_state.dart'; +import 'package:iris/store/use_app_store.dart'; +import 'package:iris/store/use_history_store.dart'; +import 'package:iris/store/use_play_queue_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; +import 'package:iris/store/use_storage_store.dart'; +import 'package:iris/utils/check_data_source_type.dart'; +import 'package:iris/utils/logger.dart'; +import 'package:iris/utils/platform.dart'; +import 'package:media_stream/media_stream.dart'; +import 'package:saf_util/saf_util.dart'; +import 'package:video_player/video_player.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; + +FvpPlayer useFvpPlayer(BuildContext context) { + final autoPlay = useAppStore().select(context, (state) => state.autoPlay); + final rate = useAppStore().select(context, (state) => state.rate); + final volume = useAppStore().select(context, (state) => state.volume); + final isMuted = useAppStore().select(context, (state) => state.isMuted); + final repeat = useAppStore().select(context, (state) => state.repeat); + final playQueue = + usePlayQueueStore().select(context, (state) => state.playQueue); + final currentIndex = + usePlayQueueStore().select(context, (state) => state.currentIndex); + final bool alwaysPlayFromBeginning = + useAppStore().select(context, (state) => state.alwaysPlayFromBeginning); + + final history = useHistoryStore().select(context, (state) => state.history); + + final looping = + useMemoized(() => repeat == Repeat.one ? true : false, [repeat]); + + final int currentPlayIndex = useMemoized( + () => playQueue.indexWhere((element) => element.index == currentIndex), + [playQueue, currentIndex]); + + final FileItem? file = useMemoized( + () => playQueue.isEmpty || currentPlayIndex < 0 + ? null + : playQueue[currentPlayIndex].file, + [playQueue, currentPlayIndex]); + + final externalSubtitle = useState(null); + + final List externalSubtitles = + useMemoized(() => file?.subtitles ?? [], [file?.subtitles]); + + final isInitializing = useState(false); + + MediaStream mediaStream = useMemoized(() => MediaStream()); + final streamUrl = useMemoized(() => mediaStream.url); + + final controller = useState(VideoPlayerController.networkUrl(Uri.parse(''))); + + final isPlaying = useListenableSelector( + controller.value, () => controller.value.value.isPlaying); + final position = useListenableSelector( + controller.value, () => controller.value.value.position); + final duration = useListenableSelector( + controller.value, () => controller.value.value.duration); + final buffered = useListenableSelector( + controller.value, () => controller.value.value.buffered); + final width = useListenableSelector( + controller.value, () => controller.value.value.size.width); + final height = useListenableSelector( + controller.value, () => controller.value.value.size.height); + + useEffect(() { + if (file?.type != ContentType.video) { + usePlayerUiStore().updateAspectRatio(0); + return; + } + + if (width != 0 && height != 0) { + usePlayerUiStore().updateAspectRatio(width / height); + } else { + usePlayerUiStore().updateAspectRatio(0); + } + return; + }, [file?.type, width, height]); + + Future init(FileItem? file) async { + isInitializing.value = true; + + try { + if (controller.value.value.isInitialized) { + logger('Dispose player'); + controller.value.dispose(); + } + + if (file == null || file.uri.isEmpty) { + controller.value = VideoPlayerController.networkUrl(Uri.parse('')); + } else { + final storage = useStorageStore().findById(file.storageId); + final auth = storage?.getAuth(); + + logger('Open file: $file'); + + switch (checkDataSourceType(file)) { + case DataSourceType.file: + controller.value = VideoPlayerController.file( + File(file.uri), + httpHeaders: auth != null ? {'authorization': auth} : {}, + ); + case DataSourceType.contentUri: + final isExists = await SafUtil().exists(file.uri, false); + controller.value = VideoPlayerController.contentUri( + isExists ? Uri.parse(file.uri) : Uri.parse(''), + ); + default: + controller.value = VideoPlayerController.networkUrl( + Uri.parse(file.storageType == StorageType.ftp + ? '$streamUrl/${file.uri}' + : file.uri), + httpHeaders: auth != null ? {'authorization': auth} : {}, + ); + } + } + await controller.value.initialize(); + await controller.value.setLooping(repeat == Repeat.one ? true : false); + await controller.value.setPlaybackSpeed(rate); + await controller.value.setVolume(isMuted ? 0 : volume / 100); + } catch (e) { + logger('Error initializing player: $e'); + } finally { + isInitializing.value = false; + } + } + + useEffect(() { + init(file); + return; + }, [file?.uri]); + + useEffect(() { + return () { + if (controller.value.value.isInitialized) { + controller.value.dispose(); + } + }; + }, []); + + useEffect(() { + () async { + final currentExternalSubtitle = externalSubtitle.value; + if (currentExternalSubtitle == null || externalSubtitles.isEmpty) { + controller.value.setExternalSubtitle(''); + } else if (externalSubtitle.value! < externalSubtitles.length) { + bool isExists = true; + + final uri = file?.storageType == StorageType.ftp + ? '$streamUrl/${externalSubtitles[currentExternalSubtitle].uri}' + : externalSubtitles[currentExternalSubtitle].uri; + + logger('External subtitle uri: $uri'); + + if (Platform.isAndroid && + externalSubtitles[currentExternalSubtitle] + .uri + .startsWith('content://')) { + isExists = await SafUtil().exists(uri, false); + } + + if (isExists) { + controller.value.setExternalSubtitle(uri); + } else { + externalSubtitle.value = null; + } + } + }(); + + return; + }, [externalSubtitles, externalSubtitle.value]); + + useEffect(() { + () async { + if (file != null && + controller.value.value.isCompleted && + controller.value.value.position != Duration.zero && + controller.value.value.duration != Duration.zero) { + logger('Completed: ${file.name}'); + if (repeat == Repeat.one) return; + if (currentPlayIndex == playQueue.length - 1) { + if (repeat == Repeat.all) { + await usePlayQueueStore().updateCurrentIndex(playQueue[0].index); + } + } else { + await usePlayQueueStore() + .updateCurrentIndex(playQueue[currentPlayIndex + 1].index); + } + } + }(); + return; + }, [controller.value.value.isCompleted]); + + useEffect(() { + if (controller.value.value.isInitialized) { + controller.value.setPlaybackSpeed(rate); + } + return; + }, [rate]); + + useEffect(() { + if (controller.value.value.isInitialized) { + controller.value.setVolume(isMuted ? 0 : volume / 100); + } + return; + }, [volume, isMuted]); + + useEffect(() { + if (controller.value.value.isInitialized) { + logger('Set looping: $looping'); + controller.value.setLooping(repeat == Repeat.one ? true : false); + } + return; + }, [looping]); + + useEffect(() { + () async { + if (controller.value.value.duration != Duration.zero && + file != null && + file.type == ContentType.video) { + Progress? progress = history[file.getID()]; + if (progress != null) { + if (!alwaysPlayFromBeginning && + (progress.duration.inMilliseconds - + progress.position.inMilliseconds) > + 5000) { + logger( + 'Resume progress: ${file.name} position: ${progress.position} duration: ${progress.duration}'); + await controller.value.seekTo(progress.position); + } + } + } + + if (autoPlay) { + controller.value.play(); + } + + if (externalSubtitles.isNotEmpty) { + externalSubtitle.value = 0; + } + }(); + return; + }, [controller.value.value.duration]); + + useEffect(() { + return () { + if (isAndroid && + globals.initUri == file?.uri && + globals.initUri != null && + globals.initUri!.startsWith('content://')) { + return; + } + + if (file != null && + controller.value.value.isInitialized && + controller.value.value.duration.inSeconds != 0) { + logger( + 'Save progress: ${file.name}, position: ${controller.value.value.position}, duration: ${controller.value.value.duration}'); + useHistoryStore().add(Progress( + dateTime: DateTime.now().toUtc(), + position: controller.value.value.position, + duration: controller.value.value.duration, + file: file, + )); + } + }; + }, [file]); + + useEffect(() { + if (controller.value.value.isPlaying) { + logger('Enable wakelock'); + WakelockPlus.enable(); + } else { + logger('Disable wakelock'); + WakelockPlus.disable(); + } + return; + }, [controller.value.value.isPlaying]); + + Future play() async { + if (!controller.value.value.isInitialized && + !isInitializing.value && + file != null) { + init(file); + } + controller.value.play(); + } + + Future pause() async { + controller.value.pause(); + } + + Future seek(Duration newPosition) async { + logger('Seek to: $newPosition'); + if (controller.value.value.duration == Duration.zero) return; + newPosition.inSeconds < 0 + ? await controller.value.seekTo(Duration.zero) + : newPosition.inSeconds > controller.value.value.duration.inSeconds + ? await controller.value.seekTo(controller.value.value.duration) + : await controller.value.seekTo(newPosition); + } + + Future backward(int seconds) async => await seek( + Duration(seconds: controller.value.value.position.inSeconds - seconds)); + + Future forward(int seconds) async => await seek( + Duration(seconds: controller.value.value.position.inSeconds + seconds)); + + Future stepBackward() async { + if (file?.type == ContentType.video) { + await controller.value.step(frames: -1); + logger('Step backward'); + } + } + + Future stepForward() async { + if (file?.type == ContentType.video) { + await controller.value.step(frames: 1); + logger('Step forward'); + } + } + + Future saveProgress() async { + if (isAndroid && + globals.initUri == file?.uri && + globals.initUri != null && + globals.initUri!.startsWith('content://')) { + return; + } + + if (file != null && controller.value.value.duration != Duration.zero) { + logger( + 'Save progress: ${file.name}, position: ${controller.value.value.position}, duration: ${controller.value.value.duration}'); + useHistoryStore().add(Progress( + dateTime: DateTime.now().toUtc(), + position: controller.value.value.position, + duration: controller.value.value.duration, + file: file, + )); + } + } + + useEffect(() => saveProgress, []); + + final fvpPlayer = useMemoized( + () => FvpPlayer( + controller: controller.value, + isInitializing: isInitializing.value, + isPlaying: isPlaying, + externalSubtitle: externalSubtitle, + externalSubtitles: externalSubtitles, + position: + !controller.value.value.isInitialized || duration == Duration.zero + ? Duration.zero + : position, + duration: controller.value.value.isInitialized ? duration : Duration.zero, + buffer: buffered.isEmpty || duration == Duration.zero + ? Duration.zero + : buffered.reduce((max, curr) => curr.end > max.end ? curr : max).end, + width: width, + height: height, + play: play, + pause: pause, + backward: backward, + forward: forward, + stepBackward: stepBackward, + stepForward: stepForward, + seek: seek, + saveProgress: saveProgress, + ), + [ + controller.value, + controller.value.value.isInitialized, + isInitializing.value, + isPlaying, + externalSubtitle.value, + externalSubtitles, + position, + duration, + buffered, + width, + height, + play, + pause, + seek, + backward, + forward, + stepBackward, + stepForward, + saveProgress, + ], + ); + + return fvpPlayer; +} diff --git a/lib/hooks/use_media_kit_player.dart b/lib/hooks/player/use_media_kit_player.dart similarity index 72% rename from lib/hooks/use_media_kit_player.dart rename to lib/hooks/player/use_media_kit_player.dart index 87648a0..5e76c68 100644 --- a/lib/hooks/use_media_kit_player.dart +++ b/lib/hooks/player/use_media_kit_player.dart @@ -12,6 +12,7 @@ import 'package:iris/models/store/app_state.dart'; import 'package:iris/store/use_app_store.dart'; import 'package:iris/store/use_history_store.dart'; import 'package:iris/store/use_play_queue_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; import 'package:iris/store/use_storage_store.dart'; import 'package:iris/utils/logger.dart'; import 'package:iris/utils/platform.dart'; @@ -67,7 +68,9 @@ MediaKitPlayer useMediaKitPlayer(BuildContext context) { await nativePlayer.setProperty("sub-font", "NotoSansCJKsc-Medium"); } }(); - return player.dispose; + return () { + player.dispose(); + }; }, []); final List playQueue = @@ -92,24 +95,37 @@ MediaKitPlayer useMediaKitPlayer(BuildContext context) { : playQueue[currentPlayIndex].file, [playQueue, currentPlayIndex]); - ValueNotifier seeking = useState(false); + final playingStream = useMemoized(() => player.stream.playing, []); + bool playing = useStream(playingStream).data ?? false; + + final videoParamsStream = useMemoized(() => player.stream.videoParams, []); + VideoParams? videoParams = useStream(videoParamsStream).data; - bool playing = useStream(player.stream.playing).data ?? false; - VideoParams? videoParams = useStream(player.stream.videoParams).data; // AudioParams? audioParams = useStream(player.stream.audioParams).data; - ValueNotifier position = useState(Duration.zero); - Duration duration = useStream(player.stream.duration).data ?? Duration.zero; - Duration buffer = useStream(player.stream.buffer).data ?? Duration.zero; - bool completed = useStream(player.stream.completed).data ?? false; + + final positionStream = useMemoized(() => player.stream.position, []); + final position = useStream(positionStream).data ?? Duration.zero; + + final durationStream = useMemoized(() => player.stream.duration, []); + Duration duration = useStream(durationStream).data ?? Duration.zero; + + final bufferStream = useMemoized(() => player.stream.buffer, []); + Duration buffer = useStream(bufferStream).data ?? Duration.zero; + + final completedStream = useMemoized(() => player.stream.completed, []); + bool completed = useStream(completedStream).data ?? false; + // double rate = useStream(player.stream.rate).data ?? 1.0; - Track? track = useStream(player.stream.track).data; + final trackStream = useMemoized(() => player.stream.track, []); + Track? track = useStream(trackStream).data; AudioTrack audio = useMemoized(() => track?.audio ?? AudioTrack.no(), [track?.audio]); SubtitleTrack subtitle = useMemoized( () => track?.subtitle ?? SubtitleTrack.no(), [track?.subtitle]); - Tracks? tracks = useStream(player.stream.tracks).data; + final tracksStream = useMemoized(() => player.stream.tracks, []); + Tracks? tracks = useStream(tracksStream).data; List audios = useMemoized(() => (tracks?.audio ?? []), [tracks?.audio]); List subtitles = useMemoized( @@ -122,18 +138,9 @@ MediaKitPlayer useMediaKitPlayer(BuildContext context) { (subtitle) => subtitles.any((item) => item.title == subtitle.name)), [file?.subtitles, subtitles]); - final positionStream = useStream(player.stream.position); - - if (positionStream.hasData) { - if (!seeking.value) { - position.value = positionStream.data!; - } - } - final isInitializing = useState(false); - MediaStream mediaStream = MediaStream(); - final streamUrl = mediaStream.url; + MediaStream mediaStream = useMemoized(() => MediaStream(), []); Future init(FileItem file) async { if (file.uri == '') return; @@ -146,7 +153,7 @@ MediaKitPlayer useMediaKitPlayer(BuildContext context) { await player.open( Media( file.storageType == StorageType.ftp - ? '$streamUrl/${file.uri}' + ? '${mediaStream.url}/${file.uri}' : file.uri, httpHeaders: auth != null ? {'authorization': auth} : {}, ), @@ -210,7 +217,7 @@ MediaKitPlayer useMediaKitPlayer(BuildContext context) { if (externalSubtitles!.isNotEmpty) { logger('Set external subtitle: ${externalSubtitles[0]}'); final uri = file?.storageType == StorageType.ftp - ? '$streamUrl/${externalSubtitles[0].uri}' + ? '${mediaStream.url}/${externalSubtitles[0].uri}' : externalSubtitles[0].uri; logger('External subtitle uri: $uri'); await player.setSubtitleTrack( @@ -267,9 +274,21 @@ MediaKitPlayer useMediaKitPlayer(BuildContext context) { return; }, [repeat]); - void updatePosition(Duration newPosition) => position.value = newPosition; + useEffect(() { + if (file?.type != ContentType.video) { + usePlayerUiStore().updateAspectRatio(0); + return; + } - void updateSeeking(bool value) => seeking.value = value; + final width = videoParams?.w ?? 0; + final height = videoParams?.h ?? 0; + if (width == 0 || height == 0) { + usePlayerUiStore().updateAspectRatio(0); + } else { + usePlayerUiStore().updateAspectRatio(width / height); + } + return; + }, [file?.type, videoParams?.w, videoParams?.h]); Future saveProgress() async { if (isAndroid && @@ -294,7 +313,6 @@ MediaKitPlayer useMediaKitPlayer(BuildContext context) { useEffect(() => saveProgress, []); Future play() async { - await useAppStore().updateAutoPlay(true); if (duration == Duration.zero && file != null && !isInitializing.value) { await init(file); } @@ -302,31 +320,27 @@ MediaKitPlayer useMediaKitPlayer(BuildContext context) { } Future pause() async { - await useAppStore().updateAutoPlay(false); await player.pause(); } - Future seekTo(Duration newPosition) async => newPosition.inSeconds < 0 - ? await player.seek(Duration.zero) - : newPosition.inSeconds > duration.inSeconds - ? await player.seek(duration) - : await player.seek(newPosition); + Future seek(Duration newPosition) async => + newPosition.inMilliseconds < 0 + ? await player.seek(Duration.zero) + : newPosition.inMilliseconds > duration.inMilliseconds + ? await player.seek(duration) + : await player.seek(newPosition); Future backward(int seconds) async { - if (file?.type == ContentType.video) { - await seekTo(Duration(seconds: position.value.inSeconds - seconds)); - } + await seek(Duration(seconds: position.inSeconds - seconds)); } Future forward(int seconds) async { - if (file?.type == ContentType.video) { - await seekTo(Duration(seconds: position.value.inSeconds + seconds)); - } + await seek(Duration(seconds: position.inSeconds + seconds)); } Future stepBackward() async { final nativePlayer = player.platform; - if (nativePlayer is NativePlayer) { + if (nativePlayer is NativePlayer && file?.type == ContentType.video) { await nativePlayer.command(['frame-back-step']); logger('Step backward'); } @@ -334,38 +348,62 @@ MediaKitPlayer useMediaKitPlayer(BuildContext context) { Future stepForward() async { final nativePlayer = player.platform; - if (nativePlayer is NativePlayer) { + if (nativePlayer is NativePlayer && file?.type == ContentType.video) { await nativePlayer.command(['frame-step']); logger('Step forward'); } } - return MediaKitPlayer( - player: player, - controller: controller, - subtitle: subtitle, - subtitles: subtitles, - externalSubtitles: externalSubtitles ?? [], - audio: audio, - audios: audios, - isInitializing: isInitializing.value, - isPlaying: playing, - position: duration == Duration.zero ? Duration.zero : position.value, - duration: duration, - buffer: duration == Duration.zero ? Duration.zero : buffer, - seeking: seeking.value, - aspect: videoParams?.aspect, - width: videoParams?.w?.toDouble(), - height: videoParams?.h?.toDouble(), - updatePosition: updatePosition, - updateSeeking: updateSeeking, - saveProgress: saveProgress, - play: play, - pause: pause, - backward: backward, - forward: forward, - stepBackward: stepBackward, - stepForward: stepForward, - seekTo: seekTo, + final mediaKitPlayer = useMemoized( + () => MediaKitPlayer( + player: player, + controller: controller, + subtitle: subtitle, + subtitles: subtitles, + externalSubtitles: externalSubtitles ?? [], + audio: audio, + audios: audios, + isInitializing: isInitializing.value, + isPlaying: playing, + position: duration == Duration.zero ? Duration.zero : position, + duration: duration, + buffer: duration == Duration.zero ? Duration.zero : buffer, + width: videoParams?.w?.toDouble() ?? 0, + height: videoParams?.h?.toDouble() ?? 0, + saveProgress: saveProgress, + play: play, + pause: pause, + backward: backward, + forward: forward, + stepBackward: stepBackward, + stepForward: stepForward, + seek: seek, + ), + [ + player, + controller, + subtitle, + subtitles, + externalSubtitles, + audio, + audios, + isInitializing.value, + playing, + position, + duration, + buffer, + videoParams?.w, + videoParams?.h, + saveProgress, + play, + pause, + backward, + forward, + stepBackward, + stepForward, + seek, + ], ); + + return mediaKitPlayer; } diff --git a/lib/hooks/use_full_screen.dart b/lib/hooks/ui/use_full_screen.dart similarity index 66% rename from lib/hooks/use_full_screen.dart rename to lib/hooks/ui/use_full_screen.dart index e03bda6..3f9f6fc 100644 --- a/lib/hooks/use_full_screen.dart +++ b/lib/hooks/ui/use_full_screen.dart @@ -1,13 +1,14 @@ -import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; -import 'package:iris/store/use_ui_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; import 'package:iris/utils/platform.dart'; import 'package:window_manager/window_manager.dart'; -void useFullScreen(BuildContext context) { +void useFullScreen() { + final context = useContext(); + final isFullScreen = - useUiStore().select(context, (state) => state.isFullScreen); + usePlayerUiStore().select(context, (state) => state.isFullScreen); useEffect(() { () async { diff --git a/lib/hooks/use_orientation.dart b/lib/hooks/ui/use_orientation.dart similarity index 77% rename from lib/hooks/use_orientation.dart rename to lib/hooks/ui/use_orientation.dart index c1b3e62..60df571 100644 --- a/lib/hooks/use_orientation.dart +++ b/lib/hooks/ui/use_orientation.dart @@ -1,17 +1,19 @@ import 'dart:io'; - -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; -import 'package:iris/models/player.dart'; import 'package:iris/models/store/app_state.dart'; import 'package:iris/store/use_app_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; -void useOrientation(BuildContext context, MediaPlayer player) { +void useOrientation() { + final context = useContext(); final orientation = useAppStore().select(context, (state) => state.orientation); + final aspectRatio = + usePlayerUiStore().select(context, (state) => state.aspectRatio); + setOrientation(ScreenOrientation orientation, double? aspect) { if (Platform.isAndroid || Platform.isIOS) { switch (orientation) { @@ -35,12 +37,12 @@ void useOrientation(BuildContext context, MediaPlayer player) { } useEffect(() { - setOrientation(orientation, player.aspect); + setOrientation(orientation, aspectRatio); return () => SystemChrome.setPreferredOrientations([]); }, []); useEffect(() { - setOrientation(orientation, player.aspect); + setOrientation(orientation, aspectRatio); return; - }, [orientation, player.aspect]); + }, [orientation, aspectRatio]); } diff --git a/lib/hooks/ui/use_resize_window.dart b/lib/hooks/ui/use_resize_window.dart new file mode 100644 index 0000000..454300d --- /dev/null +++ b/lib/hooks/ui/use_resize_window.dart @@ -0,0 +1,127 @@ +import 'dart:math' as math; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_zustand/flutter_zustand.dart'; +import 'package:iris/models/file.dart'; +import 'package:iris/store/use_app_store.dart'; +import 'package:iris/store/use_play_queue_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; +import 'package:iris/utils/logger.dart'; +import 'package:iris/utils/platform.dart'; +import 'package:window_manager/window_manager.dart'; +import 'package:window_size/window_size.dart'; + +Future _applyResize(Rect newBounds) async { + if (await windowManager.isFullScreen() || await windowManager.isMaximized()) { + return; + } + await windowManager.setBounds(newBounds, animate: true); +} + +void useResizeWindow() { + final context = useContext(); + + final autoResize = useAppStore().select(context, (state) => state.autoResize); + final isFullScreen = + usePlayerUiStore().select(context, (state) => state.isFullScreen); + final aspectRatio = + usePlayerUiStore().select(context, (state) => state.aspectRatio); + + final currentPlay = usePlayQueueStore().select(context, (state) { + final index = + state.playQueue.indexWhere((e) => e.index == state.currentIndex); + return index != -1 ? state.playQueue[index] : null; + }); + final contentType = currentPlay?.file.type ?? ContentType.other; + + final prevIsFullScreen = usePrevious(isFullScreen); + final prevAspectRatio = usePrevious(aspectRatio); + + useEffect(() { + if (!isDesktop) return; + + Future performResize() async { + if (isFullScreen) return; + + if (!autoResize) { + await windowManager.setAspectRatio(0); + return; + } + + if (contentType == ContentType.audio) { + await windowManager.setAspectRatio(0); + return; + } + + if (contentType == ContentType.video) { + if (aspectRatio <= 0) { + await windowManager.setAspectRatio(0); + return; + } + + await windowManager.setAspectRatio(aspectRatio); + final oldBounds = await windowManager.getBounds(); + final screen = await getCurrentScreen(); + if (screen == null) return; + + if (oldBounds.size.aspectRatio.toStringAsFixed(2) == + aspectRatio.toStringAsFixed(2)) { + return; + } + + Size newSize; + final bool isPreviousPortrait = (prevAspectRatio ?? 1.0) < 1.0; + final bool isCurrentLandscape = aspectRatio >= 1.0; + + if (isPreviousPortrait && isCurrentLandscape) { + logger('Resize rule: Portrait to Landscape (Height-based)'); + double newHeight = oldBounds.height; + double newWidth = newHeight * aspectRatio; + newSize = Size(newWidth, newHeight); + } else { + logger('Resize rule: Standard (Normalized Area-based)'); + double currentArea = oldBounds.width * oldBounds.height; + const double standardAspectRatio = 16.0 / 9.0; + double normalizedHeight = + math.sqrt(currentArea / standardAspectRatio); + + double newHeight = normalizedHeight; + double newWidth = newHeight * aspectRatio; + newSize = Size(newWidth, newHeight); + } + + double maxWidth = screen.frame.width / screen.scaleFactor * 0.95; + double maxHeight = screen.frame.height / screen.scaleFactor * 0.95; + + if (newSize.width > maxWidth) { + newSize = Size(maxWidth, maxWidth / aspectRatio); + } + if (newSize.height > maxHeight) { + newSize = Size(maxHeight * aspectRatio, maxHeight); + } + + final newPosition = Offset( + oldBounds.left + (oldBounds.width - newSize.width) / 2, + oldBounds.top + (oldBounds.height - newSize.height) / 2, + ); + + await _applyResize(Rect.fromLTWH( + newPosition.dx, newPosition.dy, newSize.width, newSize.height)); + } + } + + final wasFullScreen = prevIsFullScreen == true; + if (wasFullScreen && !isFullScreen) { + Future.delayed(const Duration(milliseconds: 50), performResize); + } else { + performResize(); + } + + return null; + }, [ + autoResize, + isFullScreen, + aspectRatio, + contentType, + ]); +} diff --git a/lib/hooks/use_app_lifecycle.dart b/lib/hooks/use_app_lifecycle.dart index cf8b4aa..6d31494 100644 --- a/lib/hooks/use_app_lifecycle.dart +++ b/lib/hooks/use_app_lifecycle.dart @@ -1,17 +1,20 @@ import 'dart:ui'; - import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:iris/models/player.dart'; import 'package:iris/utils/logger.dart'; +import 'package:provider/provider.dart'; + +void useAppLifecycle() { + final context = useContext(); + final saveProgress = context.read().saveProgress; -void useAppLifecycle(MediaPlayer player) { AppLifecycleState? appLifecycleState = useAppLifecycleState(); useEffect(() { try { if (appLifecycleState == AppLifecycleState.paused) { logger('App lifecycle state: paused'); - player.saveProgress(); + saveProgress(); } } catch (e) { logger('App lifecycle state error: $e'); diff --git a/lib/hooks/use_cover.dart b/lib/hooks/use_cover.dart index 2d45a30..942ffe8 100644 --- a/lib/hooks/use_cover.dart +++ b/lib/hooks/use_cover.dart @@ -1,19 +1,15 @@ import 'package:collection/collection.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; import 'package:iris/models/file.dart'; -import 'package:iris/models/player.dart'; import 'package:iris/models/storages/local.dart'; import 'package:iris/models/storages/storage.dart'; import 'package:iris/store/use_play_queue_store.dart'; import 'package:iris/store/use_storage_store.dart'; -import 'package:iris/utils/files_filter.dart'; -FileItem? useCover( - BuildContext context, - MediaPlayer player, -) { +FileItem? useCover() { + final context = useContext(); + final playQueue = usePlayQueueStore().select(context, (state) => state.playQueue); final currentIndex = @@ -23,10 +19,10 @@ FileItem? useCover( () => playQueue.indexWhere((element) => element.index == currentIndex), [playQueue, currentIndex]); - final PlayQueueItem? currentPlay = useMemoized( + final FileItem? file = useMemoized( () => playQueue.isEmpty || currentPlayIndex < 0 ? null - : playQueue[currentPlayIndex], + : playQueue[currentPlayIndex].file, [playQueue, currentPlayIndex]); final localStoragesFuture = @@ -35,38 +31,40 @@ FileItem? useCover( final storages = useStorageStore().select(context, (state) => state.storages); - final List dir = useMemoized( - () => currentPlay?.file == null || currentPlay!.file.path.isEmpty - ? [] - : ([...currentPlay.file.path]..removeLast()), - [currentPlay?.file], - ); - final Storage? storage = useMemoized( - () => currentPlay?.file == null + () => file == null ? null - : [...localStorages, ...storages].firstWhereOrNull( - (storage) => storage.id == currentPlay?.file.storageId), - [currentPlay?.file, localStorages, storages]); + : [...localStorages, ...storages] + .firstWhereOrNull((storage) => storage.id == file.storageId), + [file, localStorages, storages]); - final getCover = useMemoized(() async { - if (currentPlay?.file.type != ContentType.audio) return null; + final cover = useState(null); - final files = await storage?.getFiles(dir); + useEffect(() { + () async { + if (storage == null || file == null || file.type != ContentType.audio) { + cover.value = null; + return; + } - if (files == null) return null; + final dir = + file.path.isEmpty ? [] : ([...file.path]..removeLast()); - final images = filesFilter(files, [ContentType.image]); + final files = await storage.getFiles(dir); - return images.firstWhereOrNull( - (image) => image.name.split('.').first.toLowerCase() == 'cover') ?? - images.firstWhereOrNull((image) => - image.name.toLowerCase().startsWith('cover') || - image.name.toLowerCase().startsWith('folder')) ?? - images.firstOrNull; - }, [currentPlay?.file, dir, player.isPlaying]); + final images = files + .where((file) => [ContentType.image].contains(file.type)) + .toList(); - final cover = useFuture(getCover).data; + cover.value = images.firstWhereOrNull((image) => + image.name.split('.').first.toLowerCase() == 'cover') ?? + images.firstWhereOrNull((image) => + image.name.toLowerCase().startsWith('cover') || + image.name.toLowerCase().startsWith('folder')) ?? + images.firstOrNull; + }(); + return null; + }, [storage, file]); - return cover; + return cover.value; } diff --git a/lib/hooks/use_fvp_player.dart b/lib/hooks/use_fvp_player.dart deleted file mode 100644 index 20359ae..0000000 --- a/lib/hooks/use_fvp_player.dart +++ /dev/null @@ -1,366 +0,0 @@ -import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:flutter_zustand/flutter_zustand.dart'; -import 'package:fvp/fvp.dart'; -import 'package:iris/globals.dart' as globals; -import 'package:iris/models/file.dart'; -import 'package:iris/models/player.dart'; -import 'package:iris/models/progress.dart'; -import 'package:iris/models/storages/storage.dart'; -import 'package:iris/models/store/app_state.dart'; -import 'package:iris/store/use_app_store.dart'; -import 'package:iris/store/use_history_store.dart'; -import 'package:iris/store/use_play_queue_store.dart'; -import 'package:iris/store/use_storage_store.dart'; -import 'package:iris/utils/check_data_source_type.dart'; -import 'package:iris/utils/logger.dart'; -import 'package:iris/utils/platform.dart'; -import 'package:media_stream/media_stream.dart'; -import 'package:saf_util/saf_util.dart'; -import 'package:video_player/video_player.dart'; -import 'package:wakelock_plus/wakelock_plus.dart'; - -FvpPlayer useFvpPlayer(BuildContext context) { - final autoPlay = useAppStore().select(context, (state) => state.autoPlay); - final rate = useAppStore().select(context, (state) => state.rate); - final volume = useAppStore().select(context, (state) => state.volume); - final isMuted = useAppStore().select(context, (state) => state.isMuted); - final repeat = useAppStore().select(context, (state) => state.repeat); - final playQueue = - usePlayQueueStore().select(context, (state) => state.playQueue); - final currentIndex = - usePlayQueueStore().select(context, (state) => state.currentIndex); - final bool alwaysPlayFromBeginning = - useAppStore().select(context, (state) => state.alwaysPlayFromBeginning); - - final history = useHistoryStore().select(context, (state) => state.history); - - final looping = - useMemoized(() => repeat == Repeat.one ? true : false, [repeat]); - - final int currentPlayIndex = useMemoized( - () => playQueue.indexWhere((element) => element.index == currentIndex), - [playQueue, currentIndex]); - - final PlayQueueItem? currentPlay = useMemoized( - () => playQueue.isEmpty || currentPlayIndex < 0 - ? null - : playQueue[currentPlayIndex], - [playQueue, currentPlayIndex]); - - final file = useMemoized(() => currentPlay?.file, [currentPlay]); - - final externalSubtitle = useState(null); - - final List externalSubtitles = useMemoized( - () => currentPlay?.file.subtitles ?? [], [currentPlay?.file.subtitles]); - - final initValue = useState(false); - - final isInitializing = useState(false); - - Future init() async => initValue.value = true; - - MediaStream mediaStream = MediaStream(); - final streamUrl = mediaStream.url; - - final controllerFuture = useMemoized(() async { - if (file == null) return VideoPlayerController.networkUrl(Uri.parse('')); - isInitializing.value = true; - final storage = useStorageStore().findById(file.storageId); - final auth = storage?.getAuth(); - logger('Open file: $file'); - switch (checkDataSourceType(file)) { - case DataSourceType.file: - return VideoPlayerController.file( - File(file.uri), - httpHeaders: auth != null ? {'authorization': auth} : {}, - ); - case DataSourceType.contentUri: - final isExists = await SafUtil().exists(file.uri, false); - return VideoPlayerController.contentUri( - isExists ? Uri.parse(file.uri) : Uri.parse(''), - ); - default: - return VideoPlayerController.networkUrl( - Uri.parse(file.storageType == StorageType.ftp - ? '$streamUrl/${file.uri}' - : file.uri), - httpHeaders: auth != null ? {'authorization': auth} : {}, - ); - } - }, [file, initValue.value]); - - final controller = useFuture(controllerFuture).data ?? - VideoPlayerController.networkUrl(Uri.parse('')); - - useEffect(() { - () async { - if (controller.dataSource.isEmpty) return; - - try { - await controller.initialize(); - await controller.setLooping(repeat == Repeat.one ? true : false); - await controller.setPlaybackSpeed(rate); - await controller.setVolume(isMuted ? 0 : volume / 100); - } catch (e) { - logger('Error initializing player: $e'); - } - - isInitializing.value = false; - }(); - - return () { - controller.dispose(); - externalSubtitle.value = null; - }; - }, [controller, initValue.value]); - - useEffect(() => controller.dispose, []); - - final isPlaying = - useListenableSelector(controller, () => controller.value.isPlaying); - final duration = - useListenableSelector(controller, () => controller.value.duration); - final position = - useListenableSelector(controller, () => controller.value.position); - final buffered = - useListenableSelector(controller, () => controller.value.buffered); - final size = useListenableSelector(controller, () => controller.value.size); - final isCompleted = - useListenableSelector(controller, () => controller.value.isCompleted); - - final double aspect = useMemoized( - () => size.width != 0 && size.height != 0 ? size.width / size.height : 0, - [size.width, size.height]); - - final seeking = useState(false); - - useEffect(() { - () async { - if (duration != Duration.zero && - currentPlay != null && - currentPlay.file.type == ContentType.video) { - Progress? progress = history[currentPlay.file.getID()]; - if (progress != null) { - if (!alwaysPlayFromBeginning && - (progress.duration.inMilliseconds - - progress.position.inMilliseconds) > - 5000) { - logger( - 'Resume progress: ${currentPlay.file.name} position: ${progress.position} duration: ${progress.duration}'); - await controller.seekTo(progress.position); - } - } - } - - if (autoPlay) { - controller.play(); - } - - if (externalSubtitles.isNotEmpty) { - externalSubtitle.value = 0; - } - }(); - return; - }, [duration]); - - useEffect(() { - () async { - final currentExternalSubtitle = externalSubtitle.value; - if (currentExternalSubtitle == null || externalSubtitles.isEmpty) { - controller.setExternalSubtitle(''); - } else if (externalSubtitle.value! < externalSubtitles.length) { - bool isExists = true; - - final uri = file?.storageType == StorageType.ftp - ? '$streamUrl/${externalSubtitles[currentExternalSubtitle].uri}' - : externalSubtitles[currentExternalSubtitle].uri; - - logger('External subtitle uri: $uri'); - - if (Platform.isAndroid && - externalSubtitles[currentExternalSubtitle] - .uri - .startsWith('content://')) { - isExists = await SafUtil().exists(uri, false); - } - - if (isExists) { - controller.setExternalSubtitle(uri); - } else { - externalSubtitle.value = null; - } - } - }(); - - return; - }, [externalSubtitles, externalSubtitle.value]); - - useEffect(() { - () async { - if (currentPlay != null && - isCompleted && - controller.value.position != Duration.zero && - controller.value.duration != Duration.zero) { - logger('Completed: ${currentPlay.file.name}'); - if (repeat == Repeat.one) return; - if (currentPlayIndex == playQueue.length - 1) { - if (repeat == Repeat.all) { - await usePlayQueueStore().updateCurrentIndex(playQueue[0].index); - } - } else { - await usePlayQueueStore() - .updateCurrentIndex(playQueue[currentPlayIndex + 1].index); - } - } - }(); - return; - }, [isCompleted]); - - useEffect(() { - if (controller.value.isInitialized) { - controller.setPlaybackSpeed(rate); - } - return; - }, [rate]); - - useEffect(() { - if (controller.value.isInitialized) { - controller.setVolume(isMuted ? 0 : volume / 100); - } - return; - }, [volume, isMuted]); - - useEffect(() { - if (controller.value.isInitialized) { - logger('Set looping: $looping'); - controller.setLooping(repeat == Repeat.one ? true : false); - } - return; - }, [looping]); - - useEffect(() { - return () { - if (isAndroid && - globals.initUri == file?.uri && - globals.initUri != null && - globals.initUri!.startsWith('content://')) { - return; - } - - if (file != null && - controller.value.isInitialized && - controller.value.duration.inSeconds != 0) { - logger( - 'Save progress: ${file.name}, position: ${controller.value.position}, duration: ${controller.value.duration}'); - useHistoryStore().add(Progress( - dateTime: DateTime.now().toUtc(), - position: controller.value.position, - duration: controller.value.duration, - file: file, - )); - } - }; - }, [currentPlay?.file]); - - useEffect(() { - if (isPlaying) { - logger('Enable wakelock'); - WakelockPlus.enable(); - } else { - logger('Disable wakelock'); - WakelockPlus.disable(); - } - return; - }, [isPlaying]); - - Future play() async { - await useAppStore().updateAutoPlay(true); - if (!controller.value.isInitialized && !isInitializing.value) { - init(); - } - controller.play(); - } - - Future pause() async { - await useAppStore().updateAutoPlay(false); - controller.pause(); - } - - Future seekTo(Duration newPosition) async { - logger('Seek to: $newPosition'); - if (duration == Duration.zero) return; - newPosition.inSeconds < 0 - ? await controller.seekTo(Duration.zero) - : newPosition.inSeconds > duration.inSeconds - ? await controller.seekTo(duration) - : await controller.seekTo(newPosition); - } - - Future stepBackward() async { - if (file?.type == ContentType.video) { - await controller.step(frames: -1); - logger('Step backward'); - } - } - - Future stepForward() async { - if (file?.type == ContentType.video) { - await controller.step(frames: 1); - logger('Step forward'); - } - } - - Future saveProgress() async { - if (isAndroid && - globals.initUri == file?.uri && - globals.initUri != null && - globals.initUri!.startsWith('content://')) { - return; - } - - if (file != null && duration != Duration.zero) { - logger( - 'Save progress: ${file.name}, position: $position, duration: $duration'); - useHistoryStore().add(Progress( - dateTime: DateTime.now().toUtc(), - position: position, - duration: duration, - file: file, - )); - } - } - - useEffect(() => saveProgress, []); - - return FvpPlayer( - controller: controller, - isInitializing: isInitializing.value, - isPlaying: isPlaying, - externalSubtitle: externalSubtitle, - externalSubtitles: externalSubtitles, - position: duration == Duration.zero ? Duration.zero : position, - duration: duration, - buffer: buffered.isEmpty || duration == Duration.zero - ? Duration.zero - : buffered.reduce((max, curr) => curr.end > max.end ? curr : max).end, - aspect: aspect, - width: size.width, - height: size.height, - play: play, - pause: pause, - backward: (seconds) => - seekTo(Duration(seconds: position.inSeconds - seconds)), - forward: (seconds) => - seekTo(Duration(seconds: position.inSeconds + seconds)), - stepBackward: stepBackward, - stepForward: stepForward, - seekTo: seekTo, - saveProgress: saveProgress, - seeking: seeking.value, - updatePosition: seekTo, - updateSeeking: (value) => seeking.value = value, - ); -} diff --git a/lib/hooks/use_gesture.dart b/lib/hooks/use_gesture.dart new file mode 100644 index 0000000..817a6a2 --- /dev/null +++ b/lib/hooks/use_gesture.dart @@ -0,0 +1,328 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:iris/globals.dart' show speedStops, speedSelectorItemWidth; +import 'package:iris/hooks/use_brightness.dart'; +import 'package:iris/hooks/use_volume.dart'; +import 'package:iris/models/player.dart'; +import 'package:iris/store/use_app_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; +import 'package:iris/utils/logger.dart'; +import 'package:iris/utils/platform.dart'; +import 'package:provider/provider.dart'; +import 'package:window_manager/window_manager.dart'; + +class Gesture { + final void Function(TapDownDetails) onTapDown; + final void Function() onTap; + final void Function(TapDownDetails) onDoubleTapDown; + final void Function(LongPressStartDetails) onLongPressStart; + final void Function(LongPressMoveUpdateDetails) onLongPressMoveUpdate; + final void Function(LongPressEndDetails) onLongPressEnd; + final void Function() onLongPressCancel; + final void Function(DragStartDetails) onPanStart; + final void Function(DragUpdateDetails) onPanUpdate; + final void Function(DragEndDetails) onPanEnd; + final void Function() onPanCancel; + final void Function(PointerHoverEvent) onHover; + + final bool isLongPress; + final bool isLeftGesture; + final bool isRightGesture; + final double? brightness; + final double? volume; + final MouseCursor cursor; + + Gesture({ + required this.onTapDown, + required this.onTap, + required this.onDoubleTapDown, + required this.onLongPressStart, + required this.onLongPressMoveUpdate, + required this.onLongPressEnd, + required this.onLongPressCancel, + required this.onPanStart, + required this.onPanUpdate, + required this.onPanEnd, + required this.onPanCancel, + required this.onHover, + required this.isLongPress, + required this.isLeftGesture, + required this.isRightGesture, + required this.brightness, + required this.volume, + required this.cursor, + }); +} + +Gesture useGesture({ + required void Function() showControl, + required void Function() hideControl, + required void Function() showProgress, + required void Function(Offset position) showSpeedSelector, + required void Function(double finalSpeed) hideSpeedSelector, + required void Function(double speed, double visualOffset) updateSelectedSpeed, +}) { + final context = useContext(); + + final player = context.read(); + + final gestureState = useRef({ + 'isTouch': false, + 'isLongPress': false, + 'isDragging': false, + 'startPanOffset': Offset.zero, + 'startSeekPosition': Duration.zero, + 'panDirection': null, // null: 未确定, Axis.horizontal, Axis.vertical + }); + + final isLeftGesture = useState(false); + final isRightGesture = useState(false); + + final brightness = useBrightness(isLeftGesture.value); + final volume = useVolume(isRightGesture.value); + + void onTapDown(TapDownDetails details) { + if (details.kind == PointerDeviceKind.touch) { + gestureState.value['isTouch'] = true; + } + } + + void onTap() { + if (usePlayerUiStore().state.isShowControl) { + hideControl(); + } else { + showControl(); + } + } + + void onDoubleTapDown(TapDownDetails details) { + if (details.kind == PointerDeviceKind.touch) { + final screenWidth = MediaQuery.sizeOf(context).width; + final tapDx = details.globalPosition.dx; + + if (tapDx > screenWidth * 0.7) { + // 右侧 30% + showProgress(); + player.forward(10); + } else if (tapDx < screenWidth * 0.3) { + // 左侧 30% + showProgress(); + player.backward(10); + } else { + // 中间 40% + if (player.isPlaying) { + useAppStore().updateAutoPlay(false); + player.pause(); + showControl(); + } else { + useAppStore().updateAutoPlay(true); + player.play(); + } + } + } else if (isDesktop) { + // 桌面端双击切换全屏 + usePlayerUiStore() + .updateFullScreen(!usePlayerUiStore().state.isFullScreen); + } + } + + void onLongPressStart(LongPressStartDetails details) { + if (gestureState.value['isTouch'] as bool && player.isPlaying) { + gestureState.value['isLongPress'] = true; + gestureState.value['startPanOffset'] = details.globalPosition; + + final currentRate = useAppStore().state.rate; + final closestSpeed = speedStops.reduce( + (a, b) => (a - currentRate).abs() < (b - currentRate).abs() ? a : b); + gestureState.value['initialSpeedIndex'] = + speedStops.indexOf(closestSpeed); + + showSpeedSelector(details.globalPosition); + updateSelectedSpeed(closestSpeed, 0.0); + } + } + + void onLongPressMoveUpdate(LongPressMoveUpdateDetails details) { + if (!(gestureState.value['isLongPress'] as bool)) return; + + final startDx = (gestureState.value['startPanOffset'] as Offset).dx; + final currentDx = details.globalPosition.dx; + final deltaDx = currentDx - startDx; + + const double sensitivity = speedSelectorItemWidth; + final double visualOffset = deltaDx; + + int steps = (-visualOffset / sensitivity).round(); + + int initialIndex = gestureState.value['initialSpeedIndex'] as int? ?? + speedStops.indexOf(1.0); + int finalIndex = (initialIndex + steps).clamp(0, speedStops.length - 1); + + double selectedSpeed = speedStops[finalIndex]; + + updateSelectedSpeed(selectedSpeed, visualOffset); + if (useAppStore().state.rate != selectedSpeed) { + useAppStore().updateRate(selectedSpeed); + } + } + + void onLongPressEnd(LongPressEndDetails details) { + if (gestureState.value['isLongPress'] as bool) { + hideSpeedSelector(useAppStore().state.rate); + } + gestureState.value['isLongPress'] = false; + gestureState.value['isTouch'] = false; + } + + void onLongPressCancel() { + if (gestureState.value['isLongPress'] as bool) { + hideSpeedSelector(useAppStore().state.rate); + } + gestureState.value['isLongPress'] = false; + gestureState.value['isTouch'] = false; + } + + void onPanStart(DragStartDetails details) { + if (isDesktop && details.kind != PointerDeviceKind.touch) { + windowManager.startDragging(); + return; + } + + if (gestureState.value['isLongPress'] as bool) { + return; + } + + if (details.kind == PointerDeviceKind.touch) { + const double edgeDeadZone = 48.0; + final screenSize = MediaQuery.sizeOf(context); + final startDx = details.globalPosition.dx; + + if (startDx < edgeDeadZone || startDx > screenSize.width - edgeDeadZone) { + logger("Edge swipe detected. Ignoring for system navigation."); + return; + } + + gestureState.value['isTouch'] = true; + gestureState.value['isDragging'] = true; + gestureState.value['startPanOffset'] = details.globalPosition; + gestureState.value['startSeekPosition'] = player.position; + gestureState.value['panDirection'] = null; + isLeftGesture.value = false; + isRightGesture.value = false; + } + } + + void onPanUpdate(DragUpdateDetails details) { + if (!(gestureState.value['isDragging'] as bool)) return; + + final startOffset = gestureState.value['startPanOffset'] as Offset; + final totalDx = details.globalPosition.dx - startOffset.dx; + final totalDy = details.globalPosition.dy - startOffset.dy; + + // 增加手势“死区”,防止误触 + const double panDeadzone = 8.0; + if (gestureState.value['panDirection'] == null) { + if (totalDx.abs() > panDeadzone || totalDy.abs() > panDeadzone) { + gestureState.value['panDirection'] = + totalDx.abs() > totalDy.abs() ? Axis.horizontal : Axis.vertical; + } + } + + final direction = gestureState.value['panDirection']; + if (direction == null) return; + + // 水平滑动 (Seek) + if (direction == Axis.horizontal) { + if (!usePlayerUiStore().state.isSeeking) { + usePlayerUiStore().updateIsSeeking(true); + } + + const double sensitivity = 3.0; // 每滑动3像素代表1秒 + final double seekSecondsOffset = totalDx / sensitivity; + final startSeconds = + (gestureState.value['startSeekPosition'] as Duration).inSeconds; + + int targetSeconds = (startSeconds + seekSecondsOffset).round(); + + // 边界检查 + targetSeconds = targetSeconds.clamp(0, player.duration.inSeconds); + + player.seek(Duration(seconds: targetSeconds)); + showProgress(); + } + + // 垂直滑动 (亮度和音量) + if (direction == Axis.vertical) { + // 仅在垂直滑动开始时判断一次左右区域 + if (!isLeftGesture.value && !isRightGesture.value) { + isLeftGesture.value = + startOffset.dx < MediaQuery.sizeOf(context).width / 2; + isRightGesture.value = !isLeftGesture.value; + } + + final double dy = details.delta.dy; + + if (isLeftGesture.value && brightness.value != null) { + final newBrightness = brightness.value! - dy / 200; + brightness.value = newBrightness.clamp(0.0, 1.0); + } + + if (isRightGesture.value && volume.value != null) { + final newVolume = volume.value! - dy / 200; + volume.value = newVolume.clamp(0.0, 1.0); + } + } + } + + // ignore: no_leading_underscores_for_local_identifiers + void _resetPanState() { + if (usePlayerUiStore().state.isSeeking) { + usePlayerUiStore().updateIsSeeking(false); + } + gestureState.value = { + ...gestureState.value, + 'isDragging': false, + 'panDirection': null, + }; + isLeftGesture.value = false; + isRightGesture.value = false; + } + + void onPanEnd(DragEndDetails details) => _resetPanState(); + void onPanCancel() => _resetPanState(); + + void onHover(PointerHoverEvent event) { + if (event.kind != PointerDeviceKind.touch) { + usePlayerUiStore().updateIsHovering(true); + showControl(); + } + } + + final cursor = useMemoized(() { + return player.isPlaying == false + ? SystemMouseCursors.basic + : SystemMouseCursors.none; + }, [player.isPlaying]); + + return Gesture( + onTapDown: onTapDown, + onTap: onTap, + onDoubleTapDown: onDoubleTapDown, + onLongPressStart: onLongPressStart, + onLongPressMoveUpdate: onLongPressMoveUpdate, + onLongPressEnd: onLongPressEnd, + onLongPressCancel: onLongPressCancel, + onPanStart: onPanStart, + onPanUpdate: onPanUpdate, + onPanEnd: onPanEnd, + onPanCancel: onPanCancel, + onHover: onHover, + isLongPress: gestureState.value['isLongPress'] as bool, + isLeftGesture: isLeftGesture.value, + isRightGesture: isRightGesture.value, + brightness: brightness.value, + volume: volume.value, + cursor: cursor, + ); +} diff --git a/lib/hooks/use_keyboard.dart b/lib/hooks/use_keyboard.dart new file mode 100644 index 0000000..25213f2 --- /dev/null +++ b/lib/hooks/use_keyboard.dart @@ -0,0 +1,262 @@ +import 'dart:io'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:iris/globals.dart'; +import 'package:iris/models/player.dart'; +import 'package:iris/models/storages/local.dart'; +import 'package:iris/pages/player/overlays/history.dart'; +import 'package:iris/pages/player/overlays/play_queue.dart'; +import 'package:iris/pages/player/overlays/track/subtitle_and_audio_track.dart'; +import 'package:iris/pages/settings/settings.dart'; +import 'package:iris/pages/storages/storages.dart'; +import 'package:iris/store/use_app_store.dart'; +import 'package:iris/store/use_play_queue_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; +import 'package:iris/utils/platform.dart'; +import 'package:iris/widgets/bottom_sheets/show_open_link_bottom_sheet.dart'; +import 'package:iris/widgets/dialogs/show_open_link_dialog.dart'; +import 'package:iris/widgets/popup.dart'; +import 'package:provider/provider.dart'; +import 'package:window_manager/window_manager.dart'; + +typedef KeyboardEvent = void Function(KeyEvent event); + +KeyboardEvent useKeyboard({ + required void Function() showControl, + required Future Function(Future) showControlForHover, + required void Function() showProgress, +}) { + final context = useContext(); + + final player = context.read(); + + void onKeyEvent(KeyEvent event) async { + if (event.runtimeType == KeyDownEvent) { + if (HardwareKeyboard.instance.isAltPressed) { + switch (event.logicalKey) { + // 退出 + case LogicalKeyboardKey.keyX: + showControl(); + await player.saveProgress(); + if (isDesktop) { + windowManager.close(); + } else { + SystemNavigator.pop(); + exit(0); + } + } + return; + } + + if (HardwareKeyboard.instance.isControlPressed) { + final appState = useAppStore().state; + switch (event.logicalKey) { + // 上一个 + case LogicalKeyboardKey.arrowLeft: + showControl(); + usePlayQueueStore().previous(); + break; + // 下一个 + case LogicalKeyboardKey.arrowRight: + showControl(); + usePlayQueueStore().next(); + break; + // 设置 + case LogicalKeyboardKey.keyP: + showControlForHover( + showPopup( + context: context, + child: const Settings(), + direction: PopupDirection.right, + ), + ); + break; + // 打开文件 + case LogicalKeyboardKey.keyO: + showControl(); + await pickLocalFile(); + showControl(); + break; + // 随机 + case LogicalKeyboardKey.keyX: + showControl(); + if (appState.shuffle) { + usePlayQueueStore().sort(); + } else { + usePlayQueueStore().shuffle(); + } + useAppStore().updateShuffle(!appState.shuffle); + break; + // 循环 + case LogicalKeyboardKey.keyR: + showControl(); + useAppStore().toggleRepeat(); + break; + // 视频缩放 + case LogicalKeyboardKey.keyV: + showControl(); + useAppStore().toggleFit(); + break; + // 历史 + case LogicalKeyboardKey.keyH: + showControlForHover( + showPopup( + context: context, + child: const History(), + direction: PopupDirection.right, + ), + ); + break; + // 打开链接 + case LogicalKeyboardKey.keyL: + showControl(); + isDesktop + ? await showOpenLinkDialog(context) + : await showOpenLinkBottomSheet(context); + showControl(); + break; + // 关闭当前播放媒体文件 + case LogicalKeyboardKey.keyC: + showControl(); + player.pause(); + usePlayQueueStore().updateCurrentIndex(-1); + break; + // 静音 + case LogicalKeyboardKey.keyM: + showControl(); + useAppStore().toggleMute(); + break; + default: + break; + } + return; + } + + final playerUiState = usePlayerUiStore().state; + switch (event.logicalKey) { + // 播放 | 暂停 + case LogicalKeyboardKey.space: + case LogicalKeyboardKey.mediaPlayPause: + showControl(); + if (context.read().isPlaying) { + useAppStore().updateAutoPlay(false); + player.pause(); + } else { + useAppStore().updateAutoPlay(true); + player.play(); + } + break; + // 上一个 + case LogicalKeyboardKey.mediaTrackPrevious: + usePlayQueueStore().previous(); + showControl(); + break; + // 下一个 + case LogicalKeyboardKey.mediaTrackNext: + showControl(); + usePlayQueueStore().next(); + break; + // 存储 + case LogicalKeyboardKey.keyF: + showControlForHover( + showPopup( + context: context, + child: const Storages(), + direction: PopupDirection.right, + ), + ); + break; + // 播放队列 + case LogicalKeyboardKey.keyP: + showControlForHover( + showPopup( + context: context, + child: const PlayQueue(), + direction: PopupDirection.right, + ), + ); + break; + // 字幕和音轨 + case LogicalKeyboardKey.keyS: + showControlForHover( + showPopup( + context: context, + child: SubtitleAndAudioTrack(), + direction: PopupDirection.right, + ), + ); + break; + // 退出全屏 + case LogicalKeyboardKey.escape: + if (isDesktop && playerUiState.isFullScreen) { + usePlayerUiStore().updateFullScreen(false); + } + break; + // 全屏 + case LogicalKeyboardKey.enter: + case LogicalKeyboardKey.f11: + if (isDesktop) { + usePlayerUiStore().updateFullScreen(!playerUiState.isFullScreen); + } + break; + case LogicalKeyboardKey.tab: + showControl(); + break; + case LogicalKeyboardKey.f10: + showControl(); + await usePlayerUiStore().toggleIsAlwaysOnTop(); + break; + case LogicalKeyboardKey.equal: + await player.stepForward(); + break; + case LogicalKeyboardKey.minus: + await player.stepBackward(); + break; + case LogicalKeyboardKey.contextMenu: + showControl(); + moreMenuKey.currentState?.showButtonMenu(); + break; + default: + break; + } + } + + if (event.runtimeType == KeyDownEvent || + event.runtimeType == KeyRepeatEvent) { + switch (event.logicalKey) { + // 快退 + case LogicalKeyboardKey.arrowLeft: + if (usePlayerUiStore().state.isShowControl) { + showControl(); + } else { + showProgress(); + } + player.backward(5); + break; + // 快进 + case LogicalKeyboardKey.arrowRight: + if (usePlayerUiStore().state.isShowControl) { + showControl(); + } else { + showProgress(); + } + player.forward(5); + break; + // 提升音量 + case LogicalKeyboardKey.arrowUp: + showControl(); + await useAppStore().updateVolume(useAppStore().state.volume + 1); + break; + // 降低音量 + case LogicalKeyboardKey.arrowDown: + showControl(); + await useAppStore().updateVolume(useAppStore().state.volume - 1); + break; + default: + break; + } + } + } + + return onKeyEvent; +} diff --git a/lib/hooks/use_volume.dart b/lib/hooks/use_volume.dart index fcfa6be..e6788c3 100644 --- a/lib/hooks/use_volume.dart +++ b/lib/hooks/use_volume.dart @@ -16,7 +16,10 @@ ValueNotifier useVolume(bool isGesture) { } catch (e) { logger('Error getting volume: $e'); } - return () => volume.value = null; + return () { + volume.value = null; + FlutterVolumeController.updateShowSystemUI(true); + }; }, [isGesture]); useEffect(() { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index a57b3c2..b4a4caa 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -27,6 +27,7 @@ "confirmUpdate": "Confirm update", "crop": "Crop", "dark": "Datk", + "dependencies": "Dependencies", "device": "Device", "download": "Download", "download_and_update": "Download and update", @@ -37,6 +38,7 @@ "edit_local_storage": "Edit local storage", "edit_webdav_storage": "Edit WebDAV storage", "enter_fullscreen": "Enter fullscreen", + "exit": "Exit", "exit_app_back_again": "Press back again to exit.", "exit_fullscreen": "Exit fullscreen", "experimental": "Experimental", @@ -52,7 +54,6 @@ "landscape": "Landscape", "language": "Language", "last_modified": "Last modified", - "libraries": "Libraries", "light": "Light", "local_storage": "Local storage", "media_file_does_not_exist": "Media file does not exist", @@ -95,6 +96,7 @@ "size": "Size", "sort": "Sort", "source_code": "Source code", + "stop": "Stop", "storage": "Storage", "stretch": "Stretch", "subtitle": "Subtitle", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 9a22871..7a0c624 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -27,6 +27,7 @@ "confirmUpdate": "确认更新", "crop": "裁切", "dark": "暗色", + "dependencies": "开源库", "device": "设备", "download": "下载", "download_and_update": "下载并更新", @@ -37,6 +38,7 @@ "edit_local_storage": "编辑本地存储", "edit_webdav_storage": "编辑 WebDAV 存储", "enter_fullscreen": "进入全屏", + "exit": "退出", "exit_app_back_again": "再次点击返回退出应用。", "exit_fullscreen": "退出全屏", "experimental": "实验性", @@ -52,7 +54,6 @@ "landscape": "横向", "language": "语言", "last_modified": "最后修改", - "libraries": "开源库", "light": "亮色", "local_storage": "本地存储", "media_file_does_not_exist": "媒体文件不存在", @@ -95,6 +96,7 @@ "size": "大小", "sort": "排序", "source_code": "源码", + "stop": "停止", "storage": "存储", "stretch": "拉伸", "subtitle": "字幕", diff --git a/lib/l10n/iso_639.dart b/lib/l10n/iso_639.dart index f00489e..6cd3fdd 100644 --- a/lib/l10n/iso_639.dart +++ b/lib/l10n/iso_639.dart @@ -1,15 +1,15 @@ class Info { final List en; - Info({required this.en}); + const Info({required this.en}); } -Map customLanguageCodes = { +const Map customLanguageCodes = { 'chs': Info(en: ['Chinese (Simplified)']), 'cht': Info(en: ['Chinese (Traditional)']), }; -Map iso_639_1 = { +const Map iso_639_1 = { 'aa': Info(en: ['Afar']), 'ab': Info(en: ['Abkhazian']), 'ae': Info(en: ['Avestan']), @@ -203,7 +203,7 @@ Map iso_639_1 = { 'zu': Info(en: ['Zulu']), }; -Map iso_639_2 = { +const Map iso_639_2 = { 'aar': Info(en: ['Afar']), 'abk': Info(en: ['Abkhazian']), 'ace': Info(en: ['Achinese']), diff --git a/lib/l10n/languages.dart b/lib/l10n/languages.dart index 69dafea..57b6516 100644 --- a/lib/l10n/languages.dart +++ b/lib/l10n/languages.dart @@ -1,4 +1,4 @@ -final Map languages = { +const Map languages = { 'zh': '简体中文', 'en': 'English', }; diff --git a/lib/main.dart b/lib/main.dart index 1dc5245..549d3aa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,8 @@ import 'dart:io'; import 'package:app_links/app_links.dart'; +import 'package:flutter/services.dart'; import 'package:fvp/fvp.dart' as fvp; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; import 'package:iris/info.dart'; @@ -65,7 +65,6 @@ void main(List arguments) async { backgroundColor: Colors.transparent, skipTaskbar: false, titleBarStyle: TitleBarStyle.hidden, - title: INFO.title, ); windowManager.waitUntilReadyToShow(windowOptions, () async { @@ -74,6 +73,8 @@ void main(List arguments) async { }); } + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); + MediaStream mediaStream = MediaStream(); mediaStream.startServer(); @@ -85,14 +86,6 @@ class MyApp extends HookWidget { @override Widget build(BuildContext context) { - useEffect(() { - SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - systemNavigationBarColor: Colors.transparent, - )); - return null; - }, []); - useEffect(() { () async { globals.storagePermissionStatus = Platform.isAndroid diff --git a/lib/models/file.dart b/lib/models/file.dart index 2d4351d..83340c9 100644 --- a/lib/models/file.dart +++ b/lib/models/file.dart @@ -6,7 +6,6 @@ part 'file.freezed.dart'; part 'file.g.dart'; enum ContentType { - dir, video, audio, image, @@ -38,7 +37,7 @@ abstract class FileItem with _$FileItem { factory FileItem.fromJson(Map json) => _$FileItemFromJson(json); - String getID() => '$storageId:$uri}'; + String getID() => '$storageId:$uri'; } @freezed diff --git a/lib/models/player.dart b/lib/models/player.dart index 5067ff5..b0f885b 100644 --- a/lib/models/player.dart +++ b/lib/models/player.dart @@ -11,12 +11,8 @@ class MediaPlayer { final Duration position; final Duration duration; final Duration buffer; - final bool seeking; - final double? aspect; - final double? width; - final double? height; - final void Function(Duration) updatePosition; - final void Function(bool) updateSeeking; + final double width; + final double height; final Future Function() saveProgress; final Future Function() play; final Future Function() pause; @@ -24,7 +20,7 @@ class MediaPlayer { final Future Function(int) forward; final Future Function() stepBackward; final Future Function() stepForward; - final Future Function(Duration) seekTo; + final Future Function(Duration) seek; MediaPlayer({ required this.isInitializing, @@ -33,12 +29,8 @@ class MediaPlayer { required this.position, required this.duration, required this.buffer, - required this.seeking, - required this.aspect, required this.width, required this.height, - required this.updatePosition, - required this.updateSeeking, required this.saveProgress, required this.play, required this.pause, @@ -46,7 +38,7 @@ class MediaPlayer { required this.forward, required this.stepBackward, required this.stepForward, - required this.seekTo, + required this.seek, }); } @@ -71,12 +63,8 @@ class MediaKitPlayer extends MediaPlayer { required super.position, required super.duration, required super.buffer, - required super.seeking, - required super.aspect, required super.width, required super.height, - required super.updatePosition, - required super.updateSeeking, required super.saveProgress, required super.play, required super.pause, @@ -84,7 +72,7 @@ class MediaKitPlayer extends MediaPlayer { required super.forward, required super.stepBackward, required super.stepForward, - required super.seekTo, + required super.seek, }); } @@ -101,12 +89,8 @@ class FvpPlayer extends MediaPlayer { required super.position, required super.duration, required super.buffer, - required super.seeking, - required super.aspect, required super.width, required super.height, - required super.updatePosition, - required super.updateSeeking, required super.saveProgress, required super.play, required super.pause, @@ -114,6 +98,6 @@ class FvpPlayer extends MediaPlayer { required super.forward, required super.stepBackward, required super.stepForward, - required super.seekTo, + required super.seek, }); } diff --git a/lib/models/storages/ftp.dart b/lib/models/storages/ftp.dart index 578ddbf..7b28d6e 100644 --- a/lib/models/storages/ftp.dart +++ b/lib/models/storages/ftp.dart @@ -1,10 +1,10 @@ import 'dart:convert'; - import 'package:iris/models/file.dart'; import 'package:iris/models/storages/storage.dart'; import 'package:iris/utils/check_content_type.dart'; -import 'package:iris/utils/find_subtitle.dart'; +import 'package:iris/utils/get_subtitle_map.dart'; import 'package:iris/utils/logger.dart'; +import 'package:path/path.dart' as p; import 'package:pure_ftp/pure_ftp.dart'; Future> getFTPFiles( @@ -26,37 +26,56 @@ Future> getFTPFiles( try { await client.connect(); - await client.fs.changeDirectory(path.join('/').replaceFirst('//', '/')); + await client.fs.changeDirectory(path.join('/').replaceAll('//', '/')); final files = await client.fs.listDirectory(); await client.disconnect(); final baseUri = - 'ftp?host=${storage.host}&port=${storage.port}&path=${path.join('/').replaceFirst('//', '/')}'; + 'ftp?host=${storage.host}&port=${storage.port}&path=${path.join('/').replaceAll('//', '/')}'; + + String getUri(String fileName) { + final separator = baseUri.endsWith('/') ? '' : '/'; + return Uri.encodeFull('$baseUri$separator$fileName'); + } + + final subtitleMap = getSubtitleMap( + files: files, + getName: (file) => file.name, + getUri: (file) => getUri(file.name), + ); - return await Future.wait(files.map( - (file) async => FileItem( - storageId: storage.id, - storageType: StorageType.ftp, - name: file.name, - uri: Uri.encodeFull('$baseUri/${file.name}'), - path: [...path, file.name], - isDir: file.isDirectory, - size: file.isDirectory ? 0 : file.info?.size ?? 0, - lastModified: file.info?.modifyTime != null - ? DateTime.tryParse(file.info!.modifyTime!) - : null, - type: file.isDirectory ? ContentType.dir : checkContentType(file.name), - subtitles: await findSubtitle( - files.map((file) => file.name).toList(), - file.name, - baseUri, - ), - ), - )); + List fileItems = []; + + for (final file in files) { + if (file.isDirectory || isMediaFile(file.name)) { + final basename = p.basenameWithoutExtension(file.name).split('.').first; + fileItems.add( + FileItem( + storageId: storage.id, + storageType: StorageType.ftp, + name: file.name, + uri: getUri(file.name), + path: [...path, file.name], + isDir: file.isDirectory, + size: file.isDirectory ? 0 : file.info?.size ?? 0, + lastModified: file.info?.modifyTime != null + ? DateTime.tryParse(file.info!.modifyTime!) + : null, + type: file.isDirectory + ? ContentType.other + : checkContentType(file.name), + subtitles: + isVideoFile(file.name) ? subtitleMap[basename] ?? [] : [], + ), + ); + } + } + + return fileItems; } catch (error) { - logger('Error testing FTP: $error'); + logger('Error getting FTP files: $error'); return []; } } diff --git a/lib/models/storages/local.dart b/lib/models/storages/local.dart index e5b81e2..931a5bf 100644 --- a/lib/models/storages/local.dart +++ b/lib/models/storages/local.dart @@ -8,9 +8,8 @@ import 'package:iris/models/storages/storage.dart'; import 'package:iris/models/store/play_queue_state.dart'; import 'package:iris/store/use_app_store.dart'; import 'package:iris/store/use_play_queue_store.dart'; -import 'package:iris/utils/files_filter.dart'; import 'package:iris/utils/files_sort.dart'; -import 'package:iris/utils/find_subtitle.dart'; +import 'package:iris/utils/get_subtitle_map.dart'; import 'package:iris/utils/get_localizations.dart'; import 'package:iris/utils/logger.dart'; import 'package:iris/utils/path_conv.dart'; @@ -19,6 +18,7 @@ import 'package:path/path.dart' as p; import 'package:iris/models/file.dart'; import 'package:iris/utils/check_content_type.dart'; import 'package:saf_util/saf_util.dart'; +import 'package:saf_util/saf_util_platform_interface.dart'; Future> getLocalStorages( BuildContext context, @@ -84,9 +84,22 @@ Future> getLocalStorages( return storages; } else if (isAndroid) { final androidXStorage = AndroidXStorage(); - final external = await androidXStorage.getExternalStorageDirectory(); - final sdcard = await androidXStorage.getSDCardStorageDirectory(); - final usbs = await androidXStorage.getUSBStorageDirectories(); + final external = + await androidXStorage.getExternalStorageDirectory().catchError((error) { + logger('Error getting external storage: $error'); + return null; + }); + final sdcard = + await androidXStorage.getSDCardStorageDirectory().catchError((error) { + logger('Error getting SD card: $error'); + return null; + }); + final usbs = + await androidXStorage.getUSBStorageDirectories().catchError((error) { + logger('Error getting USB storages: $error'); + return []; + }); + List storages = []; if (external != null) { @@ -142,8 +155,11 @@ Future getLocalPlayQueue(String filePath) async { basePath: dirPath, ).getFiles(dirPath); final List sortedFiles = filesSort(files: files); - final List filteredFiles = - filesFilter(sortedFiles, [ContentType.video, ContentType.audio]); + final List filteredFiles = sortedFiles + .where( + (file) => [ContentType.video, ContentType.audio].contains(file.type)) + .toList(); + final List playQueue = filteredFiles .asMap() .entries @@ -182,64 +198,84 @@ Future pickLocalFile() async { Future> getLocalFiles( LocalStorage storage, List path) async { - final directory = Directory(p.normalize(path.join('/'))); + final directoryPath = p.joinAll(path); + final directory = Directory(directoryPath); - List files = []; - try { - final entities = directory.list(); + if (!await directory.exists()) { + logger('Error: Directory does not exist at $directoryPath'); + return []; + } - await for (final entity in entities) { - final isDir = entity is Directory; - int size = 0; - DateTime? lastModified; - if (!isDir) { - final file = File(entity.path); - try { - size = await file.length(); - lastModified = await file.lastModified(); - } on PathAccessException catch (e) { - logger( - 'PathAccessException when getting file info for ${entity.path}: $e'); - } catch (e) { - logger('Error getting file info for ${entity.path}: $e'); - } - } + final Map> groupedEntities = {}; + final allEntities = await directory.list().toList(); + + for (final entity in allEntities) { + final baseName = p.basenameWithoutExtension(entity.path); + groupedEntities.putIfAbsent(baseName, () => []).add(entity); + } - if (isDir) { - final dir = Directory(entity.path); - try { - final stat = await dir.stat(); - lastModified = stat.modified; - } on PathAccessException catch (e) { - logger( - 'PathAccessException when getting directory info for ${entity.path}: $e'); - } catch (e) { - logger('Error getting directory info for ${entity.path}: $e'); + final List fileItems = []; + final subtitleExtensions = {'ass', 'srt', 'vtt', 'sub'}; + + for (final group in groupedEntities.values) { + final videos = group + .where((e) => + e is! Directory && checkContentType(e.path) == ContentType.video) + .toList(); + final subtitles = group.where((e) { + final ext = p.extension(e.path).replaceFirst('.', ''); + return e is! Directory && subtitleExtensions.contains(ext); + }).toList(); + final others = group + .where((e) => !videos.contains(e) && !subtitles.contains(e)) + .toList(); + + for (final video in videos) { + final videoStat = await video.stat(); + final associatedSubtitles = subtitles.map((sub) { + final baseName = p.basenameWithoutExtension(video.path); + String subTitleName = p.basename(sub.path); + final regex = RegExp(r'^' + RegExp.escape(baseName) + r'\.(.+?)\.'); + final match = regex.firstMatch(subTitleName); + if (match != null) { + subTitleName = match.group(1) ?? subTitleName; } - } + return Subtitle(name: subTitleName, uri: sub.path); + }).toList(); - final subtitles = await findLocalSubtitle(entity.path); + fileItems.add(FileItem( + storageId: storage.id, + storageType: storage.type, + name: p.basename(video.path), + uri: video.path, + path: [...path, p.basename(video.path)], + isDir: false, + size: videoStat.size, + lastModified: videoStat.modified, + type: ContentType.video, + subtitles: associatedSubtitles, + )); + } - files.add(FileItem( + for (final entity in others) { + final stat = await entity.stat(); + final isDir = entity is Directory; + fileItems.add(FileItem( storageId: storage.id, storageType: storage.type, name: p.basename(entity.path), uri: entity.path, path: [...path, p.basename(entity.path)], isDir: isDir, - size: size, - lastModified: lastModified, - type: - isDir ? ContentType.dir : checkContentType(p.basename(entity.path)), - subtitles: subtitles, + size: isDir ? 0 : stat.size, + lastModified: stat.modified, + type: isDir ? ContentType.other : checkContentType(entity.path), + subtitles: [], )); } - } catch (e) { - logger('Error reading directory $path : $e'); - return []; } - return files; + return fileItems; } Future pickContentFile() async { @@ -263,24 +299,31 @@ Future pickContentFile() async { } Future> getContentFiles(String uri) async { - final list = await SafUtil().list(uri); + final files = await SafUtil().list(uri); - return await Future.wait(list - .map((file) async => FileItem( - name: file.name, - uri: file.uri, - path: [uri, file.name], - isDir: file.isDir, - size: file.isDir ? 0 : file.length, - lastModified: - DateTime.fromMillisecondsSinceEpoch(file.lastModified), - type: file.isDir ? ContentType.dir : checkContentType(file.name), - subtitles: await findSubtitle( - list.map((e) => e.name).toList(), - file.name, - uri, - encodeUri: false, - ), - )) - .toList()); + final subtitleMap = getSubtitleMap( + files: files, + getName: (file) => file.name, + getUri: (file) => file.uri, + ); + + List fileItems = []; + + for (final file in files) { + if (file.isDir || isMediaFile(file.name)) { + final basename = p.basenameWithoutExtension(file.name).split('.').first; + fileItems.add(FileItem( + name: file.name, + uri: file.uri, + path: [uri, file.name], + isDir: file.isDir, + size: file.isDir ? 0 : file.length, + lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified), + type: file.isDir ? ContentType.other : checkContentType(file.name), + subtitles: isVideoFile(file.name) ? subtitleMap[basename] ?? [] : [], + )); + } + } + + return fileItems; } diff --git a/lib/models/storages/webdav.dart b/lib/models/storages/webdav.dart index 4159a31..5bab88f 100644 --- a/lib/models/storages/webdav.dart +++ b/lib/models/storages/webdav.dart @@ -1,8 +1,9 @@ import 'dart:convert'; import 'package:iris/models/storages/storage.dart'; import 'package:iris/utils/check_content_type.dart'; -import 'package:iris/utils/find_subtitle.dart'; +import 'package:iris/utils/get_subtitle_map.dart'; import 'package:iris/utils/logger.dart'; +import 'package:path/path.dart' as p; import 'package:webdav_client/webdav_client.dart' as webdav; import 'package:iris/models/file.dart'; @@ -37,7 +38,9 @@ Future testWebDAV(WebDAVStorage storage) async { } Future> getWebDAVFiles( - WebDAVStorage storage, List path) async { + WebDAVStorage storage, + List path, +) async { final id = storage.id; final host = storage.host; final port = storage.port; @@ -59,26 +62,58 @@ Future> getWebDAVFiles( var files = await client.readDir(path.join('/')); - final String baseUri = - 'http${https ? 's' : ''}://$host:$port${path.join('/')}'; + final cleanPathSegments = path.map((e) => e.replaceAll('/', '')).toList(); + final baseUri = Uri( + scheme: storage.https ? 'https' : 'http', + host: storage.host, + port: int.tryParse(storage.port), + pathSegments: cleanPathSegments, + ); + final baseUriString = baseUri.toString(); + + String getUri(String fileName) { + try { + final dirUri = Uri.parse( + baseUriString.endsWith('/') ? baseUriString : '$baseUriString/'); + return dirUri.resolve(fileName).toString(); + } catch (e) { + final separator = baseUriString.endsWith('/') ? '' : '/'; + return '$baseUriString$separator$fileName'; + } + } + + final subtitleMap = getSubtitleMap( + files: files, + getName: (file) => file.name ?? '', + getUri: (file) => getUri(file.name ?? ''), + ); - return await Future.wait(files.map((file) async => FileItem( + List fileItems = []; + + for (final file in files) { + final fileName = file.name; + + if (fileName == null) continue; + + final isDir = file.isDir; + if (isDir == true || isMediaFile(fileName)) { + final basename = p.basenameWithoutExtension(fileName).split('.').first; + fileItems.add(FileItem( storageId: id, storageType: StorageType.webdav, - name: '${file.name}', - uri: Uri.encodeFull('$baseUri/${file.name}'), - path: [...path, '${file.name}'], - isDir: file.isDir ?? false, + name: fileName, + uri: getUri(fileName), + path: [...path, fileName], + isDir: isDir ?? false, size: file.size ?? 0, lastModified: file.mTime, - type: file.isDir ?? false - ? ContentType.dir - : checkContentType(file.name!), - subtitles: await findSubtitle( - files.map((file) => file.name as String).toList(), - file.name as String, - baseUri), - ))); + type: isDir ?? false ? ContentType.other : checkContentType(fileName), + subtitles: isVideoFile(fileName) ? subtitleMap[basename] ?? [] : [], + )); + } + } + + return fileItems; } String getWebDAVAuth(WebDAVStorage storage) => diff --git a/lib/models/store/player_ui_state.dart b/lib/models/store/player_ui_state.dart new file mode 100644 index 0000000..7c9ce5d --- /dev/null +++ b/lib/models/store/player_ui_state.dart @@ -0,0 +1,21 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter/foundation.dart'; + +part 'player_ui_state.freezed.dart'; +part 'player_ui_state.g.dart'; + +@freezed +abstract class PlayerUiState with _$PlayerUiState { + const factory PlayerUiState({ + @Default(0) double aspectRatio, + @Default(false) bool isAlwaysOnTop, + @Default(false) bool isFullScreen, + @Default(false) bool isSeeking, + @Default(false) bool isHovering, + @Default(true) bool isShowControl, + @Default(false) bool isShowProgress, + }) = _PlayerUiState; + + factory PlayerUiState.fromJson(Map json) => + _$PlayerUiStateFromJson(json); +} diff --git a/lib/models/store/ui_state.dart b/lib/models/store/ui_state.dart deleted file mode 100644 index 2c89b99..0000000 --- a/lib/models/store/ui_state.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:flutter/foundation.dart'; - -part 'ui_state.freezed.dart'; -part 'ui_state.g.dart'; - -@freezed -abstract class UiState with _$UiState { - const factory UiState({ - @Default(false) bool isAlwaysOnTop, - @Default(false) bool isFullScreen, - }) = _UiState; - - factory UiState.fromJson(Map json) => - _$UiStateFromJson(json); -} diff --git a/lib/oss_licenses.dart b/lib/oss_licenses.dart index fe3eb5e..6674294 100644 --- a/lib/oss_licenses.dart +++ b/lib/oss_licenses.dart @@ -1,15 +1,17 @@ +// dart format off // cSpell:disable // ignore_for_file: always_put_required_named_parameters_first // ignore_for_file: constant_identifier_names // ignore_for_file: sort_constructors_first -// This code was generated by flutter_oss_licenses -// https://pub.dev/packages/flutter_oss_licenses +// This code was generated by dart_pubspec_licenses +// https://pub.dev/packages/dart_pubspec_licenses /// All dependencies including transitive dependencies. const allDependencies = [ __fe_analyzer_shared, _analyzer, + _analyzer_plugin, _android_x_storage, _app_links, _app_links_linux, @@ -23,14 +25,13 @@ const allDependencies = [ _build, _build_config, _build_daemon, - _build_resolvers, _build_runner, - _build_runner_core, _built_collection, _built_value, _characters, _charset, _checked_yaml, + _ci, _cli_util, _clock, _code_builder, @@ -42,6 +43,10 @@ const allDependencies = [ _cryptography, _csslib, _cupertino_icons, + _custom_lint, + _custom_lint_builder, + _custom_lint_core, + _custom_lint_visitor, _dart_pubspec_licenses, _dart_style, _dbus, @@ -62,9 +67,9 @@ const allDependencies = [ _flutter, _flutter_breadcrumb, _flutter_hooks, + _flutter_hooks_lint, _flutter_lints, _flutter_markdown, - _flutter_oss_licenses, _flutter_plugin_android_lifecycle, _flutter_secure_storage, _flutter_secure_storage_linux, @@ -73,6 +78,7 @@ const allDependencies = [ _flutter_secure_storage_web, _flutter_secure_storage_windows, _flutter_volume_controller, + _flutter_web_plugins, _flutter_zustand, _freezed, _freezed_annotation, @@ -83,6 +89,7 @@ const allDependencies = [ _google_fonts, _graphs, _gtk, + _hotreloader, _html, _http, _http_methods, @@ -144,6 +151,7 @@ const allDependencies = [ _pub_semver, _pubspec_parse, _pure_ftp, + _rxdart, _saf_util, _safe_local_storage, _screen_brightness, @@ -174,7 +182,6 @@ const allDependencies = [ _synchronized, _term_glyph, _test_api, - _timing, _typed_data, _universal_platform, _uri_parser, @@ -214,96 +221,107 @@ const allDependencies = [ /// Direct `dependencies`. const dependencies = [ - _android_x_storage, - _app_links, - _collection, + _flutter, _cupertino_icons, - _desktop_drop, - _device_info_plus, - _disks_desktop, - _drives_windows, - _dynamic_color, + _intl, _file_picker, - _flutter, _flutter_breadcrumb, _flutter_hooks, - _flutter_markdown, _flutter_secure_storage, - _flutter_volume_controller, _flutter_zustand, - _freezed_annotation, - _fvp, - _google_fonts, - _http, - _intl, - _json_annotation, _media_kit, - _media_kit_libs_video, _media_kit_video, - _media_stream, - _package_info_plus, + _media_kit_libs_video, _path, _path_provider, - _permission_handler, - _popover, + _package_info_plus, _provider, - _pure_ftp, - _saf_util, - _screen_brightness, - _scrollable_positioned_list, + _webdav_client, + _window_manager, _url_launcher, + _scrollable_positioned_list, + _google_fonts, + _dynamic_color, + _window_size, _uuid, + _flutter_markdown, + _http, + _collection, + _json_annotation, + _freezed_annotation, + _disks_desktop, + _android_x_storage, + _permission_handler, + _desktop_drop, + _app_links, + _device_info_plus, + _saf_util, + _screen_brightness, + _flutter_volume_controller, + _fvp, _video_player, _wakelock_plus, - _webdav_client, - _window_manager, - _window_size + _popover, + _pure_ftp, + _drives_windows, + _media_stream ]; /// Direct `dev_dependencies`. const devDependencies = [ - _build_runner, _flutter_lints, - _flutter_oss_licenses, + _dart_pubspec_licenses, + _build_runner, _freezed, _json_serializable, - _msix + _msix, + _flutter_hooks_lint, + _custom_lint ]; /// Package license definition. class Package { /// Package name final String name; + /// Description final String description; - /// Website URL - final String? homepage; - /// Repository URL - final String? repository; + /// Authors final List authors; - /// Version - final String version; - /// License - final String? license; + /// Whether the license is in markdown format or not (plain text). final bool isMarkdown; + /// Whether the package is included in the SDK or not. final bool isSdk; + /// Direct dependencies final List dependencies; + /// Website URL + final String? homepage; + + /// Repository URL + final String? repository; + + /// Version + final String? version; + + /// License + final String? license; + const Package({ required this.name, required this.description, - this.homepage, - this.repository, required this.authors, - required this.version, - this.license, required this.isMarkdown, required this.isSdk, required this.dependencies, + this.homepage, + this.repository, + this.version, + this.license, }); } @@ -315,13 +333,15 @@ class PackageRef { Package resolve() => allDependencies.firstWhere((d) => d.name == name); } -/// _fe_analyzer_shared 82.0.0 +/// _fe_analyzer_shared 88.0.0 const __fe_analyzer_shared = Package( name: '_fe_analyzer_shared', - description: 'Logic that is shared between the front_end and analyzer packages.', - repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/_fe_analyzer_shared', + description: + 'Logic that is shared between the front_end and analyzer packages.', + repository: + 'https://github.com/dart-lang/sdk/tree/main/pkg/_fe_analyzer_shared', authors: [], - version: '82.0.0', + version: '88.0.0', license: '''Copyright 2019, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -351,16 +371,16 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta')] - ); + dependencies: [PackageRef('meta')]); -/// analyzer 7.4.5 +/// analyzer 8.1.1 const _analyzer = Package( name: 'analyzer', - description: 'This package provides a library that performs static analysis of Dart code.', + description: + 'This package provides a library that performs static analysis of Dart code.', repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/analyzer', authors: [], - version: '7.4.5', + version: '8.1.1', license: '''Copyright 2013, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -390,13 +410,73 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('_fe_analyzer_shared'), PackageRef('collection'), PackageRef('convert'), PackageRef('crypto'), PackageRef('glob'), PackageRef('meta'), PackageRef('package_config'), PackageRef('path'), PackageRef('pub_semver'), PackageRef('source_span'), PackageRef('watcher'), PackageRef('yaml')] - ); + dependencies: [ + PackageRef('_fe_analyzer_shared'), + PackageRef('collection'), + PackageRef('convert'), + PackageRef('crypto'), + PackageRef('glob'), + PackageRef('meta'), + PackageRef('package_config'), + PackageRef('path'), + PackageRef('pub_semver'), + PackageRef('source_span'), + PackageRef('watcher'), + PackageRef('yaml') + ]); + +/// analyzer_plugin 0.13.7 +const _analyzer_plugin = Package( + name: 'analyzer_plugin', + description: + 'A framework and support code for building plugins for the analysis server.', + repository: + 'https://github.com/dart-lang/sdk/tree/main/pkg/analyzer_plugin', + authors: [], + version: '0.13.7', + license: '''Copyright 2017, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + dependencies: [ + PackageRef('analyzer'), + PackageRef('collection'), + PackageRef('dart_style'), + PackageRef('pub_semver'), + PackageRef('yaml'), + PackageRef('path') + ]); /// android_x_storage 1.0.2 const _android_x_storage = Package( name: 'android_x_storage', - description: 'A new Flutter plugin for Android to get the external storage directories.', + description: + 'A new Flutter plugin for Android to get the external storage directories.', homepage: 'https://github.com/djeddi-yacine/android_x_storage', repository: 'https://github.com/djeddi-yacine/android_x_storage', authors: [], @@ -432,16 +512,19 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); -/// app_links 6.4.0 +/// app_links 6.4.1 const _app_links = Package( name: 'app_links', - description: 'Android App Links, Deep Links, iOs Universal Links and Custom URL schemes handler for Flutter (desktop included).', + description: + 'Android App Links, Deep Links, iOs Universal Links and Custom URL schemes handler for Flutter (desktop included).', homepage: 'https://github.com/llfbandit/app_links', authors: [], - version: '6.4.0', + version: '6.4.1', license: '''Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -645,14 +728,19 @@ const _app_links = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('app_links_linux'), PackageRef('app_links_platform_interface'), PackageRef('app_links_web')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('app_links_linux'), + PackageRef('app_links_platform_interface'), + PackageRef('app_links_web') + ]); /// app_links_linux 1.0.3 const _app_links_linux = Package( name: 'app_links_linux', description: 'Linux platform implementation of app_links plugin.', - homepage: 'https://github.com/llfbandit/app_links/tree/master/app_links_linux', + homepage: + 'https://github.com/llfbandit/app_links/tree/master/app_links_linux', authors: [], version: '1.0.3', license: '''Apache License @@ -858,14 +946,18 @@ const _app_links_linux = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('app_links_platform_interface'), PackageRef('gtk')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('app_links_platform_interface'), + PackageRef('gtk') + ]); /// app_links_platform_interface 2.0.2 const _app_links_platform_interface = Package( name: 'app_links_platform_interface', description: 'A common platform interface for the app_links plugin.', - homepage: 'https://github.com/llfbandit/app_links/tree/master/app_links_platform_interface', + homepage: + 'https://github.com/llfbandit/app_links/tree/master/app_links_platform_interface', authors: [], version: '2.0.2', license: '''Apache License @@ -1071,14 +1163,17 @@ const _app_links_platform_interface = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); /// app_links_web 1.0.4 const _app_links_web = Package( name: 'app_links_web', description: 'Web platform implementation of app_links plugin.', - homepage: 'https://github.com/llfbandit/app_links/tree/master/app_links_web', + homepage: + 'https://github.com/llfbandit/app_links/tree/master/app_links_web', authors: [], version: '1.0.4', license: '''Apache License @@ -1284,13 +1379,18 @@ const _app_links_web = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('app_links_platform_interface'), PackageRef('web')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_web_plugins'), + PackageRef('app_links_platform_interface'), + PackageRef('web') + ]); /// archive 4.0.7 const _archive = Package( name: 'archive', - description: 'Provides encoders and decoders for various archive and compression formats such as zip, tar, bzip2, gzip, and zlib.', + description: + 'Provides encoders and decoders for various archive and compression formats such as zip, tar, bzip2, gzip, and zlib.', repository: 'https://github.com/brendan-duncan/archive', authors: [], version: '4.0.7', @@ -1318,13 +1418,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('crypto'), PackageRef('path'), PackageRef('posix')] - ); + dependencies: [ + PackageRef('crypto'), + PackageRef('path'), + PackageRef('posix') + ]); /// args 2.7.0 const _args = Package( name: 'args', - description: 'Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options.', + description: + 'Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options.', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/args', authors: [], version: '2.7.0', @@ -1357,16 +1461,16 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); -/// asn1lib 1.6.4 +/// asn1lib 1.6.5 const _asn1lib = Package( name: 'asn1lib', - description: 'An ASN1 parser library for Dart. Encodes / decodes from ASN1 Objects to BER bytes', + description: + 'An ASN1 parser library for Dart. Encodes / decodes from ASN1 Objects to BER bytes', homepage: 'https://github.com/wstrange/asn1lib', authors: [], - version: '1.6.4', + version: '1.6.5', license: '''http://opensource.org/licenses/BSD-3-Clause Copyright (c) 2015, Warren Strange All rights reserved. @@ -1393,13 +1497,13 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// async 2.13.0 const _async = Package( name: 'async', - description: "Utility functions and classes related to the 'dart:async' library.", + description: + "Utility functions and classes related to the 'dart:async' library.", repository: 'https://github.com/dart-lang/core/tree/main/pkgs/async', authors: [], version: '2.13.0', @@ -1432,14 +1536,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('meta')] - ); + dependencies: [PackageRef('collection'), PackageRef('meta')]); /// boolean_selector 2.1.2 const _boolean_selector = Package( name: 'boolean_selector', - description: "A flexible syntax for boolean expressions, based on a simplified version of Dart's expression syntax.", - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/boolean_selector', + description: + "A flexible syntax for boolean expressions, based on a simplified version of Dart's expression syntax.", + repository: + 'https://github.com/dart-lang/tools/tree/main/pkgs/boolean_selector', authors: [], version: '2.1.2', license: '''Copyright 2016, the Dart project authors. @@ -1471,16 +1576,16 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('source_span'), PackageRef('string_scanner')] - ); + dependencies: [PackageRef('source_span'), PackageRef('string_scanner')]); -/// build 2.4.2 +/// build 4.0.0 const _build = Package( name: 'build', - description: 'A package for authoring build_runner compatible code generators.', + description: + 'A package for authoring build_runner compatible code generators.', repository: 'https://github.com/dart-lang/build/tree/master/build', authors: [], - version: '2.4.2', + version: '4.0.0', license: '''Copyright 2016, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -1510,16 +1615,23 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('async'), PackageRef('convert'), PackageRef('crypto'), PackageRef('glob'), PackageRef('logging'), PackageRef('meta'), PackageRef('package_config'), PackageRef('path')] - ); + dependencies: [ + PackageRef('analyzer'), + PackageRef('crypto'), + PackageRef('glob'), + PackageRef('logging'), + PackageRef('package_config'), + PackageRef('path') + ]); -/// build_config 1.1.2 +/// build_config 1.2.0 const _build_config = Package( name: 'build_config', - description: 'Format definition and support for parsing `build.yaml` configuration.', + description: + 'Format definition and support for parsing `build.yaml` configuration.', repository: 'https://github.com/dart-lang/build/tree/master/build_config', authors: [], - version: '1.1.2', + version: '1.2.0', license: '''Copyright 2017, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -1549,8 +1661,12 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('checked_yaml'), PackageRef('json_annotation'), PackageRef('path'), PackageRef('pubspec_parse'), PackageRef('yaml')] - ); + dependencies: [ + PackageRef('checked_yaml'), + PackageRef('json_annotation'), + PackageRef('path'), + PackageRef('pubspec_parse') + ]); /// build_daemon 4.0.4 const _build_daemon = Package( @@ -1588,55 +1704,29 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('built_collection'), PackageRef('built_value'), PackageRef('crypto'), PackageRef('http_multi_server'), PackageRef('logging'), PackageRef('path'), PackageRef('pool'), PackageRef('shelf'), PackageRef('shelf_web_socket'), PackageRef('stream_transform'), PackageRef('watcher'), PackageRef('web_socket_channel')] - ); - -/// build_resolvers 2.4.4 -const _build_resolvers = Package( - name: 'build_resolvers', - description: 'Resolve Dart code in a Builder', - repository: 'https://github.com/dart-lang/build/tree/master/build_resolvers', - authors: [], - version: '2.4.4', - license: '''Copyright 2018, the Dart project authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('async'), PackageRef('build'), PackageRef('collection'), PackageRef('convert'), PackageRef('crypto'), PackageRef('graphs'), PackageRef('logging'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('pub_semver'), PackageRef('stream_transform'), PackageRef('yaml')] - ); - -/// build_runner 2.4.15 + dependencies: [ + PackageRef('built_collection'), + PackageRef('built_value'), + PackageRef('crypto'), + PackageRef('http_multi_server'), + PackageRef('logging'), + PackageRef('path'), + PackageRef('pool'), + PackageRef('shelf'), + PackageRef('shelf_web_socket'), + PackageRef('stream_transform'), + PackageRef('watcher'), + PackageRef('web_socket_channel') + ]); + +/// build_runner 2.8.0 const _build_runner = Package( name: 'build_runner', - description: 'A build system for Dart code generation and modular compilation.', + description: + 'A build system for Dart code generation and modular compilation.', repository: 'https://github.com/dart-lang/build/tree/master/build_runner', authors: [], - version: '2.4.15', + version: '2.8.0', license: '''Copyright 2016, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -1666,52 +1756,47 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('args'), PackageRef('async'), PackageRef('build'), PackageRef('build_config'), PackageRef('build_daemon'), PackageRef('build_resolvers'), PackageRef('build_runner_core'), PackageRef('code_builder'), PackageRef('collection'), PackageRef('crypto'), PackageRef('dart_style'), PackageRef('frontend_server_client'), PackageRef('glob'), PackageRef('graphs'), PackageRef('http'), PackageRef('http_multi_server'), PackageRef('io'), PackageRef('js'), PackageRef('logging'), PackageRef('meta'), PackageRef('mime'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('pub_semver'), PackageRef('pubspec_parse'), PackageRef('shelf'), PackageRef('shelf_web_socket'), PackageRef('stack_trace'), PackageRef('stream_transform'), PackageRef('timing'), PackageRef('watcher'), PackageRef('web'), PackageRef('web_socket_channel'), PackageRef('yaml')] - ); - -/// build_runner_core 8.0.0 -const _build_runner_core = Package( - name: 'build_runner_core', - description: 'Core tools to organize the structure of a build and run Builders.', - repository: 'https://github.com/dart-lang/build/tree/master/build_runner_core', - authors: [], - version: '8.0.0', - license: '''Copyright 2018, the Dart project authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('async'), PackageRef('build'), PackageRef('build_config'), PackageRef('build_resolvers'), PackageRef('collection'), PackageRef('convert'), PackageRef('crypto'), PackageRef('glob'), PackageRef('graphs'), PackageRef('json_annotation'), PackageRef('logging'), PackageRef('meta'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('timing'), PackageRef('watcher'), PackageRef('yaml')] - ); + dependencies: [ + PackageRef('analyzer'), + PackageRef('args'), + PackageRef('async'), + PackageRef('build'), + PackageRef('build_config'), + PackageRef('build_daemon'), + PackageRef('built_collection'), + PackageRef('built_value'), + PackageRef('code_builder'), + PackageRef('collection'), + PackageRef('convert'), + PackageRef('crypto'), + PackageRef('dart_style'), + PackageRef('frontend_server_client'), + PackageRef('glob'), + PackageRef('graphs'), + PackageRef('http_multi_server'), + PackageRef('io'), + PackageRef('json_annotation'), + PackageRef('logging'), + PackageRef('meta'), + PackageRef('mime'), + PackageRef('package_config'), + PackageRef('path'), + PackageRef('pool'), + PackageRef('pub_semver'), + PackageRef('shelf'), + PackageRef('shelf_web_socket'), + PackageRef('stack_trace'), + PackageRef('stream_transform'), + PackageRef('watcher'), + PackageRef('web_socket_channel'), + PackageRef('yaml') + ]); /// built_collection 5.1.1 const _built_collection = Package( name: 'built_collection', - description: '''Immutable collections based on the SDK collections. Each SDK collection class is split into a new immutable collection class and a corresponding mutable builder class. + description: + '''Immutable collections based on the SDK collections. Each SDK collection class is split into a new immutable collection class and a corresponding mutable builder class. ''', homepage: 'https://github.com/google/built_collection.dart', authors: [], @@ -1746,17 +1831,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); -/// built_value 8.10.1 +/// built_value 8.12.0 const _built_value = Package( name: 'built_value', - description: '''Value types with builders, Dart classes as enums, and serialization. This library is the runtime dependency. + description: + '''Value types with builders, Dart classes as enums, and serialization. This library is the runtime dependency. ''', - repository: 'https://github.com/google/built_value.dart/tree/master/built_value', + repository: + 'https://github.com/google/built_value.dart/tree/master/built_value', authors: [], - version: '8.10.1', + version: '8.12.0', license: '''Copyright 2015, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -1787,13 +1873,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('built_collection'), PackageRef('collection'), PackageRef('fixnum'), PackageRef('meta')] - ); + dependencies: [ + PackageRef('built_collection'), + PackageRef('collection'), + PackageRef('fixnum'), + PackageRef('meta') + ]); /// characters 1.4.0 const _characters = Package( name: 'characters', - description: 'String replacement with operations that are Unicode/grapheme cluster aware.', + description: + 'String replacement with operations that are Unicode/grapheme cluster aware.', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/characters', authors: [], version: '1.4.0', @@ -1826,13 +1917,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// charset 2.0.1 const _charset = Package( name: 'charset', - description: 'Charset encoding and decoding Library, include iso-(2-15), windows series, gbk, euc-jp, euc-kr, shift-jis. And supportted charset detect, canEncode, canDecode.', + description: + 'Charset encoding and decoding Library, include iso-(2-15), windows series, gbk, euc-jp, euc-kr, shift-jis. And supportted charset detect, canEncode, canDecode.', repository: 'https://github.com/shirne/charset-dart', authors: [], version: '2.0.1', @@ -2039,14 +2130,15 @@ const _charset = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// checked_yaml 2.0.4 const _checked_yaml = Package( name: 'checked_yaml', - description: 'Generate more helpful exceptions when decoding YAML documents using package:json_serializable and package:yaml.', - repository: 'https://github.com/google/json_serializable.dart/tree/master/checked_yaml', + description: + 'Generate more helpful exceptions when decoding YAML documents using package:json_serializable and package:yaml.', + repository: + 'https://github.com/google/json_serializable.dart/tree/master/checked_yaml', authors: [], version: '2.0.4', license: '''Copyright 2019, the Dart project authors. All rights reserved. @@ -2077,55 +2169,21 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('json_annotation'), PackageRef('source_span'), PackageRef('yaml')] - ); - -/// cli_util 0.4.2 -const _cli_util = Package( - name: 'cli_util', - description: 'A library to help in building Dart command-line apps.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/cli_util', - authors: [], - version: '0.4.2', - license: '''Copyright 2015, the Dart project authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('meta'), PackageRef('path')] - ); + dependencies: [ + PackageRef('json_annotation'), + PackageRef('source_span'), + PackageRef('yaml') + ]); -/// clock 1.1.2 -const _clock = Package( - name: 'clock', - description: 'A fakeable wrapper for dart:core clock APIs.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/clock', +/// ci 0.1.0 +const _ci = Package( + name: 'ci', + description: + "Detect whether you're running in a CI environment and information about the CI vendor.", + repository: + 'https://github.com/invertase/dart-cli-utilities/tree/main/packages/ci', authors: [], - version: '1.1.2', + version: '0.1.0', license: '''Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2314,7 +2372,7 @@ const _clock = Package( same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2021 Invertase Limited Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -2329,17 +2387,16 @@ const _clock = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); -/// code_builder 4.10.1 -const _code_builder = Package( - name: 'code_builder', - description: 'A fluent, builder-based library for generating valid Dart code.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/code_builder', +/// cli_util 0.4.2 +const _cli_util = Package( + name: 'cli_util', + description: 'A library to help in building Dart command-line apps.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/cli_util', authors: [], - version: '4.10.1', - license: '''Copyright 2016, the Dart project authors. + version: '0.4.2', + license: '''Copyright 2015, the Dart project authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -2368,19 +2425,277 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('built_collection'), PackageRef('built_value'), PackageRef('collection'), PackageRef('matcher'), PackageRef('meta')] - ); + dependencies: [PackageRef('meta'), PackageRef('path')]); -/// collection 1.19.1 -const _collection = Package( - name: 'collection', - description: 'Collections and utilities functions and classes related to collections.', - repository: 'https://github.com/dart-lang/core/tree/main/pkgs/collection', +/// clock 1.1.2 +const _clock = Package( + name: 'clock', + description: 'A fakeable wrapper for dart:core clock APIs.', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/clock', authors: [], - version: '1.19.1', - license: '''Copyright 2015, the Dart project authors. + version: '1.1.2', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -Redistribution and use in source and binary forms, with or without + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + dependencies: []); + +/// code_builder 4.11.0 +const _code_builder = Package( + name: 'code_builder', + description: + 'A fluent, builder-based library for generating valid Dart code.', + repository: + 'https://github.com/dart-lang/tools/tree/main/pkgs/code_builder', + authors: [], + version: '4.11.0', + license: '''Copyright 2016, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + dependencies: [ + PackageRef('built_collection'), + PackageRef('built_value'), + PackageRef('collection'), + PackageRef('matcher'), + PackageRef('meta') + ]); + +/// collection 1.19.1 +const _collection = Package( + name: 'collection', + description: + 'Collections and utilities functions and classes related to collections.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/collection', + authors: [], + version: '1.19.1', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -2407,13 +2722,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// console 4.1.0 const _console = Package( name: 'console', - description: 'A library for common features required by console applications, including color formatting, keyboard input, and progress bars.', + description: + 'A library for common features required by console applications, including color formatting, keyboard input, and progress bars.', homepage: 'https://github.com/DirectMyFile/console.dart', authors: ['Kenneth Endfinger '], version: '4.1.0', @@ -2442,13 +2757,13 @@ SOFTWARE. ```''', isMarkdown: true, isSdk: false, - dependencies: [PackageRef('vector_math')] - ); + dependencies: [PackageRef('vector_math')]); /// convert 3.1.2 const _convert = Package( name: 'convert', - description: 'Utilities for converting between data representations. Provides a number of Sink, Codec, Decoder, and Encoder types.', + description: + 'Utilities for converting between data representations. Provides a number of Sink, Codec, Decoder, and Encoder types.', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/convert', authors: [], version: '3.1.2', @@ -2468,105 +2783,1056 @@ met: contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('typed_data')] - ); +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + dependencies: [PackageRef('typed_data')]); + +/// cross_file 0.3.4+2 +const _cross_file = Package( + name: 'cross_file', + description: + 'An abstraction to allow working with files across multiple platforms.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/cross_file', + authors: [], + version: '0.3.4+2', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + dependencies: [PackageRef('meta'), PackageRef('web')]); + +/// crypto 3.0.6 +const _crypto = Package( + name: 'crypto', + description: + 'Implementations of SHA, MD5, and HMAC cryptographic functions.', + repository: 'https://github.com/dart-lang/core/tree/main/pkgs/crypto', + authors: [], + version: '3.0.6', + license: '''Copyright 2015, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + dependencies: [PackageRef('typed_data')]); + +/// cryptography 2.7.0 +const _cryptography = Package( + name: 'cryptography', + description: + 'Cryptographic algorithms for encryption, digital signatures, key agreement, authentication, and hashing. AES, Chacha20, ED25519, X25519, Argon2, and more. Good cross-platform support.', + homepage: 'https://github.com/dint-dev/cryptography', + authors: [], + version: '2.7.0', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS''', + isMarkdown: false, + isSdk: false, + dependencies: [ + PackageRef('collection'), + PackageRef('crypto'), + PackageRef('ffi'), + PackageRef('js'), + PackageRef('meta'), + PackageRef('typed_data') + ]); + +/// csslib 1.0.2 +const _csslib = Package( + name: 'csslib', + description: + 'A library for parsing and analyzing CSS (Cascading Style Sheets).', + repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/csslib', + authors: [], + version: '1.0.2', + license: '''Copyright 2013, the Dart project authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + isMarkdown: false, + isSdk: false, + dependencies: [PackageRef('source_span')]); + +/// cupertino_icons 1.0.8 +const _cupertino_icons = Package( + name: 'cupertino_icons', + description: + 'Default icons asset for Cupertino widgets based on Apple styled icons', + repository: + 'https://github.com/flutter/packages/tree/main/third_party/packages/cupertino_icons', + authors: [], + version: '1.0.8', + license: '''The MIT License (MIT) + +Copyright (c) 2016 Vladimir Kharlampidi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', + isMarkdown: false, + isSdk: false, + dependencies: []); + +/// custom_lint 0.8.1 +const _custom_lint = Package( + name: 'custom_lint', + description: + 'Lint rules are a powerful way to improve the maintainability of a project. Custom Lint allows package authors and developers to easily write custom lint rules.', + repository: 'https://github.com/invertase/dart_custom_lint', + authors: [], + version: '0.8.1', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Invertase Limited + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + dependencies: [ + PackageRef('analyzer'), + PackageRef('analyzer_plugin'), + PackageRef('args'), + PackageRef('async'), + PackageRef('ci'), + PackageRef('cli_util'), + PackageRef('collection'), + PackageRef('custom_lint_core'), + PackageRef('freezed_annotation'), + PackageRef('json_annotation'), + PackageRef('meta'), + PackageRef('package_config'), + PackageRef('path'), + PackageRef('pub_semver'), + PackageRef('pubspec_parse'), + PackageRef('rxdart'), + PackageRef('uuid'), + PackageRef('yaml') + ]); + +/// custom_lint_builder 0.8.1 +const _custom_lint_builder = Package( + name: 'custom_lint_builder', + description: 'A package to help writing custom linters', + repository: 'https://github.com/invertase/dart_custom_lint', + authors: [], + version: '0.8.1', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Invertase Limited + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + dependencies: [ + PackageRef('analyzer'), + PackageRef('analyzer_plugin'), + PackageRef('collection'), + PackageRef('custom_lint'), + PackageRef('custom_lint_core'), + PackageRef('custom_lint_visitor'), + PackageRef('glob'), + PackageRef('hotreloader'), + PackageRef('meta'), + PackageRef('package_config'), + PackageRef('path'), + PackageRef('pubspec_parse'), + PackageRef('rxdart') + ]); + +/// custom_lint_core 0.8.1 +const _custom_lint_core = Package( + name: 'custom_lint_core', + description: 'A package to help writing custom linters', + repository: 'https://github.com/invertase/dart_custom_lint', + authors: [], + version: '0.8.1', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. -/// cross_file 0.3.4+2 -const _cross_file = Package( - name: 'cross_file', - description: 'An abstraction to allow working with files across multiple platforms.', - repository: 'https://github.com/flutter/packages/tree/main/packages/cross_file', - authors: [], - version: '0.3.4+2', - license: '''Copyright 2013 The Flutter Authors. All rights reserved. + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: + END OF TERMS AND CONDITIONS - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. + APPENDIX: How to apply the Apache License to your work. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('meta'), PackageRef('web')] - ); + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. -/// crypto 3.0.6 -const _crypto = Package( - name: 'crypto', - description: 'Implementations of SHA, MD5, and HMAC cryptographic functions.', - repository: 'https://github.com/dart-lang/core/tree/main/pkgs/crypto', - authors: [], - version: '3.0.6', - license: '''Copyright 2015, the Dart project authors. + Copyright 2020 Invertase Limited -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. + http://www.apache.org/licenses/LICENSE-2.0 -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('typed_data')] - ); - -/// cryptography 2.7.0 -const _cryptography = Package( - name: 'cryptography', - description: 'Cryptographic algorithms for encryption, digital signatures, key agreement, authentication, and hashing. AES, Chacha20, ED25519, X25519, Argon2, and more. Good cross-platform support.', - homepage: 'https://github.com/dint-dev/cryptography', - authors: [], - version: '2.7.0', + dependencies: [ + PackageRef('analyzer'), + PackageRef('analyzer_plugin'), + PackageRef('collection'), + PackageRef('custom_lint_visitor'), + PackageRef('glob'), + PackageRef('matcher'), + PackageRef('meta'), + PackageRef('package_config'), + PackageRef('path'), + PackageRef('pubspec_parse'), + PackageRef('source_span'), + PackageRef('uuid'), + PackageRef('yaml') + ]); + +/// custom_lint_visitor 1.0.0+8.1.1 +const _custom_lint_visitor = Package( + name: 'custom_lint_visitor', + description: 'A package that exports visitors for CustomLint.', + repository: 'https://github.com/invertase/dart_custom_lint', + authors: [], + version: '1.0.0+8.1.1', license: '''Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2742,91 +4008,46 @@ const _cryptography = Package( incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - END OF TERMS AND CONDITIONS''', - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('crypto'), PackageRef('ffi'), PackageRef('js'), PackageRef('meta'), PackageRef('typed_data')] - ); - -/// csslib 1.0.2 -const _csslib = Package( - name: 'csslib', - description: 'A library for parsing and analyzing CSS (Cascading Style Sheets).', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/csslib', - authors: [], - version: '1.0.2', - license: '''Copyright 2013, the Dart project authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. + END OF TERMS AND CONDITIONS -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('source_span')] - ); + APPENDIX: How to apply the Apache License to your work. -/// cupertino_icons 1.0.8 -const _cupertino_icons = Package( - name: 'cupertino_icons', - description: 'Default icons asset for Cupertino widgets based on Apple styled icons', - repository: 'https://github.com/flutter/packages/tree/main/third_party/packages/cupertino_icons', - authors: [], - version: '1.0.8', - license: '''The MIT License (MIT) + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. -Copyright (c) 2016 Vladimir Kharlampidi + Copyright 2020 Invertase Limited -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + http://www.apache.org/licenses/LICENSE-2.0 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: [PackageRef('analyzer')]); -/// dart_pubspec_licenses 3.0.4 +/// dart_pubspec_licenses 3.0.8 const _dart_pubspec_licenses = Package( name: 'dart_pubspec_licenses', - description: 'A library to make it easy to extract OSS license information from Dart packages using pubspec.yaml', - homepage: 'https://github.com/espresso3389/flutter_oss_licenses/tree/master/packages/dart_pubspec_licenses', + description: + 'A library to make it easy to extract OSS license information from Dart packages using pubspec.yaml', + homepage: + 'https://github.com/espresso3389/flutter_oss_licenses/tree/master/packages/dart_pubspec_licenses', repository: 'https://github.com/espresso3389/flutter_oss_licenses', authors: [], - version: '3.0.4', + version: '3.0.8', license: '''MIT License Copyright (c) 2019 Takashi Kawasaki @@ -2850,16 +4071,21 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('yaml'), PackageRef('path'), PackageRef('json_annotation')] - ); + dependencies: [ + PackageRef('yaml'), + PackageRef('path'), + PackageRef('json_annotation'), + PackageRef('args') + ]); -/// dart_style 3.1.0 +/// dart_style 3.1.2 const _dart_style = Package( name: 'dart_style', - description: 'Opinionated, automatic Dart source code formatter. Provides an API and a CLI tool.', + description: + 'Opinionated, automatic Dart source code formatter. Provides an API and a CLI tool.', repository: 'https://github.com/dart-lang/dart_style', authors: [], - version: '3.1.0', + version: '3.1.2', license: '''Copyright 2014, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -2889,13 +4115,22 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('args'), PackageRef('collection'), PackageRef('package_config'), PackageRef('path'), PackageRef('pub_semver'), PackageRef('source_span'), PackageRef('yaml')] - ); + dependencies: [ + PackageRef('analyzer'), + PackageRef('args'), + PackageRef('collection'), + PackageRef('package_config'), + PackageRef('path'), + PackageRef('pub_semver'), + PackageRef('source_span'), + PackageRef('yaml') + ]); /// dbus 0.7.11 const _dbus = Package( name: 'dbus', - description: 'A native Dart implementation of the D-Bus message bus client. This package allows Dart applications to directly access services on the Linux desktop.', + description: + 'A native Dart implementation of the D-Bus message bus client. This package allows Dart applications to directly access services on the Linux desktop.', homepage: 'https://github.com/canonical/dbus.dart', authors: [], version: '0.7.11', @@ -3274,16 +4509,22 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice defined by the Mozilla Public License, v. 2.0.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('args'), PackageRef('ffi'), PackageRef('meta'), PackageRef('xml')] - ); + dependencies: [ + PackageRef('args'), + PackageRef('ffi'), + PackageRef('meta'), + PackageRef('xml') + ]); -/// desktop_drop 0.6.0 +/// desktop_drop 0.6.1 const _desktop_drop = Package( name: 'desktop_drop', - description: 'A plugin which allows user dragging files to your flutter desktop applications.', - homepage: 'https://github.com/MixinNetwork/flutter-plugins/tree/main/packages/desktop_drop', + description: + 'A plugin which allows user dragging files to your flutter desktop applications.', + homepage: + 'https://github.com/MixinNetwork/flutter-plugins/tree/main/packages/desktop_drop', authors: [], - version: '0.6.0', + version: '0.6.1', license: '''Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -3487,17 +4728,24 @@ const _desktop_drop = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('cross_file'), PackageRef('web'), PackageRef('universal_platform')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_web_plugins'), + PackageRef('cross_file'), + PackageRef('web'), + PackageRef('universal_platform') + ]); -/// device_info_plus 11.4.0 +/// device_info_plus 12.1.0 const _device_info_plus = Package( name: 'device_info_plus', - description: 'Flutter plugin providing detailed information about the device (make, model, etc.), and Android or iOS version the app is running on.', + description: + 'Flutter plugin providing detailed information about the device (make, model, etc.), and Android or iOS version the app is running on.', homepage: 'https://github.com/fluttercommunity/plus_plugins', - repository: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus/device_info_plus', + repository: + 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus/device_info_plus', authors: [], - version: '11.4.0', + version: '12.1.0', license: '''Copyright 2017 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -3527,17 +4775,27 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('device_info_plus_platform_interface'), PackageRef('ffi'), PackageRef('file'), PackageRef('flutter'), PackageRef('meta'), PackageRef('web'), PackageRef('win32'), PackageRef('win32_registry')] - ); + dependencies: [ + PackageRef('device_info_plus_platform_interface'), + PackageRef('ffi'), + PackageRef('file'), + PackageRef('flutter'), + PackageRef('flutter_web_plugins'), + PackageRef('meta'), + PackageRef('web'), + PackageRef('win32'), + PackageRef('win32_registry') + ]); -/// device_info_plus_platform_interface 7.0.2 +/// device_info_plus_platform_interface 7.0.3 const _device_info_plus_platform_interface = Package( name: 'device_info_plus_platform_interface', description: 'A common platform interface for the device_info_plus plugin.', homepage: 'https://github.com/fluttercommunity/plus_plugins', - repository: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/', + repository: + 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/', authors: [], - version: '7.0.2', + version: '7.0.3', license: '''Copyright 2017 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -3567,10 +4825,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('meta'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('meta'), + PackageRef('plugin_platform_interface') + ]); -/// dio 5.8.0+1 +/// dio 5.9.0 const _dio = Package( name: 'dio', description: '''A powerful HTTP networking package, @@ -3581,7 +4842,7 @@ Custom adapters, Transformers, etc. homepage: 'https://github.com/cfug/dio', repository: 'https://github.com/cfug/dio/blob/main/dio', authors: [], - version: '5.8.0+1', + version: '5.9.0', license: '''MIT License Copyright (c) 2018 Wen Du (wendux) @@ -3606,8 +4867,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('collection'), PackageRef('http_parser'), PackageRef('meta'), PackageRef('path'), PackageRef('dio_web_adapter')] - ); + dependencies: [ + PackageRef('async'), + PackageRef('collection'), + PackageRef('http_parser'), + PackageRef('meta'), + PackageRef('mime'), + PackageRef('path'), + PackageRef('dio_web_adapter') + ]); /// dio_web_adapter 2.1.1 const _dio_web_adapter = Package( @@ -3641,13 +4909,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('dio'), PackageRef('http_parser'), PackageRef('meta'), PackageRef('web')] - ); + dependencies: [ + PackageRef('dio'), + PackageRef('http_parser'), + PackageRef('meta'), + PackageRef('web') + ]); /// disks_desktop 1.0.1 const _disks_desktop = Package( name: 'disks_desktop', - description: 'A Flutter desktop library able to retrieve the installed devices information', + description: + 'A Flutter desktop library able to retrieve the installed devices information', homepage: 'https://www.angelocassano.it', repository: 'https://github.com/AngeloAvv/disks', authors: [], @@ -3676,8 +4949,11 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('equatable'), PackageRef('path')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('equatable'), + PackageRef('path') + ]); /// drives_windows 0.0.1 const _drives_windows = Package( @@ -3710,16 +4986,22 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('ffi'), PackageRef('flutter'), PackageRef('path'), PackageRef('win32')] - ); + dependencies: [ + PackageRef('ffi'), + PackageRef('flutter'), + PackageRef('path'), + PackageRef('win32') + ]); -/// dynamic_color 1.7.0 +/// dynamic_color 1.8.1 const _dynamic_color = Package( name: 'dynamic_color', - description: "A Flutter package to create Material color schemes based on a platform's implementation of dynamic color.", - repository: 'https://github.com/material-foundation/flutter-packages/tree/main/packages/dynamic_color', + description: + "A Flutter package to create Material color schemes based on a platform's implementation of dynamic color.", + repository: + 'https://github.com/material-foundation/flutter-packages/tree/main/packages/dynamic_color', authors: [], - version: '1.7.0', + version: '1.8.1', license: '''Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -3923,13 +5205,16 @@ const _dynamic_color = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('material_color_utilities')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('material_color_utilities') + ]); /// equatable 2.0.7 const _equatable = Package( name: 'equatable', - description: 'A Dart package that helps to implement value based equality without needing to explicitly override == and hashCode.', + description: + 'A Dart package that helps to implement value based equality without needing to explicitly override == and hashCode.', homepage: 'https://github.com/felangel/equatable', repository: 'https://github.com/felangel/equatable', authors: [], @@ -3957,13 +5242,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('meta')] - ); + dependencies: [PackageRef('collection'), PackageRef('meta')]); /// fake_async 1.3.3 const _fake_async = Package( name: 'fake_async', - description: 'Fake asynchronous events such as timers and microtasks for deterministic testing.', + description: + 'Fake asynchronous events such as timers and microtasks for deterministic testing.', repository: 'https://github.com/dart-lang/test/tree/master/pkgs/fake_async', authors: [], version: '1.3.3', @@ -4170,13 +5455,13 @@ const _fake_async = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: [PackageRef('clock'), PackageRef('collection')]); /// ffi 2.1.4 const _ffi = Package( name: 'ffi', - description: 'Utilities for working with Foreign Function Interface (FFI) code.', + description: + 'Utilities for working with Foreign Function Interface (FFI) code.', repository: 'https://github.com/dart-lang/native/tree/main/pkgs/ffi', authors: [], version: '2.1.4', @@ -4209,13 +5494,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// file 7.0.1 const _file = Package( name: 'file', - description: 'A pluggable, mockable file system abstraction for Dart. Supports local file system access, as well as in-memory file systems, record-replay file systems, and chroot file systems.', + description: + 'A pluggable, mockable file system abstraction for Dart. Supports local file system access, as well as in-memory file systems, record-replay file systems, and chroot file systems.', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/file', authors: [], version: '7.0.1', @@ -4247,17 +5532,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta'), PackageRef('path')] - ); + dependencies: [PackageRef('meta'), PackageRef('path')]); -/// file_picker 10.1.9 +/// file_picker 10.3.3 const _file_picker = Package( name: 'file_picker', - description: 'A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.', + description: + 'A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.', homepage: 'https://github.com/miguelpruivo/plugins_flutter_file_picker', repository: 'https://github.com/miguelpruivo/flutter_file_picker', authors: [], - version: '10.1.9', + version: '10.3.3', license: '''MIT License Copyright (c) 2018 Miguel Ruivo @@ -4281,13 +5566,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('flutter_plugin_android_lifecycle'), PackageRef('plugin_platform_interface'), PackageRef('ffi'), PackageRef('path'), PackageRef('win32'), PackageRef('cross_file'), PackageRef('web')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_web_plugins'), + PackageRef('flutter_plugin_android_lifecycle'), + PackageRef('plugin_platform_interface'), + PackageRef('ffi'), + PackageRef('path'), + PackageRef('win32'), + PackageRef('cross_file'), + PackageRef('web'), + PackageRef('dbus') + ]); /// fixnum 1.1.1 const _fixnum = Package( name: 'fixnum', - description: 'Library for 32- and 64-bit signed fixed-width integers with consistent behavior between native and JS runtimes.', + description: + 'Library for 32- and 64-bit signed fixed-width integers with consistent behavior between native and JS runtimes.', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/fixnum', authors: [], version: '1.1.1', @@ -4320,16 +5616,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); -/// flutter 3.32.2 +/// flutter 3.35.3 const _flutter = Package( name: 'flutter', description: 'A framework for writing Flutter applications', homepage: 'https://flutter.dev', authors: [], - version: '3.32.2', + version: '3.35.3', license: '''Copyright 2014 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -4357,8 +5652,13 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: true, - dependencies: [PackageRef('characters'), PackageRef('collection'), PackageRef('material_color_utilities'), PackageRef('meta'), PackageRef('vector_math')] - ); + dependencies: [ + PackageRef('characters'), + PackageRef('collection'), + PackageRef('material_color_utilities'), + PackageRef('meta'), + PackageRef('vector_math') + ]); /// flutter_breadcrumb 1.0.1 const _flutter_breadcrumb = Package( @@ -4399,17 +5699,18 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('pedantic')] - ); + dependencies: [PackageRef('flutter'), PackageRef('pedantic')]); -/// flutter_hooks 0.21.2 +/// flutter_hooks 0.21.3+1 const _flutter_hooks = Package( name: 'flutter_hooks', - description: 'A flutter implementation of React hooks. It adds a new kind of widget with enhanced code reuse.', + description: + 'A flutter implementation of React hooks. It adds a new kind of widget with enhanced code reuse.', homepage: 'https://github.com/rrousselGit/flutter_hooks', - repository: 'https://github.com/rrousselGit/flutter_hooks/tree/master/packages/flutter_hooks', + repository: + 'https://github.com/rrousselGit/flutter_hooks/tree/master/packages/flutter_hooks', authors: [], - version: '0.21.2', + version: '0.21.3+1', license: '''MIT License Copyright (c) 2018 Remi Rousselet @@ -4433,14 +5734,49 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')] - ); + dependencies: [PackageRef('flutter')]); + +/// flutter_hooks_lint 1.4.0 +const _flutter_hooks_lint = Package( + name: 'flutter_hooks_lint', + description: + 'A lint package providing guidelines for using flutter_hooks in your Flutter widget!', + homepage: 'https://github.com/nikaera/flutter_hooks_lint', + repository: 'https://github.com/nikaera/flutter_hooks_lint', + authors: [], + version: '1.4.0', + license: '''MIT License + +Copyright (c) 2024 nikaera + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + isMarkdown: false, + isSdk: false, + dependencies: [PackageRef('analyzer'), PackageRef('custom_lint_builder')]); /// flutter_lints 6.0.0 const _flutter_lints = Package( name: 'flutter_lints', - description: 'Recommended lints for Flutter apps, packages, and plugins to encourage good coding practices.', - repository: 'https://github.com/flutter/packages/tree/main/packages/flutter_lints', + description: + 'Recommended lints for Flutter apps, packages, and plugins to encourage good coding practices.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/flutter_lints', authors: [], version: '6.0.0', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -4470,14 +5806,15 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('lints')] - ); + dependencies: [PackageRef('lints')]); /// flutter_markdown 0.7.7+1 const _flutter_markdown = Package( name: 'flutter_markdown', - description: 'A Markdown renderer for Flutter. Create rich text output, including text styles, tables, links, and more, from plain text data formatted with simple Markdown tags.', - repository: 'https://github.com/flutter/packages/tree/main/packages/flutter_markdown', + description: + 'A Markdown renderer for Flutter. Create rich text output, including text styles, tables, links, and more, from plain text data formatted with simple Markdown tags.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/flutter_markdown', authors: [], version: '0.7.7+1', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -4507,50 +5844,22 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('markdown'), PackageRef('meta'), PackageRef('path')] - ); - -/// flutter_oss_licenses 3.0.4 -const _flutter_oss_licenses = Package( - name: 'flutter_oss_licenses', - description: 'A tool to generate detail and better OSS license list using pubspec.yaml/lock files.', - homepage: 'https://github.com/espresso3389/flutter_oss_licenses/tree/master/packages/flutter_oss_licenses', - repository: 'https://github.com/espresso3389/flutter_oss_licenses', - authors: [], - version: '3.0.4', - license: '''MIT License - -Copyright (c) 2019 Takashi Kawasaki - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.''', - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('path'), PackageRef('meta'), PackageRef('yaml'), PackageRef('dart_pubspec_licenses'), PackageRef('args')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('markdown'), + PackageRef('meta'), + PackageRef('path') + ]); -/// flutter_plugin_android_lifecycle 2.0.28 +/// flutter_plugin_android_lifecycle 2.0.30 const _flutter_plugin_android_lifecycle = Package( name: 'flutter_plugin_android_lifecycle', - description: 'Flutter plugin for accessing an Android Lifecycle within other plugins.', - repository: 'https://github.com/flutter/packages/tree/main/packages/flutter_plugin_android_lifecycle', + description: + 'Flutter plugin for accessing an Android Lifecycle within other plugins.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/flutter_plugin_android_lifecycle', authors: [], - version: '2.0.28', + version: '2.0.30', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -4578,14 +5887,15 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')] - ); + dependencies: [PackageRef('flutter')]); /// flutter_secure_storage 8.1.0 const _flutter_secure_storage = Package( name: 'flutter_secure_storage', - description: 'Flutter Secure Storage provides API to store data in secure storage. Keychain is used in iOS, KeyStore based solution is used in Android.', - repository: 'https://github.com/mogol/flutter_secure_storage/tree/develop/flutter_secure_storage', + description: + 'Flutter Secure Storage provides API to store data in secure storage. Keychain is used in iOS, KeyStore based solution is used in Android.', + repository: + 'https://github.com/mogol/flutter_secure_storage/tree/develop/flutter_secure_storage', authors: [], version: '8.1.0', license: '''BSD 3-Clause License @@ -4619,8 +5929,15 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('flutter_secure_storage_linux'), PackageRef('flutter_secure_storage_macos'), PackageRef('flutter_secure_storage_platform_interface'), PackageRef('flutter_secure_storage_web'), PackageRef('flutter_secure_storage_windows'), PackageRef('meta')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_secure_storage_linux'), + PackageRef('flutter_secure_storage_macos'), + PackageRef('flutter_secure_storage_platform_interface'), + PackageRef('flutter_secure_storage_web'), + PackageRef('flutter_secure_storage_windows'), + PackageRef('meta') + ]); /// flutter_secure_storage_linux 1.2.3 const _flutter_secure_storage_linux = Package( @@ -4660,8 +5977,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('flutter_secure_storage_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_secure_storage_platform_interface') + ]); /// flutter_secure_storage_macos 3.1.3 const _flutter_secure_storage_macos = Package( @@ -4701,13 +6020,16 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('flutter_secure_storage_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_secure_storage_platform_interface') + ]); /// flutter_secure_storage_platform_interface 1.1.2 const _flutter_secure_storage_platform_interface = Package( name: 'flutter_secure_storage_platform_interface', - description: 'A common platform interface for the flutter_secure_storage plugin.', + description: + 'A common platform interface for the flutter_secure_storage plugin.', homepage: 'https://github.com/mogol/flutter_secure_storage', authors: [], version: '1.1.2', @@ -4742,13 +6064,16 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); /// flutter_secure_storage_web 1.2.1 const _flutter_secure_storage_web = Package( name: 'flutter_secure_storage_web', - description: 'Web implementation of flutter_secure_storage. Use flutter_secure_storage for the full flutter package.', + description: + 'Web implementation of flutter_secure_storage. Use flutter_secure_storage for the full flutter package.', repository: 'https://github.com/mogol/flutter_secure_storage', authors: [], version: '1.2.1', @@ -4783,13 +6108,18 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('flutter_secure_storage_platform_interface'), PackageRef('js')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_secure_storage_platform_interface'), + PackageRef('flutter_web_plugins'), + PackageRef('js') + ]); /// flutter_secure_storage_windows 2.1.1 const _flutter_secure_storage_windows = Package( name: 'flutter_secure_storage_windows', - description: 'Windows implementation of flutter_secure_storage. Please use flutter_secure_storage instead of this package.', + description: + 'Windows implementation of flutter_secure_storage. Please use flutter_secure_storage instead of this package.', repository: 'https://github.com/mogol/flutter_secure_storage', authors: [], version: '2.1.1', @@ -4824,13 +6154,16 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('flutter_secure_storage_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_secure_storage_platform_interface') + ]); /// flutter_volume_controller 1.3.3 const _flutter_volume_controller = Package( name: 'flutter_volume_controller', - description: 'A Flutter plugin to control system volume and listen for volume changes on different platforms.', + description: + 'A Flutter plugin to control system volume and listen for volume changes on different platforms.', homepage: 'https://github.com/yosemiteyss/flutter_volume_controller', authors: [], version: '1.3.3', @@ -4856,16 +6189,31 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('flutter_plugin_android_lifecycle')] - ); + isSdk: false, + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_plugin_android_lifecycle') + ]); + +/// flutter_web_plugins null +const _flutter_web_plugins = Package( + name: 'flutter_web_plugins', + description: 'Library to register Flutter Web plugins', + homepage: 'https://flutter.dev', + authors: [], + isMarkdown: false, + isSdk: true, + dependencies: [PackageRef('flutter')]); /// flutter_zustand 0.0.5 const _flutter_zustand = Package( name: 'flutter_zustand', - description: "Brings zustand's bear necessities for state management to Flutter", - homepage: 'https://github.com/josiahsrc/flutter_zustand/tree/main/packages/flutter_zustand', - repository: 'https://github.com/josiahsrc/flutter_zustand/tree/main/packages/flutter_zustand', + description: + "Brings zustand's bear necessities for state management to Flutter", + homepage: + 'https://github.com/josiahsrc/flutter_zustand/tree/main/packages/flutter_zustand', + repository: + 'https://github.com/josiahsrc/flutter_zustand/tree/main/packages/flutter_zustand', authors: [], version: '0.0.5', license: '''MIT License @@ -4891,17 +6239,21 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('provider'), PackageRef('zustand')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('provider'), + PackageRef('zustand') + ]); -/// freezed 3.0.6 +/// freezed 3.2.3 const _freezed = Package( name: 'freezed', - description: '''Code generation for immutable classes that has a simple syntax/API without compromising on the features. + description: + '''Code generation for immutable classes that has a simple syntax/API without compromising on the features. ''', repository: 'https://github.com/rrousselGit/freezed', authors: [], - version: '3.0.6', + version: '3.2.3', license: '''MIT License Copyright (c) 2020 Remi Rousselet @@ -4925,17 +6277,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('build'), PackageRef('build_config'), PackageRef('collection'), PackageRef('meta'), PackageRef('source_gen'), PackageRef('freezed_annotation'), PackageRef('json_annotation')] - ); - -/// freezed_annotation 3.0.0 + dependencies: [ + PackageRef('analyzer'), + PackageRef('build'), + PackageRef('build_config'), + PackageRef('collection'), + PackageRef('meta'), + PackageRef('source_gen'), + PackageRef('freezed_annotation'), + PackageRef('json_annotation'), + PackageRef('dart_style'), + PackageRef('pub_semver') + ]); + +/// freezed_annotation 3.1.0 const _freezed_annotation = Package( name: 'freezed_annotation', - description: '''Annotations for the freezed code-generator. This package does nothing without freezed too. + description: + '''Annotations for the freezed code-generator. This package does nothing without freezed too. ''', repository: 'https://github.com/rrousselGit/freezed', authors: [], - version: '3.0.0', + version: '3.1.0', license: '''MIT License Copyright (c) 2020 Remi Rousselet @@ -4959,14 +6322,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('json_annotation'), PackageRef('meta')] - ); + dependencies: [ + PackageRef('collection'), + PackageRef('json_annotation'), + PackageRef('meta') + ]); /// frontend_server_client 4.0.0 const _frontend_server_client = Package( name: 'frontend_server_client', - description: 'Client code to start and interact with the frontend_server compiler from the Dart SDK.', - repository: 'https://github.com/dart-lang/webdev/tree/master/frontend_server_client', + description: + 'Client code to start and interact with the frontend_server compiler from the Dart SDK.', + repository: + 'https://github.com/dart-lang/webdev/tree/master/frontend_server_client', authors: [], version: '4.0.0', license: '''Copyright 2020, the Dart project authors. @@ -4998,16 +6366,16 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('path')] - ); + dependencies: [PackageRef('async'), PackageRef('path')]); -/// fvp 0.32.1 +/// fvp 0.34.0 const _fvp = Package( name: 'fvp', - description: 'video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg', + description: + 'video_player plugin and backend APIs. Support all desktop/mobile platforms with hardware decoders, optimal renders. Supports most formats via FFmpeg', homepage: 'https://github.com/wang-bin/fvp', authors: [], - version: '0.32.1', + version: '0.34.0', license: '''BSD-3-Clause License Copyright 2022 Wang Bin. All rights reserved. @@ -5037,16 +6405,26 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('ffi'), PackageRef('flutter'), PackageRef('logging'), PackageRef('path'), PackageRef('plugin_platform_interface'), PackageRef('video_player'), PackageRef('video_player_platform_interface'), PackageRef('path_provider'), PackageRef('http')] - ); + dependencies: [ + PackageRef('ffi'), + PackageRef('flutter'), + PackageRef('logging'), + PackageRef('path'), + PackageRef('plugin_platform_interface'), + PackageRef('video_player'), + PackageRef('video_player_platform_interface'), + PackageRef('path_provider'), + PackageRef('http') + ]); -/// get_it 7.7.0 +/// get_it 8.2.0 const _get_it = Package( name: 'get_it', - description: 'Simple direct Service Locator that allows to decouple the interface from a concrete implementation and to access the concrete implementation from everywhere in your App"', - homepage: 'https://github.com/fluttercommunity/get_it', + description: + 'Simple direct Service Locator that allows to decouple the interface from a concrete implementation and to access the concrete implementation from everywhere in your App"', + homepage: 'https://github.com/flutter-it/get_it', authors: [], - version: '7.7.0', + version: '8.2.0', license: '''MIT License Copyright (c) 2018 Thomas Burkhart @@ -5070,8 +6448,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('collection'), PackageRef('meta')] - ); + dependencies: [ + PackageRef('async'), + PackageRef('collection'), + PackageRef('meta') + ]); /// glob 2.1.3 const _glob = Package( @@ -5109,226 +6490,62 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('collection'), PackageRef('file'), PackageRef('path'), PackageRef('string_scanner')] - ); + dependencies: [ + PackageRef('async'), + PackageRef('collection'), + PackageRef('file'), + PackageRef('path'), + PackageRef('string_scanner') + ]); -/// google_fonts 6.2.1 +/// google_fonts 6.3.1 const _google_fonts = Package( name: 'google_fonts', - description: 'A Flutter package to use fonts from fonts.google.com. Supports HTTP fetching, caching, and asset bundling.', - repository: 'https://github.com/material-foundation/flutter-packages/tree/main/packages/google_fonts', + description: + 'A Flutter package to use fonts from fonts.google.com. Supports HTTP fetching, caching, and asset bundling.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/google_fonts', authors: [], - version: '6.2.1', - license: '''Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] + version: '6.3.1', + license: '''Copyright 2013 The Flutter Authors. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: - http://www.apache.org/licenses/LICENSE-2.0 + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License.''', +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('http'), PackageRef('path_provider'), PackageRef('crypto')] - ); + dependencies: [ + PackageRef('crypto'), + PackageRef('flutter'), + PackageRef('http'), + PackageRef('path_provider') + ]); /// graphs 2.3.2 const _graphs = Package( name: 'graphs', - description: 'Graph algorithms that operate on graphs in any representation.', + description: + 'Graph algorithms that operate on graphs in any representation.', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/graphs', authors: [], version: '2.3.2', @@ -5361,8 +6578,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection')] - ); + dependencies: [PackageRef('collection')]); /// gtk 2.1.0 const _gtk = Package( @@ -5747,13 +6963,239 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice defined by the Mozilla Public License, v. 2.0.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('ffi'), PackageRef('flutter'), PackageRef('meta')] - ); + dependencies: [ + PackageRef('ffi'), + PackageRef('flutter'), + PackageRef('meta') + ]); + +/// hotreloader 4.3.0 +const _hotreloader = Package( + name: 'hotreloader', + description: + '''Automatic hot code reloader for Dart projects that monitors the source files of a Dart project for changes and automatically applies them to the running Dart process. +''', + homepage: 'https://github.com/vegardit/dart-hotreloader', + repository: 'https://github.com/vegardit/dart-hotreloader.git', + authors: [], + version: '4.3.0', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.''', + isMarkdown: false, + isSdk: false, + dependencies: [ + PackageRef('collection'), + PackageRef('logging'), + PackageRef('path'), + PackageRef('stream_transform'), + PackageRef('vm_service'), + PackageRef('watcher') + ]); /// html 0.15.6 const _html = Package( name: 'html', - description: 'APIs for parsing and manipulating HTML content outside the browser.', + description: + 'APIs for parsing and manipulating HTML content outside the browser.', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/html', authors: [], version: '0.15.6', @@ -5782,16 +7224,16 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('csslib'), PackageRef('source_span')] - ); + dependencies: [PackageRef('csslib'), PackageRef('source_span')]); -/// http 1.4.0 +/// http 1.5.0 const _http = Package( name: 'http', - description: 'A composable, multi-platform, Future-based API for HTTP requests.', + description: + 'A composable, multi-platform, Future-based API for HTTP requests.', repository: 'https://github.com/dart-lang/http/tree/master/pkgs/http', authors: [], - version: '1.4.0', + version: '1.5.0', license: '''Copyright 2014, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -5821,13 +7263,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('http_parser'), PackageRef('meta'), PackageRef('web')] - ); + dependencies: [ + PackageRef('async'), + PackageRef('http_parser'), + PackageRef('meta'), + PackageRef('web') + ]); /// http_methods 1.1.1 const _http_methods = Package( name: 'http_methods', - description: '''List of all HTTP methods registered with IANA as list of strings, and metadata + description: + '''List of all HTTP methods registered with IANA as list of strings, and metadata such as whether a method idempotent. ''', homepage: 'https://github.com/google/dart-neats/tree/master/http_methods', @@ -6037,14 +7484,15 @@ such as whether a method idempotent. limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// http_multi_server 3.2.2 const _http_multi_server = Package( name: 'http_multi_server', - description: 'A dart:io HttpServer wrapper that handles requests from multiple servers.', - repository: 'https://github.com/dart-lang/http/tree/master/pkgs/http_multi_server', + description: + 'A dart:io HttpServer wrapper that handles requests from multiple servers.', + repository: + 'https://github.com/dart-lang/http/tree/master/pkgs/http_multi_server', authors: [], version: '3.2.2', license: '''Copyright 2014, the Dart project authors. @@ -6076,14 +7524,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async')] - ); + dependencies: [PackageRef('async')]); /// http_parser 4.1.2 const _http_parser = Package( name: 'http_parser', - description: 'A platform-independent package for parsing and serializing HTTP formats.', - repository: 'https://github.com/dart-lang/http/tree/master/pkgs/http_parser', + description: + 'A platform-independent package for parsing and serializing HTTP formats.', + repository: + 'https://github.com/dart-lang/http/tree/master/pkgs/http_parser', authors: [], version: '4.1.2', license: '''Copyright 2014, the Dart project authors. @@ -6115,13 +7564,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('source_span'), PackageRef('string_scanner'), PackageRef('typed_data')] - ); + dependencies: [ + PackageRef('collection'), + PackageRef('source_span'), + PackageRef('string_scanner'), + PackageRef('typed_data') + ]); /// image 4.5.4 const _image = Package( name: 'image', - description: 'Dart Image Library provides server and web apps the ability to load, manipulate, and save images with various image file formats.', + description: + 'Dart Image Library provides server and web apps the ability to load, manipulate, and save images with various image file formats.', homepage: 'https://github.com/brendan-duncan/image', authors: [], version: '4.5.4', @@ -6149,13 +7603,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('archive'), PackageRef('meta'), PackageRef('xml')] - ); + dependencies: [ + PackageRef('archive'), + PackageRef('meta'), + PackageRef('xml') + ]); /// intl 0.20.2 const _intl = Package( name: 'intl', - description: 'Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.', + description: + 'Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.', repository: 'https://github.com/dart-lang/i18n/tree/main/pkgs/intl', authors: [], version: '0.20.2', @@ -6188,13 +7646,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('clock'), PackageRef('meta'), PackageRef('path')] - ); + dependencies: [ + PackageRef('clock'), + PackageRef('meta'), + PackageRef('path') + ]); /// io 1.0.5 const _io = Package( name: 'io', - description: 'Utilities for the Dart VM Runtime including support for ANSI colors, file copying, and standard exit code values.', + description: + 'Utilities for the Dart VM Runtime including support for ANSI colors, file copying, and standard exit code values.', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/io', authors: [], version: '1.0.5', @@ -6227,13 +7689,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta'), PackageRef('path'), PackageRef('string_scanner')] - ); + dependencies: [ + PackageRef('meta'), + PackageRef('path'), + PackageRef('string_scanner') + ]); /// js 0.6.7 const _js = Package( name: 'js', - description: 'Annotations to create static Dart interfaces for JavaScript APIs.', + description: + 'Annotations to create static Dart interfaces for JavaScript APIs.', repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/js', authors: [], version: '0.6.7', @@ -6266,14 +7732,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta')] - ); + dependencies: [PackageRef('meta')]); /// json_annotation 4.9.0 const _json_annotation = Package( name: 'json_annotation', - description: 'Classes and helper functions that support JSON code generation via the `json_serializable` package.', - repository: 'https://github.com/google/json_serializable.dart/tree/master/json_annotation', + description: + 'Classes and helper functions that support JSON code generation via the `json_serializable` package.', + repository: + 'https://github.com/google/json_serializable.dart/tree/master/json_annotation', authors: [], version: '4.9.0', license: '''Copyright 2017, the Dart project authors. All rights reserved. @@ -6304,16 +7771,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta')] - ); + dependencies: [PackageRef('meta')]); -/// json_serializable 6.9.5 +/// json_serializable 6.11.1 const _json_serializable = Package( name: 'json_serializable', - description: 'Automatically generate code for converting to and from JSON by annotating Dart classes.', - repository: 'https://github.com/google/json_serializable.dart/tree/master/json_serializable', + description: + 'Automatically generate code for converting to and from JSON by annotating Dart classes.', + repository: + 'https://github.com/google/json_serializable.dart/tree/master/json_serializable', authors: [], - version: '6.9.5', + version: '6.11.1', license: '''Copyright 2017, the Dart project authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -6342,16 +7810,30 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('async'), PackageRef('build'), PackageRef('build_config'), PackageRef('collection'), PackageRef('dart_style'), PackageRef('json_annotation'), PackageRef('meta'), PackageRef('path'), PackageRef('pub_semver'), PackageRef('pubspec_parse'), PackageRef('source_gen'), PackageRef('source_helper')] - ); - -/// leak_tracker 10.0.9 + dependencies: [ + PackageRef('analyzer'), + PackageRef('async'), + PackageRef('build'), + PackageRef('build_config'), + PackageRef('dart_style'), + PackageRef('json_annotation'), + PackageRef('meta'), + PackageRef('path'), + PackageRef('pub_semver'), + PackageRef('pubspec_parse'), + PackageRef('source_gen'), + PackageRef('source_helper') + ]); + +/// leak_tracker 11.0.2 const _leak_tracker = Package( name: 'leak_tracker', - description: 'A framework for memory leak tracking for Dart and Flutter applications.', - repository: 'https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker', + description: + 'A framework for memory leak tracking for Dart and Flutter applications.', + repository: + 'https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker', authors: [], - version: '10.0.9', + version: '11.0.2', license: '''Copyright 2022, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -6381,16 +7863,22 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: [ + PackageRef('clock'), + PackageRef('collection'), + PackageRef('meta'), + PackageRef('path'), + PackageRef('vm_service') + ]); -/// leak_tracker_flutter_testing 3.0.9 +/// leak_tracker_flutter_testing 3.0.10 const _leak_tracker_flutter_testing = Package( name: 'leak_tracker_flutter_testing', description: 'An internal package to test leak tracking with Flutter.', - repository: 'https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker_flutter_testing', + repository: + 'https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker_flutter_testing', authors: [], - version: '3.0.9', + version: '3.0.10', license: '''Copyright 2022, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -6420,16 +7908,22 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('leak_tracker'), + PackageRef('leak_tracker_testing'), + PackageRef('matcher'), + PackageRef('meta') + ]); -/// leak_tracker_testing 3.0.1 +/// leak_tracker_testing 3.0.2 const _leak_tracker_testing = Package( name: 'leak_tracker_testing', description: 'Leak tracking code intended for usage in tests.', - repository: 'https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker_testing', + repository: + 'https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker_testing', authors: [], - version: '3.0.1', + version: '3.0.2', license: '''Copyright 2022, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -6459,13 +7953,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: [ + PackageRef('leak_tracker'), + PackageRef('matcher'), + PackageRef('meta') + ]); /// lints 6.0.0 const _lints = Package( name: 'lints', - description: """Official Dart lint rules. Defines the 'core' and 'recommended' set of lints suggested by the Dart team. + description: + """Official Dart lint rules. Defines the 'core' and 'recommended' set of lints suggested by the Dart team. """, repository: 'https://github.com/dart-lang/core/tree/main/pkgs/lints', authors: [], @@ -6499,13 +7997,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// logging 1.3.0 const _logging = Package( name: 'logging', - description: 'Provides APIs for debugging and error logging, similar to loggers in other languages, such as the Closure JS Logger and java.util.logging.Logger.', + description: + 'Provides APIs for debugging and error logging, similar to loggers in other languages, such as the Closure JS Logger and java.util.logging.Logger.', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/logging', authors: [], version: '1.3.0', @@ -6538,13 +8036,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// markdown 7.3.0 const _markdown = Package( name: 'markdown', - description: 'A portable Markdown library written in Dart that can parse Markdown into HTML.', + description: + 'A portable Markdown library written in Dart that can parse Markdown into HTML.', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/markdown', authors: [], version: '7.3.0', @@ -6577,13 +8075,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('args'), PackageRef('meta')] - ); + dependencies: [PackageRef('args'), PackageRef('meta')]); /// matcher 0.12.17 const _matcher = Package( name: 'matcher', - description: 'Support for specifying test expectations via an extensible Matcher class. Also includes a number of built-in Matcher implementations for common cases.', + description: + 'Support for specifying test expectations via an extensible Matcher class. Also includes a number of built-in Matcher implementations for common cases.', repository: 'https://github.com/dart-lang/test/tree/master/pkgs/matcher', authors: [], version: '0.12.17', @@ -6616,14 +8114,21 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('meta'), PackageRef('stack_trace'), PackageRef('term_glyph'), PackageRef('test_api')] - ); + dependencies: [ + PackageRef('async'), + PackageRef('meta'), + PackageRef('stack_trace'), + PackageRef('term_glyph'), + PackageRef('test_api') + ]); /// material_color_utilities 0.11.1 const _material_color_utilities = Package( name: 'material_color_utilities', - description: 'Algorithms and utilities that power the Material Design 3 color system, including choosing theme colors from images and creating tones of colors; all in a new color space.', - repository: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart', + description: + 'Algorithms and utilities that power the Material Design 3 color system, including choosing theme colors from images and creating tones of colors; all in a new color space.', + repository: + 'https://github.com/material-foundation/material-color-utilities/tree/main/dart', authors: [], version: '0.11.1', license: '''Apache License @@ -6829,13 +8334,13 @@ const _material_color_utilities = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection')] - ); + dependencies: [PackageRef('collection')]); /// media_kit 1.2.0 const _media_kit = Package( name: 'media_kit', - description: 'A cross-platform video player & audio player for Flutter & Dart. Performant, stable, feature-proof & modular.', + description: + 'A cross-platform video player & audio player for Flutter & Dart. Performant, stable, feature-proof & modular.', homepage: 'https://github.com/media-kit/media-kit', repository: 'https://github.com/media-kit/media-kit', authors: [], @@ -6863,13 +8368,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('http'), PackageRef('image'), PackageRef('meta'), PackageRef('path'), PackageRef('safe_local_storage'), PackageRef('synchronized'), PackageRef('universal_platform'), PackageRef('web'), PackageRef('uri_parser'), PackageRef('uuid')] - ); + dependencies: [ + PackageRef('collection'), + PackageRef('http'), + PackageRef('image'), + PackageRef('meta'), + PackageRef('path'), + PackageRef('safe_local_storage'), + PackageRef('synchronized'), + PackageRef('universal_platform'), + PackageRef('web'), + PackageRef('uri_parser'), + PackageRef('uuid') + ]); /// media_kit_libs_android_video 1.3.7 const _media_kit_libs_android_video = Package( name: 'media_kit_libs_android_video', - description: 'Android package providing video (& audio) native libraries for package:media_kit.', + description: + 'Android package providing video (& audio) native libraries for package:media_kit.', homepage: 'https://github.com/media-kit/media-kit.git', repository: 'https://github.com/media-kit/media-kit.git', authors: [], @@ -6897,13 +8414,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); /// media_kit_libs_ios_video 1.1.4 const _media_kit_libs_ios_video = Package( name: 'media_kit_libs_ios_video', - description: 'iOS package providing video (& audio) native libraries for package:media_kit.', + description: + 'iOS package providing video (& audio) native libraries for package:media_kit.', homepage: 'https://github.com/media-kit/media-kit.git', repository: 'https://github.com/media-kit/media-kit.git', authors: [], @@ -6931,13 +8451,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); /// media_kit_libs_linux 1.2.1 const _media_kit_libs_linux = Package( name: 'media_kit_libs_linux', - description: 'GNU/Linux dependency package for package:media_kit. Necessary for initialization.', + description: + 'GNU/Linux dependency package for package:media_kit. Necessary for initialization.', homepage: 'https://github.com/media-kit/media-kit.git', repository: 'https://github.com/media-kit/media-kit.git', authors: [], @@ -6965,13 +8488,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')] - ); + dependencies: [PackageRef('flutter')]); /// media_kit_libs_macos_video 1.1.4 const _media_kit_libs_macos_video = Package( name: 'media_kit_libs_macos_video', - description: 'macOS package providing video (& audio) native libraries for package:media_kit.', + description: + 'macOS package providing video (& audio) native libraries for package:media_kit.', homepage: 'https://github.com/media-kit/media-kit.git', repository: 'https://github.com/media-kit/media-kit.git', authors: [], @@ -6999,13 +8522,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); /// media_kit_libs_video 1.0.6 const _media_kit_libs_video = Package( name: 'media_kit_libs_video', - description: 'package:media_kit video (& audio) playback native libraries for all platforms.', + description: + 'package:media_kit video (& audio) playback native libraries for all platforms.', homepage: 'https://github.com/media-kit/media-kit.git', repository: 'https://github.com/media-kit/media-kit.git', authors: [], @@ -7033,13 +8559,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('media_kit_libs_android_video'), PackageRef('media_kit_libs_ios_video'), PackageRef('media_kit_libs_macos_video'), PackageRef('media_kit_libs_windows_video'), PackageRef('media_kit_libs_linux')] - ); + dependencies: [ + PackageRef('media_kit_libs_android_video'), + PackageRef('media_kit_libs_ios_video'), + PackageRef('media_kit_libs_macos_video'), + PackageRef('media_kit_libs_windows_video'), + PackageRef('media_kit_libs_linux') + ]); /// media_kit_libs_windows_video 1.0.11 const _media_kit_libs_windows_video = Package( name: 'media_kit_libs_windows_video', - description: 'Windows package providing video (& audio) native libraries for package:media_kit.', + description: + 'Windows package providing video (& audio) native libraries for package:media_kit.', homepage: 'https://github.com/media-kit/media-kit.git', repository: 'https://github.com/media-kit/media-kit.git', authors: [], @@ -7067,13 +8599,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')] - ); + dependencies: [PackageRef('flutter')]); /// media_kit_video 1.3.0 const _media_kit_video = Package( name: 'media_kit_video', - description: 'Native implementation for video playback in package:media_kit.', + description: + 'Native implementation for video playback in package:media_kit.', homepage: 'https://github.com/media-kit/media-kit', repository: 'https://github.com/media-kit/media-kit', authors: [], @@ -7101,8 +8633,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('media_kit'), PackageRef('synchronized'), PackageRef('wakelock_plus'), PackageRef('screen_brightness_android'), PackageRef('screen_brightness_platform_interface'), PackageRef('volume_controller'), PackageRef('universal_platform'), PackageRef('plugin_platform_interface'), PackageRef('web')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('media_kit'), + PackageRef('synchronized'), + PackageRef('wakelock_plus'), + PackageRef('screen_brightness_android'), + PackageRef('screen_brightness_platform_interface'), + PackageRef('volume_controller'), + PackageRef('universal_platform'), + PackageRef('plugin_platform_interface'), + PackageRef('web') + ]); /// media_stream 0.0.1 const _media_stream = Package( @@ -7135,13 +8677,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('pure_ftp'), PackageRef('smb_connect'), PackageRef('shelf'), PackageRef('shelf_router')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('pure_ftp'), + PackageRef('smb_connect'), + PackageRef('shelf'), + PackageRef('shelf_router') + ]); /// meta 1.16.0 const _meta = Package( name: 'meta', - description: "Annotations used to express developer intentions that can't otherwise be deduced by statically analyzing source code.", + description: + "Annotations used to express developer intentions that can't otherwise be deduced by statically analyzing source code.", repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/meta', authors: [], version: '1.16.0', @@ -7174,13 +8722,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// mime 2.0.0 const _mime = Package( name: 'mime', - description: 'Utilities for handling media (MIME) types, including determining a type from a file extension and file contents.', + description: + 'Utilities for handling media (MIME) types, including determining a type from a file extension and file contents.', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/mime', authors: [], version: '2.0.0', @@ -7213,16 +8761,16 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); -/// msix 3.16.9 +/// msix 3.16.12 const _msix = Package( name: 'msix', - description: 'A command-line tool that create Msix installer from your flutter windows-build files.', + description: + 'A command-line tool that create Msix installer from your flutter windows-build files.', homepage: 'https://github.com/YehudaKremer/msix', authors: [], - version: '3.16.9', + version: '3.16.12', license: '''MIT License Copyright (c) 2022 Yehuda Kremer @@ -7246,13 +8794,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('args'), PackageRef('yaml'), PackageRef('path'), PackageRef('package_config'), PackageRef('get_it'), PackageRef('image'), PackageRef('pub_semver'), PackageRef('console'), PackageRef('cli_util')] - ); + dependencies: [ + PackageRef('args'), + PackageRef('yaml'), + PackageRef('path'), + PackageRef('package_config'), + PackageRef('get_it'), + PackageRef('image'), + PackageRef('pub_semver'), + PackageRef('console'), + PackageRef('cli_util') + ]); /// mutex 3.1.0 const _mutex = Package( name: 'mutex', - description: 'Mutual exclusion with implementation of normal and read-write mutex', + description: + 'Mutual exclusion with implementation of normal and read-write mutex', homepage: 'https://github.com/hoylen/dart-mutex', authors: [], version: '3.1.0', @@ -7282,13 +8840,13 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// nested 1.0.0 const _nested = Package( name: 'nested', - description: 'A Flutter Widget which helps nest multiple widgets without needing to manually nest them.', + description: + 'A Flutter Widget which helps nest multiple widgets without needing to manually nest them.', repository: 'https://github.com/rrousselGit/nested', authors: [], version: '1.0.0', @@ -7315,14 +8873,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')] - ); + dependencies: [PackageRef('flutter')]); /// package_config 2.2.0 const _package_config = Package( name: 'package_config', - description: 'Support for reading and writing Dart Package Configuration files.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/package_config', + description: + 'Support for reading and writing Dart Package Configuration files.', + repository: + 'https://github.com/dart-lang/tools/tree/main/pkgs/package_config', authors: [], version: '2.2.0', license: '''Copyright 2019, the Dart project authors. @@ -7354,17 +8913,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('path')] - ); + dependencies: [PackageRef('path')]); -/// package_info_plus 8.3.0 +/// package_info_plus 9.0.0 const _package_info_plus = Package( name: 'package_info_plus', - description: 'Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android.', + description: + 'Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android.', homepage: 'https://github.com/fluttercommunity/plus_plugins', - repository: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus/package_info_plus', + repository: + 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus/package_info_plus', authors: [], - version: '8.3.0', + version: '9.0.0', license: '''Copyright 2017 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -7394,17 +8954,29 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('ffi'), PackageRef('flutter'), PackageRef('http'), PackageRef('meta'), PackageRef('path'), PackageRef('package_info_plus_platform_interface'), PackageRef('web'), PackageRef('win32'), PackageRef('clock')] - ); - -/// package_info_plus_platform_interface 3.2.0 + dependencies: [ + PackageRef('ffi'), + PackageRef('flutter'), + PackageRef('flutter_web_plugins'), + PackageRef('http'), + PackageRef('meta'), + PackageRef('path'), + PackageRef('package_info_plus_platform_interface'), + PackageRef('web'), + PackageRef('win32'), + PackageRef('clock') + ]); + +/// package_info_plus_platform_interface 3.2.1 const _package_info_plus_platform_interface = Package( name: 'package_info_plus_platform_interface', - description: 'A common platform interface for the package_info_plus plugin.', + description: + 'A common platform interface for the package_info_plus plugin.', homepage: 'https://github.com/fluttercommunity/plus_plugins', - repository: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/', + repository: + 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/', authors: [], - version: '3.2.0', + version: '3.2.1', license: '''Copyright 2017 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -7434,13 +9006,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('meta'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('meta'), + PackageRef('plugin_platform_interface') + ]); /// path 1.9.1 const _path = Package( name: 'path', - description: 'A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web.', + description: + 'A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web.', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/path', authors: [], version: '1.9.1', @@ -7473,14 +9049,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// path_provider 2.1.5 const _path_provider = Package( name: 'path_provider', - description: 'Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories.', - repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider', + description: + 'Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider', authors: [], version: '2.1.5', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -7510,16 +9087,23 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('path_provider_android'), PackageRef('path_provider_foundation'), PackageRef('path_provider_linux'), PackageRef('path_provider_platform_interface'), PackageRef('path_provider_windows')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('path_provider_android'), + PackageRef('path_provider_foundation'), + PackageRef('path_provider_linux'), + PackageRef('path_provider_platform_interface'), + PackageRef('path_provider_windows') + ]); -/// path_provider_android 2.2.17 +/// path_provider_android 2.2.18 const _path_provider_android = Package( name: 'path_provider_android', description: 'Android implementation of the path_provider plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_android', + repository: + 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_android', authors: [], - version: '2.2.17', + version: '2.2.18', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -7547,16 +9131,19 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('path_provider_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('path_provider_platform_interface') + ]); -/// path_provider_foundation 2.4.1 +/// path_provider_foundation 2.4.2 const _path_provider_foundation = Package( name: 'path_provider_foundation', description: 'iOS and macOS implementation of the path_provider plugin', - repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_foundation', + repository: + 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_foundation', authors: [], - version: '2.4.1', + version: '2.4.2', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -7584,14 +9171,17 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('path_provider_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('path_provider_platform_interface') + ]); /// path_provider_linux 2.2.1 const _path_provider_linux = Package( name: 'path_provider_linux', description: 'Linux implementation of the path_provider plugin', - repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_linux', + repository: + 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_linux', authors: [], version: '2.2.1', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -7621,14 +9211,20 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('ffi'), PackageRef('flutter'), PackageRef('path'), PackageRef('path_provider_platform_interface'), PackageRef('xdg_directories')] - ); + dependencies: [ + PackageRef('ffi'), + PackageRef('flutter'), + PackageRef('path'), + PackageRef('path_provider_platform_interface'), + PackageRef('xdg_directories') + ]); /// path_provider_platform_interface 2.1.2 const _path_provider_platform_interface = Package( name: 'path_provider_platform_interface', description: 'A common platform interface for the path_provider plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_platform_interface', + repository: + 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_platform_interface', authors: [], version: '2.1.2', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -7658,14 +9254,18 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('platform'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('platform'), + PackageRef('plugin_platform_interface') + ]); /// path_provider_windows 2.3.0 const _path_provider_windows = Package( name: 'path_provider_windows', description: 'Windows implementation of the path_provider plugin', - repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_windows', + repository: + 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_windows', authors: [], version: '2.3.0', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -7695,13 +9295,18 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('ffi'), PackageRef('flutter'), PackageRef('path'), PackageRef('path_provider_platform_interface')] - ); + dependencies: [ + PackageRef('ffi'), + PackageRef('flutter'), + PackageRef('path'), + PackageRef('path_provider_platform_interface') + ]); /// pedantic 1.11.1 const _pedantic = Package( name: 'pedantic', - description: 'The Dart analyzer settings and best practices used internally at Google.', + description: + 'The Dart analyzer settings and best practices used internally at Google.', homepage: 'https://github.com/google/pedantic', authors: [], version: '1.11.1', @@ -7733,16 +9338,16 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); -/// permission_handler 12.0.0+1 +/// permission_handler 12.0.1 const _permission_handler = Package( name: 'permission_handler', - description: 'Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.', + description: + 'Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.', repository: 'https://github.com/baseflow/flutter-permission-handler', authors: [], - version: '12.0.0+1', + version: '12.0.1', license: '''MIT License Copyright (c) 2018 Baseflow @@ -7766,13 +9371,21 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('meta'), PackageRef('permission_handler_android'), PackageRef('permission_handler_apple'), PackageRef('permission_handler_html'), PackageRef('permission_handler_windows'), PackageRef('permission_handler_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('meta'), + PackageRef('permission_handler_android'), + PackageRef('permission_handler_apple'), + PackageRef('permission_handler_html'), + PackageRef('permission_handler_windows'), + PackageRef('permission_handler_platform_interface') + ]); /// permission_handler_android 13.0.1 const _permission_handler_android = Package( name: 'permission_handler_android', - description: 'Permission plugin for Flutter. This plugin provides the Android API to request and check permissions.', + description: + 'Permission plugin for Flutter. This plugin provides the Android API to request and check permissions.', homepage: 'https://github.com/baseflow/flutter-permission-handler', authors: [], version: '13.0.1', @@ -7799,13 +9412,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('permission_handler_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('permission_handler_platform_interface') + ]); /// permission_handler_apple 9.4.7 const _permission_handler_apple = Package( name: 'permission_handler_apple', - description: 'Permission plugin for Flutter. This plugin provides the iOS API to request and check permissions.', + description: + 'Permission plugin for Flutter. This plugin provides the iOS API to request and check permissions.', repository: 'https://github.com/baseflow/flutter-permission-handler', authors: [], version: '9.4.7', @@ -7832,13 +9448,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('permission_handler_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('permission_handler_platform_interface') + ]); /// permission_handler_html 0.1.3+5 const _permission_handler_html = Package( name: 'permission_handler_html', - description: 'Permission plugin for Flutter. This plugin provides the web API to request and check permissions.', + description: + 'Permission plugin for Flutter. This plugin provides the web API to request and check permissions.', homepage: 'https://github.com/baseflow/flutter-permission-handler', authors: [], version: '0.1.3+5', @@ -7865,14 +9484,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('permission_handler_platform_interface'), PackageRef('web')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_web_plugins'), + PackageRef('permission_handler_platform_interface'), + PackageRef('web') + ]); /// permission_handler_platform_interface 4.3.0 const _permission_handler_platform_interface = Package( name: 'permission_handler_platform_interface', - description: 'A common platform interface for the permission_handler plugin.', - homepage: 'https://github.com/baseflow/flutter-permission-handler/tree/master/permission_handler_platform_interface', + description: + 'A common platform interface for the permission_handler plugin.', + homepage: + 'https://github.com/baseflow/flutter-permission-handler/tree/master/permission_handler_platform_interface', authors: [], version: '4.3.0', license: '''MIT License @@ -7898,13 +9523,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('meta'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('meta'), + PackageRef('plugin_platform_interface') + ]); /// permission_handler_windows 0.2.1 const _permission_handler_windows = Package( name: 'permission_handler_windows', - description: 'Permission plugin for Flutter. This plugin provides the Windows API to request and check permissions.', + description: + 'Permission plugin for Flutter. This plugin provides the Windows API to request and check permissions.', homepage: 'https://github.com/baseflow/flutter-permission-handler', authors: [], version: '0.2.1', @@ -7931,20 +9560,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('permission_handler_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('permission_handler_platform_interface') + ]); -/// petitparser 6.1.0 +/// petitparser 7.0.1 const _petitparser = Package( name: 'petitparser', - description: 'A dynamic parser framework to build efficient grammars and parsers quickly.', + description: + 'A dynamic parser framework to build efficient grammars and parsers quickly.', homepage: 'https://petitparser.github.io', repository: 'https://github.com/petitparser/dart-petitparser', authors: [], - version: '6.1.0', + version: '7.0.1', license: '''The MIT License -Copyright (c) 2006-2025 Lukas Renggli. +Copyright (c) 2006-2024 Lukas Renggli. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy @@ -7966,13 +9598,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta'), PackageRef('collection')] - ); + dependencies: [PackageRef('meta'), PackageRef('collection')]); /// platform 3.1.6 const _platform = Package( name: 'platform', - description: 'A pluggable, mockable platform information abstraction for Dart.', + description: + 'A pluggable, mockable platform information abstraction for Dart.', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/platform', authors: [], version: '3.1.6', @@ -8004,14 +9636,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// plugin_platform_interface 2.1.8 const _plugin_platform_interface = Package( name: 'plugin_platform_interface', - description: 'Reusable base class for platform interfaces of Flutter federated plugins, to help enforce best practices.', - repository: 'https://github.com/flutter/packages/tree/main/packages/plugin_platform_interface', + description: + 'Reusable base class for platform interfaces of Flutter federated plugins, to help enforce best practices.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/plugin_platform_interface', authors: [], version: '2.1.8', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -8041,17 +9674,18 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta')] - ); + dependencies: [PackageRef('meta')]); /// pointycastle 3.9.1 const _pointycastle = Package( name: 'pointycastle', - description: 'A Dart library implementing cryptographic algorithms and primitives, modeled on the BouncyCastle library.', + description: + 'A Dart library implementing cryptographic algorithms and primitives, modeled on the BouncyCastle library.', homepage: 'https://github.com/bcgit/pc-dart', authors: [], version: '3.9.1', - license: '''Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + license: + '''Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -8071,13 +9705,17 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('convert'), PackageRef('js')] - ); + dependencies: [ + PackageRef('collection'), + PackageRef('convert'), + PackageRef('js') + ]); /// pool 1.5.1 const _pool = Package( name: 'pool', - description: 'Manage a finite pool of resources. Useful for controlling concurrent file system or network requests.', + description: + 'Manage a finite pool of resources. Useful for controlling concurrent file system or network requests.', repository: 'https://github.com/dart-lang/pool', authors: [], version: '1.5.1', @@ -8110,13 +9748,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('stack_trace')] - ); + dependencies: [PackageRef('async'), PackageRef('stack_trace')]); /// popover 0.3.1 const _popover = Package( name: 'popover', - description: 'A popover is a transient view that appears above other content onscreen when you tap a control or in an area.', + description: + 'A popover is a transient view that appears above other content onscreen when you tap a control or in an area.', homepage: 'https://github.com/minikin/popover', authors: [], version: '0.3.1', @@ -8143,16 +9781,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')] - ); + dependencies: [PackageRef('flutter')]); -/// posix 6.0.2 +/// posix 6.0.3 const _posix = Package( name: 'posix', description: 'Exposes the POSIX api on OSx and Linux', homepage: 'https://github.com/onepub-dev/dart_posix', authors: [], - version: '6.0.2', + version: '6.0.3', license: '''MIT License Copyright (c) 2020 Brett Sutton @@ -8176,16 +9813,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('ffi'), PackageRef('meta'), PackageRef('path')] - ); + dependencies: [PackageRef('ffi'), PackageRef('meta'), PackageRef('path')]); -/// provider 6.1.5 +/// provider 6.1.5+1 const _provider = Package( name: 'provider', - description: 'A wrapper around InheritedWidget to make them easier to use and more reusable.', + description: + 'A wrapper around InheritedWidget to make them easier to use and more reusable.', repository: 'https://github.com/rrousselGit/provider', authors: [], - version: '6.1.5', + version: '6.1.5+1', license: '''MIT License Copyright (c) 2019 Remi Rousselet @@ -8209,13 +9846,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('flutter'), PackageRef('nested')] - ); + dependencies: [ + PackageRef('collection'), + PackageRef('flutter'), + PackageRef('nested') + ]); /// pub_semver 2.2.0 const _pub_semver = Package( name: 'pub_semver', - description: "Versions and version constraints implementing pub's versioning policy. This is very similar to vanilla semver, with a few corner cases.", + description: + "Versions and version constraints implementing pub's versioning policy. This is very similar to vanilla semver, with a few corner cases.", repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/pub_semver', authors: [], version: '2.2.0', @@ -8248,14 +9889,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection')] - ); + dependencies: [PackageRef('collection')]); /// pubspec_parse 1.5.0 const _pubspec_parse = Package( name: 'pubspec_parse', - description: 'Simple package for parsing pubspec.yaml files with a type-safe API and rich error reporting.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/pubspec_parse', + description: + 'Simple package for parsing pubspec.yaml files with a type-safe API and rich error reporting.', + repository: + 'https://github.com/dart-lang/tools/tree/main/pkgs/pubspec_parse', authors: [], version: '1.5.0', license: '''Copyright 2018, the Dart project authors. @@ -8287,13 +9929,19 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('checked_yaml'), PackageRef('collection'), PackageRef('json_annotation'), PackageRef('pub_semver'), PackageRef('yaml')] - ); + dependencies: [ + PackageRef('checked_yaml'), + PackageRef('collection'), + PackageRef('json_annotation'), + PackageRef('pub_semver'), + PackageRef('yaml') + ]); /// pure_ftp 0.7.5 const _pure_ftp = Package( name: 'pure_ftp', - description: 'Simple and powerful FTP client for Dart. Allow you to connect to FTP server and perform basic operations. now supports all native platforms(Web in progress)', + description: + 'Simple and powerful FTP client for Dart. Allow you to connect to FTP server and perform basic operations. now supports all native platforms(Web in progress)', homepage: 'https://github.com/crifurch/pure_ftp', authors: [], version: '0.7.5', @@ -8320,16 +9968,229 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta')] - ); + dependencies: [PackageRef('meta')]); -/// saf_util 0.10.0 +/// rxdart 0.28.0 +const _rxdart = Package( + name: 'rxdart', + description: + '''RxDart is an implementation of the popular ReactiveX api for asynchronous programming, leveraging the native Dart Streams api. +''', + repository: 'https://github.com/ReactiveX/rxdart', + authors: [], + version: '0.28.0', + license: '''Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.''', + isMarkdown: false, + isSdk: false, + dependencies: []); + +/// saf_util 0.11.0 const _saf_util = Package( name: 'saf_util', description: 'Util functions for SAF (Storage Access Framework).', homepage: 'https://github.com/flutter-cavalry/saf_util', authors: [], - version: '0.10.0', + version: '0.11.0', license: '''BSD 3-Clause License Copyright (c) 2023, Mgenware (Liu YuanYuan) @@ -8361,13 +10222,16 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); /// safe_local_storage 2.0.1 const _safe_local_storage = Package( name: 'safe_local_storage', - description: 'A safe caching library to read/write values on local storage.', + description: + 'A safe caching library to read/write values on local storage.', homepage: 'https://github.com/alexmercerind/safe_local_storage.git', repository: 'https://github.com/alexmercerind/safe_local_storage.git', authors: [], @@ -8395,17 +10259,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('path'), PackageRef('synchronized')] - ); + dependencies: [PackageRef('path'), PackageRef('synchronized')]); -/// screen_brightness 2.1.4 +/// screen_brightness 2.1.7 const _screen_brightness = Package( name: 'screen_brightness', - description: 'A Plugin for controlling screen brightness with application life cycle reset implemented', + description: + 'A Plugin for controlling screen brightness with application life cycle reset implemented', homepage: 'https://github.com/aaassseee/screen_brightness', - repository: 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness', + repository: + 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness', authors: [], - version: '2.1.4', + version: '2.1.7', license: '''MIT License Copyright (c) 2021 Jack Liu @@ -8429,17 +10294,26 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_brightness_platform_interface'), PackageRef('screen_brightness_android'), PackageRef('screen_brightness_ios'), PackageRef('screen_brightness_macos'), PackageRef('screen_brightness_windows'), PackageRef('screen_brightness_ohos')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_brightness_platform_interface'), + PackageRef('screen_brightness_android'), + PackageRef('screen_brightness_ios'), + PackageRef('screen_brightness_macos'), + PackageRef('screen_brightness_windows'), + PackageRef('screen_brightness_ohos') + ]); -/// screen_brightness_android 2.1.1 +/// screen_brightness_android 2.1.3 const _screen_brightness_android = Package( name: 'screen_brightness_android', - description: 'The Android federated plugin implementation of screen_brightness.', + description: + 'The Android federated plugin implementation of screen_brightness.', homepage: 'https://github.com/aaassseee/screen_brightness', - repository: 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_android', + repository: + 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_android', authors: [], - version: '2.1.1', + version: '2.1.3', license: '''MIT License Copyright (c) 2021 Jack Liu @@ -8463,15 +10337,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_brightness_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_brightness_platform_interface') + ]); /// screen_brightness_ios 2.1.2 const _screen_brightness_ios = Package( name: 'screen_brightness_ios', - description: 'The iOS federated plugin implementation of the screen_brightness.', + description: + 'The iOS federated plugin implementation of the screen_brightness.', homepage: 'https://github.com/aaassseee/screen_brightness', - repository: 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_ios', + repository: + 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_ios', authors: [], version: '2.1.2', license: '''MIT License @@ -8497,15 +10375,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_brightness_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_brightness_platform_interface') + ]); /// screen_brightness_macos 2.1.1 const _screen_brightness_macos = Package( name: 'screen_brightness_macos', - description: 'The macOS federated plugin implementation of the screen_brightness.', + description: + 'The macOS federated plugin implementation of the screen_brightness.', homepage: 'https://github.com/aaassseee/screen_brightness', - repository: 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_macos', + repository: + 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_macos', authors: [], version: '2.1.1', license: '''MIT License @@ -8531,17 +10413,21 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_brightness_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_brightness_platform_interface') + ]); -/// screen_brightness_ohos 2.1.0 +/// screen_brightness_ohos 2.1.2 const _screen_brightness_ohos = Package( name: 'screen_brightness_ohos', - description: 'The ohos federated plugin implementation of screen_brightness.', + description: + 'The ohos federated plugin implementation of screen_brightness.', homepage: 'https://github.com/aaassseee/screen_brightness', - repository: 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_ohos', + repository: + 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_ohos', authors: [], - version: '2.1.0', + version: '2.1.2', license: '''MIT License Copyright (c) 2025 ErBWs, Jack Liu @@ -8565,15 +10451,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_brightness_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_brightness_platform_interface') + ]); /// screen_brightness_platform_interface 2.1.0 const _screen_brightness_platform_interface = Package( name: 'screen_brightness_platform_interface', description: 'A common platform interface for the screen_brightness plugin', homepage: 'https://github.com/aaassseee/screen_brightness', - repository: 'https://github.com/aaassseee/screen_brightness/tree/patch-1/screen_brightness_platform_interface', + repository: + 'https://github.com/aaassseee/screen_brightness/tree/patch-1/screen_brightness_platform_interface', authors: [], version: '2.1.0', license: '''MIT License @@ -8599,15 +10488,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); /// screen_brightness_windows 2.1.0 const _screen_brightness_windows = Package( name: 'screen_brightness_windows', - description: 'The Windows federated plugin implementation of the screen_brightness.', + description: + 'The Windows federated plugin implementation of the screen_brightness.', homepage: 'https://github.com/aaassseee/screen_brightness', - repository: 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_windows', + repository: + 'https://github.com/aaassseee/screen_brightness/tree/master/screen_brightness_windows', authors: [], version: '2.1.0', license: '''MIT License @@ -8633,13 +10526,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_brightness_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_brightness_platform_interface') + ]); /// screen_retriever 0.2.0 const _screen_retriever = Package( name: 'screen_retriever', - description: 'This plugin allows Flutter desktop apps to Retrieve information about screen size, displays, cursor position, etc.', + description: + 'This plugin allows Flutter desktop apps to Retrieve information about screen size, displays, cursor position, etc.', homepage: 'https://github.com/leanflutter/screen_retriever', authors: [], version: '0.2.0', @@ -8666,14 +10562,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_retriever_linux'), PackageRef('screen_retriever_macos'), PackageRef('screen_retriever_platform_interface'), PackageRef('screen_retriever_windows')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_retriever_linux'), + PackageRef('screen_retriever_macos'), + PackageRef('screen_retriever_platform_interface'), + PackageRef('screen_retriever_windows') + ]); /// screen_retriever_linux 0.2.0 const _screen_retriever_linux = Package( name: 'screen_retriever_linux', description: 'Linux implementation of the screen_retriever plugin.', - repository: 'https://github.com/leanflutter/screen_retriever/tree/main/packages/screen_retriever_linux', + repository: + 'https://github.com/leanflutter/screen_retriever/tree/main/packages/screen_retriever_linux', authors: [], version: '0.2.0', license: '''MIT License @@ -8699,14 +10601,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_retriever_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_retriever_platform_interface') + ]); /// screen_retriever_macos 0.2.0 const _screen_retriever_macos = Package( name: 'screen_retriever_macos', description: 'macOS implementation of the screen_retriever plugin.', - repository: 'https://github.com/leanflutter/screen_retriever/tree/main/packages/screen_retriever_macos', + repository: + 'https://github.com/leanflutter/screen_retriever/tree/main/packages/screen_retriever_macos', authors: [], version: '0.2.0', license: '''MIT License @@ -8732,14 +10637,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_retriever_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_retriever_platform_interface') + ]); /// screen_retriever_platform_interface 0.2.0 const _screen_retriever_platform_interface = Package( name: 'screen_retriever_platform_interface', description: 'A common platform interface for the screen_retriever plugin.', - homepage: 'https://github.com/leanflutter/screen_retriever/blob/main/packages/screen_retriever_platform_interface', + homepage: + 'https://github.com/leanflutter/screen_retriever/blob/main/packages/screen_retriever_platform_interface', authors: [], version: '0.2.0', license: '''MIT License @@ -8765,14 +10673,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('json_annotation'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('json_annotation'), + PackageRef('plugin_platform_interface') + ]); /// screen_retriever_windows 0.2.0 const _screen_retriever_windows = Package( name: 'screen_retriever_windows', description: 'Windows implementation of the screen_retriever plugin.', - repository: 'https://github.com/leanflutter/screen_retriever/tree/main/packages/screen_retriever_windows', + repository: + 'https://github.com/leanflutter/screen_retriever/tree/main/packages/screen_retriever_windows', authors: [], version: '0.2.0', license: '''MIT License @@ -8798,18 +10710,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('screen_retriever_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('screen_retriever_platform_interface') + ]); /// scrollable_positioned_list 0.3.8 const _scrollable_positioned_list = Package( name: 'scrollable_positioned_list', - description: '''A list with helper methods to programmatically scroll to an item. + description: + '''A list with helper methods to programmatically scroll to an item. ''', - homepage: 'https://github.com/google/flutter.widgets/tree/master/packages/scrollable_positioned_list', + homepage: + 'https://github.com/google/flutter.widgets/tree/master/packages/scrollable_positioned_list', authors: [], version: '0.3.8', - license: '''Copyright 2018 the Dart project authors, Inc. All rights reserved. + license: + '''Copyright 2018 the Dart project authors, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -8837,13 +10754,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('collection')] - ); + dependencies: [PackageRef('flutter'), PackageRef('collection')]); /// shelf 1.4.2 const _shelf = Package( name: 'shelf', - description: '''A model for web server middleware that encourages composition and easy reuse. + description: + '''A model for web server middleware that encourages composition and easy reuse. ''', repository: 'https://github.com/dart-lang/shelf/tree/master/pkgs/shelf', authors: [], @@ -8877,15 +10794,23 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('collection'), PackageRef('http_parser'), PackageRef('path'), PackageRef('stack_trace'), PackageRef('stream_channel')] - ); + dependencies: [ + PackageRef('async'), + PackageRef('collection'), + PackageRef('http_parser'), + PackageRef('path'), + PackageRef('stack_trace'), + PackageRef('stream_channel') + ]); /// shelf_router 1.1.4 const _shelf_router = Package( name: 'shelf_router', - description: '''A convenient request router for the shelf web-framework, with support for URL-parameters, nested routers and routers generated from source annotations. + description: + '''A convenient request router for the shelf web-framework, with support for URL-parameters, nested routers and routers generated from source annotations. ''', - repository: 'https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_router', + repository: + 'https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_router', authors: [], version: '1.1.4', license: '''Apache License @@ -9091,14 +11016,19 @@ const _shelf_router = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('http_methods'), PackageRef('meta'), PackageRef('shelf')] - ); + dependencies: [ + PackageRef('http_methods'), + PackageRef('meta'), + PackageRef('shelf') + ]); /// shelf_web_socket 3.0.0 const _shelf_web_socket = Package( name: 'shelf_web_socket', - description: 'A shelf handler that wires up a listener for every connection.', - repository: 'https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_web_socket', + description: + 'A shelf handler that wires up a listener for every connection.', + repository: + 'https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_web_socket', authors: [], version: '3.0.0', license: '''Copyright 2014, the Dart project authors. @@ -9130,13 +11060,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('shelf'), PackageRef('stream_channel'), PackageRef('web_socket_channel')] - ); + dependencies: [ + PackageRef('shelf'), + PackageRef('stream_channel'), + PackageRef('web_socket_channel') + ]); /// smb_connect 0.0.9 const _smb_connect = Package( name: 'smb_connect', - description: 'Native SMB/CIFS client library written in Dart for Dart. Extremely fast, can be used for streaming music and video. Supported dialects: SMB 1.0, CIFS, SMB 2.0, SMB 2.1.', + description: + 'Native SMB/CIFS client library written in Dart for Dart. Extremely fast, can be used for streaming music and video. Supported dialects: SMB 1.0, CIFS, SMB 2.0, SMB 2.1.', homepage: 'https://github.com/vadia/smb_connect', repository: 'https://github.com/vadia/smb_connect', authors: [], @@ -9344,16 +11278,24 @@ const _smb_connect = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('asn1lib'), PackageRef('charset'), PackageRef('crypto'), PackageRef('cryptography'), PackageRef('mutex'), PackageRef('pointycastle')] - ); + dependencies: [ + PackageRef('asn1lib'), + PackageRef('charset'), + PackageRef('crypto'), + PackageRef('cryptography'), + PackageRef('mutex'), + PackageRef('pointycastle') + ]); -/// source_gen 2.0.0 +/// source_gen 4.0.1 const _source_gen = Package( name: 'source_gen', - description: 'Source code generation builders and utilities for the Dart build system', - repository: 'https://github.com/dart-lang/source_gen/tree/master/source_gen', + description: + 'Source code generation builders and utilities for the Dart build system', + repository: + 'https://github.com/dart-lang/source_gen/tree/master/source_gen', authors: [], - version: '2.0.0', + version: '4.0.1', license: '''Copyright 2015, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -9383,16 +11325,26 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('async'), PackageRef('build'), PackageRef('dart_style'), PackageRef('glob'), PackageRef('path'), PackageRef('pub_semver'), PackageRef('source_span'), PackageRef('yaml')] - ); + dependencies: [ + PackageRef('analyzer'), + PackageRef('async'), + PackageRef('build'), + PackageRef('dart_style'), + PackageRef('glob'), + PackageRef('path'), + PackageRef('pub_semver'), + PackageRef('source_span'), + PackageRef('yaml') + ]); -/// source_helper 1.3.5 +/// source_helper 1.3.8 const _source_helper = Package( name: 'source_helper', - description: 'Utilities to help with Dart source code generation. Includes utilities for properly generating String literals from any String value.', + description: + 'Utilities to help with Dart source code generation. Includes utilities for properly generating String literals from any String value.', repository: 'https://github.com/google/source_helper.dart', authors: [], - version: '1.3.5', + version: '1.3.8', license: '''Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -9596,13 +11548,13 @@ const _source_helper = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('collection'), PackageRef('source_gen')] - ); + dependencies: [PackageRef('analyzer'), PackageRef('source_gen')]); /// source_span 1.10.1 const _source_span = Package( name: 'source_span', - description: 'Provides a standard representation for source code locations and spans.', + description: + 'Provides a standard representation for source code locations and spans.', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/source_span', authors: [], version: '1.10.1', @@ -9635,13 +11587,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('path'), PackageRef('term_glyph')] - ); + dependencies: [ + PackageRef('collection'), + PackageRef('path'), + PackageRef('term_glyph') + ]); /// sprintf 7.0.0 const _sprintf = Package( name: 'sprintf', - description: 'Dart implementation of sprintf. Provides simple printf like formatting such as sprintf("hello %s", ["world"]);', + description: + 'Dart implementation of sprintf. Provides simple printf like formatting such as sprintf("hello %s", ["world"]);', homepage: 'https://github.com/Naddiseo/dart-sprintf', authors: [], version: '7.0.0', @@ -9669,13 +11625,13 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// stack_trace 1.12.1 const _stack_trace = Package( name: 'stack_trace', - description: 'A package for manipulating stack traces and printing them readably.', + description: + 'A package for manipulating stack traces and printing them readably.', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/stack_trace', authors: [], version: '1.12.1', @@ -9708,14 +11664,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('path')] - ); + dependencies: [PackageRef('path')]); /// stream_channel 2.1.4 const _stream_channel = Package( name: 'stream_channel', - description: 'An abstraction for two-way communication channels based on the Dart Stream class.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/stream_channel', + description: + 'An abstraction for two-way communication channels based on the Dart Stream class.', + repository: + 'https://github.com/dart-lang/tools/tree/main/pkgs/stream_channel', authors: [], version: '2.1.4', license: '''Copyright 2015, the Dart project authors. @@ -9747,14 +11704,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async')] - ); + dependencies: [PackageRef('async')]); /// stream_transform 2.1.1 const _stream_transform = Package( name: 'stream_transform', - description: 'A collection of utilities to transform and manipulate streams.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/stream_transform', + description: + 'A collection of utilities to transform and manipulate streams.', + repository: + 'https://github.com/dart-lang/tools/tree/main/pkgs/stream_transform', authors: [], version: '2.1.1', license: '''Copyright 2017, the Dart project authors. @@ -9786,14 +11744,14 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// string_scanner 1.4.1 const _string_scanner = Package( name: 'string_scanner', description: 'A class for parsing strings using a sequence of patterns.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/string_scanner', + repository: + 'https://github.com/dart-lang/tools/tree/main/pkgs/string_scanner', authors: [], version: '1.4.1', license: '''Copyright 2014, the Dart project authors. @@ -9825,16 +11783,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('source_span')] - ); + dependencies: [PackageRef('source_span')]); -/// synchronized 3.3.1 +/// synchronized 3.4.0 const _synchronized = Package( name: 'synchronized', - description: 'Lock mechanism to prevent concurrent access to asynchronous code.', - homepage: 'https://github.com/tekartik/synchronized.dart/tree/master/synchronized', + description: + 'Lock mechanism to prevent concurrent access to asynchronous code.', + homepage: + 'https://github.com/tekartik/synchronized.dart/tree/master/synchronized', authors: [], - version: '3.3.1', + version: '3.4.0', license: '''MIT License Copyright (c) 2016, Alexandre Roux Tekartik. @@ -9858,8 +11817,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// term_glyph 1.2.2 const _term_glyph = Package( @@ -9897,55 +11855,16 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); -/// test_api 0.7.4 +/// test_api 0.7.6 const _test_api = Package( name: 'test_api', - description: 'The user facing API for structuring Dart tests and checking expectations.', + description: + 'The user facing API for structuring Dart tests and checking expectations.', repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test_api', authors: [], - version: '0.7.4', - license: '''Copyright 2018, the Dart project authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('async'), PackageRef('boolean_selector'), PackageRef('collection'), PackageRef('meta'), PackageRef('source_span'), PackageRef('stack_trace'), PackageRef('stream_channel'), PackageRef('string_scanner'), PackageRef('term_glyph')] - ); - -/// timing 1.0.2 -const _timing = Package( - name: 'timing', - description: 'A simple package for tracking the performance of synchronous and asynchronous actions.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/timing', - authors: [], - version: '1.0.2', + version: '0.7.6', license: '''Copyright 2018, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -9975,13 +11894,23 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('json_annotation')] - ); + dependencies: [ + PackageRef('async'), + PackageRef('boolean_selector'), + PackageRef('collection'), + PackageRef('meta'), + PackageRef('source_span'), + PackageRef('stack_trace'), + PackageRef('stream_channel'), + PackageRef('string_scanner'), + PackageRef('term_glyph') + ]); /// typed_data 1.4.0 const _typed_data = Package( name: 'typed_data', - description: 'Utility functions and classes related to the dart:typed_data library.', + description: + 'Utility functions and classes related to the dart:typed_data library.', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/typed_data', authors: [], version: '1.4.0', @@ -10014,13 +11943,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection')] - ); + dependencies: [PackageRef('collection')]); /// universal_platform 1.1.0 const _universal_platform = Package( name: 'universal_platform', - description: 'Replacement for dart.io.Platform class which works on Web as well as Desktop and Mobile. Allows platform checks in your view/model layer easily.', + description: + 'Replacement for dart.io.Platform class which works on Web as well as Desktop and Mobile. Allows platform checks in your view/model layer easily.', homepage: 'https://github.com/gskinnerTeam/flutter-universal-platform', authors: [], version: '1.1.0', @@ -10035,8 +11964,7 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// uri_parser 3.0.0 const _uri_parser = Package( @@ -10069,16 +11997,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('path'), PackageRef('safe_local_storage')] - ); + dependencies: [PackageRef('path'), PackageRef('safe_local_storage')]); -/// url_launcher 6.3.1 +/// url_launcher 6.3.2 const _url_launcher = Package( name: 'url_launcher', - description: 'Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes.', - repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher', + description: + 'Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher', authors: [], - version: '6.3.1', + version: '6.3.2', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -10106,16 +12035,25 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('url_launcher_android'), PackageRef('url_launcher_ios'), PackageRef('url_launcher_linux'), PackageRef('url_launcher_macos'), PackageRef('url_launcher_platform_interface'), PackageRef('url_launcher_web'), PackageRef('url_launcher_windows')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('url_launcher_android'), + PackageRef('url_launcher_ios'), + PackageRef('url_launcher_linux'), + PackageRef('url_launcher_macos'), + PackageRef('url_launcher_platform_interface'), + PackageRef('url_launcher_web'), + PackageRef('url_launcher_windows') + ]); -/// url_launcher_android 6.3.16 +/// url_launcher_android 6.3.21 const _url_launcher_android = Package( name: 'url_launcher_android', description: 'Android implementation of the url_launcher plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_android', + repository: + 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_android', authors: [], - version: '6.3.16', + version: '6.3.21', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -10143,16 +12081,19 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('url_launcher_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('url_launcher_platform_interface') + ]); -/// url_launcher_ios 6.3.3 +/// url_launcher_ios 6.3.4 const _url_launcher_ios = Package( name: 'url_launcher_ios', description: 'iOS implementation of the url_launcher plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_ios', + repository: + 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_ios', authors: [], - version: '6.3.3', + version: '6.3.4', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -10180,14 +12121,17 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('url_launcher_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('url_launcher_platform_interface') + ]); /// url_launcher_linux 3.2.1 const _url_launcher_linux = Package( name: 'url_launcher_linux', description: 'Linux implementation of the url_launcher plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_linux', + repository: + 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_linux', authors: [], version: '3.2.1', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -10217,16 +12161,19 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('url_launcher_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('url_launcher_platform_interface') + ]); -/// url_launcher_macos 3.2.2 +/// url_launcher_macos 3.2.3 const _url_launcher_macos = Package( name: 'url_launcher_macos', description: 'macOS implementation of the url_launcher plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_macos', + repository: + 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_macos', authors: [], - version: '3.2.2', + version: '3.2.3', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -10254,14 +12201,17 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('url_launcher_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('url_launcher_platform_interface') + ]); /// url_launcher_platform_interface 2.3.2 const _url_launcher_platform_interface = Package( name: 'url_launcher_platform_interface', description: 'A common platform interface for the url_launcher plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_platform_interface', + repository: + 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_platform_interface', authors: [], version: '2.3.2', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -10291,14 +12241,17 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); /// url_launcher_web 2.4.1 const _url_launcher_web = Package( name: 'url_launcher_web', description: 'Web platform implementation of url_launcher', - repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_web', + repository: + 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_web', authors: [], version: '2.4.1', license: '''url_launcher_web @@ -10534,14 +12487,19 @@ platform_detect limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('url_launcher_platform_interface'), PackageRef('web')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_web_plugins'), + PackageRef('url_launcher_platform_interface'), + PackageRef('web') + ]); /// url_launcher_windows 3.1.4 const _url_launcher_windows = Package( name: 'url_launcher_windows', description: 'Windows implementation of the url_launcher plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_windows', + repository: + 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_windows', authors: [], version: '3.1.4', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -10571,13 +12529,16 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('url_launcher_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('url_launcher_platform_interface') + ]); /// uuid 4.5.1 const _uuid = Package( name: 'uuid', - description: '''RFC4122 (v1, v4, v5, v6, v7, v8) UUID Generator and Parser for Dart + description: + '''RFC4122 (v1, v4, v5, v6, v7, v8) UUID Generator and Parser for Dart ''', repository: 'https://github.com/Daegalus/dart-uuid', authors: [], @@ -10591,16 +12552,20 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('crypto'), PackageRef('sprintf'), PackageRef('meta'), PackageRef('fixnum')] - ); + dependencies: [ + PackageRef('crypto'), + PackageRef('sprintf'), + PackageRef('meta'), + PackageRef('fixnum') + ]); -/// vector_math 2.1.4 +/// vector_math 2.2.0 const _vector_math = Package( name: 'vector_math', description: 'A Vector Math library for 2D and 3D applications.', repository: 'https://github.com/google/vector_math.dart', authors: [], - version: '2.1.4', + version: '2.2.0', license: '''Copyright 2015, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10649,14 +12614,15 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// video_player 2.10.0 const _video_player = Package( name: 'video_player', - description: 'Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, macOS and web.', - repository: 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player', + description: + 'Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, macOS and web.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player', authors: [], version: '2.10.0', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -10686,16 +12652,23 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('html'), PackageRef('video_player_android'), PackageRef('video_player_avfoundation'), PackageRef('video_player_platform_interface'), PackageRef('video_player_web')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('html'), + PackageRef('video_player_android'), + PackageRef('video_player_avfoundation'), + PackageRef('video_player_platform_interface'), + PackageRef('video_player_web') + ]); -/// video_player_android 2.8.7 +/// video_player_android 2.8.13 const _video_player_android = Package( name: 'video_player_android', description: 'Android implementation of the video_player plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player_android', + repository: + 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player_android', authors: [], - version: '2.8.7', + version: '2.8.13', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -10723,16 +12696,19 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('video_player_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('video_player_platform_interface') + ]); -/// video_player_avfoundation 2.7.1 +/// video_player_avfoundation 2.8.4 const _video_player_avfoundation = Package( name: 'video_player_avfoundation', description: 'iOS and macOS implementation of the video_player plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation', + repository: + 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation', authors: [], - version: '2.7.1', + version: '2.8.4', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -10760,16 +12736,19 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('video_player_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('video_player_platform_interface') + ]); -/// video_player_platform_interface 6.3.0 +/// video_player_platform_interface 6.4.0 const _video_player_platform_interface = Package( name: 'video_player_platform_interface', description: 'A common platform interface for the video_player plugin.', - repository: 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player_platform_interface', + repository: + 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player_platform_interface', authors: [], - version: '6.3.0', + version: '6.4.0', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -10797,16 +12776,19 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface') + ]); -/// video_player_web 2.3.5 +/// video_player_web 2.4.0 const _video_player_web = Package( name: 'video_player_web', description: 'Web platform implementation of video_player.', - repository: 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web', + repository: + 'https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web', authors: [], - version: '2.3.5', + version: '2.4.0', license: '''Copyright 2013 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -10834,16 +12816,21 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('video_player_platform_interface'), PackageRef('web')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_web_plugins'), + PackageRef('video_player_platform_interface'), + PackageRef('web') + ]); -/// vm_service 15.0.0 +/// vm_service 15.0.2 const _vm_service = Package( name: 'vm_service', - description: 'A library to communicate with a service implementing the Dart VM service protocol.', + description: + 'A library to communicate with a service implementing the Dart VM service protocol.', repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/vm_service', authors: [], - version: '15.0.0', + version: '15.0.2', license: '''Copyright 2015, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -10873,13 +12860,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// volume_controller 3.4.0 const _volume_controller = Package( name: 'volume_controller', - description: 'A Flutter volume plugin for multiple platform to control system volume.', + description: + 'A Flutter volume plugin for multiple platform to control system volume.', homepage: 'https://github.com/kurenai7968/volume_controller', authors: [], version: '3.4.0', @@ -10906,16 +12893,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')] - ); + dependencies: [PackageRef('flutter')]); -/// wakelock_plus 1.3.2 +/// wakelock_plus 1.4.0 const _wakelock_plus = Package( name: 'wakelock_plus', - description: 'Plugin that allows you to keep the device screen awake, i.e. prevent the screen from sleeping on Android, iOS, macOS, Windows, Linux, and web.', - repository: 'https://github.com/fluttercommunity/wakelock_plus/tree/main/wakelock_plus', + description: + 'Plugin that allows you to keep the device screen awake, i.e. prevent the screen from sleeping on Android, iOS, macOS, Windows, Linux, and web.', + repository: + 'https://github.com/fluttercommunity/wakelock_plus/tree/main/wakelock_plus', authors: [], - version: '1.3.2', + version: '1.4.0', license: '''BSD 3-Clause License Copyright (c) 2020-2023, creativecreatorormaybenot @@ -10947,16 +12935,26 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('meta'), PackageRef('wakelock_plus_platform_interface'), PackageRef('win32'), PackageRef('dbus'), PackageRef('package_info_plus'), PackageRef('web')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('flutter_web_plugins'), + PackageRef('meta'), + PackageRef('wakelock_plus_platform_interface'), + PackageRef('win32'), + PackageRef('dbus'), + PackageRef('package_info_plus'), + PackageRef('web') + ]); -/// wakelock_plus_platform_interface 1.2.3 +/// wakelock_plus_platform_interface 1.3.0 const _wakelock_plus_platform_interface = Package( name: 'wakelock_plus_platform_interface', - description: 'A common platform interface for the wakelock_plus plugin used by the different platform implementations.', - repository: 'https://github.com/fluttercommunity/wakelock_plus/tree/main/wakelock_plus_platform_interface', + description: + 'A common platform interface for the wakelock_plus plugin used by the different platform implementations.', + repository: + 'https://github.com/fluttercommunity/wakelock_plus/tree/main/wakelock_plus_platform_interface', authors: [], - version: '1.2.3', + version: '1.3.0', license: '''BSD 3-Clause License Copyright (c) 2020-2023, creativecreatorormaybenot @@ -10988,16 +12986,20 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('plugin_platform_interface'), PackageRef('meta')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('plugin_platform_interface'), + PackageRef('meta') + ]); -/// watcher 1.1.1 +/// watcher 1.1.3 const _watcher = Package( name: 'watcher', - description: 'A file system watcher. It monitors changes to contents of directories and sends notifications when files have been added, removed, or modified.', + description: + 'A file system watcher. It monitors changes to contents of directories and sends notifications when files have been added, removed, or modified.', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/watcher', authors: [], - version: '1.1.1', + version: '1.1.3', license: '''Copyright 2014, the Dart project authors. Redistribution and use in source and binary forms, with or without @@ -11027,8 +13029,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('path')] - ); + dependencies: [PackageRef('async'), PackageRef('path')]); /// web 1.1.1 const _web = Package( @@ -11065,13 +13066,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [] - ); + dependencies: []); /// web_socket 1.0.1 const _web_socket = Package( name: 'web_socket', - description: 'Any easy-to-use library for communicating with WebSockets that has multiple implementations.', + description: + 'Any easy-to-use library for communicating with WebSockets that has multiple implementations.', repository: 'https://github.com/dart-lang/http/tree/master/pkgs/web_socket', authors: [], version: '1.0.1', @@ -11104,14 +13105,15 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('web')] - ); + dependencies: [PackageRef('web')]); /// web_socket_channel 3.0.3 const _web_socket_channel = Package( name: 'web_socket_channel', - description: 'StreamChannel wrappers for WebSockets. Provides a cross-platform WebSocketChannel API, a cross-platform implementation of that API that communicates over an underlying StreamChannel.', - repository: 'https://github.com/dart-lang/http/tree/master/pkgs/web_socket_channel', + description: + 'StreamChannel wrappers for WebSockets. Provides a cross-platform WebSocketChannel API, a cross-platform implementation of that API that communicates over an underlying StreamChannel.', + repository: + 'https://github.com/dart-lang/http/tree/master/pkgs/web_socket_channel', authors: [], version: '3.0.3', license: '''Copyright 2016, the Dart project authors. @@ -11143,8 +13145,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('async'), PackageRef('crypto'), PackageRef('stream_channel'), PackageRef('web'), PackageRef('web_socket')] - ); + dependencies: [ + PackageRef('async'), + PackageRef('crypto'), + PackageRef('stream_channel'), + PackageRef('web'), + PackageRef('web_socket') + ]); /// webdav_client 1.2.2 const _webdav_client = Package( @@ -11184,18 +13191,22 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('dio'), PackageRef('xml'), PackageRef('convert')] - ); + dependencies: [ + PackageRef('dio'), + PackageRef('xml'), + PackageRef('convert') + ]); -/// win32 5.13.0 +/// win32 5.14.0 const _win32 = Package( name: 'win32', - description: '''Access common Win32 APIs directly from Dart using FFI — no C required! + description: + '''Access common Win32 APIs directly from Dart using FFI — no C required! ''', homepage: 'https://win32.pub', repository: 'https://github.com/halildurmus/win32', authors: [], - version: '5.13.0', + version: '5.14.0', license: '''BSD 3-Clause License Copyright (c) 2024, Halil Durmus @@ -11226,13 +13237,13 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('ffi')] - ); + dependencies: [PackageRef('ffi')]); /// win32_registry 2.1.0 const _win32_registry = Package( name: 'win32_registry', - description: 'A package that provides a friendly Dart API for accessing the Windows Registry.', + description: + 'A package that provides a friendly Dart API for accessing the Windows Registry.', repository: 'https://github.com/halildurmus/win32_registry', authors: [], version: '2.1.0', @@ -11266,17 +13277,17 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('ffi'), PackageRef('meta'), PackageRef('win32')] - ); + dependencies: [PackageRef('ffi'), PackageRef('meta'), PackageRef('win32')]); -/// window_manager 0.5.0 +/// window_manager 0.5.1 const _window_manager = Package( name: 'window_manager', - description: 'This plugin allows Flutter desktop apps to resizing and repositioning the window.', + description: + 'This plugin allows Flutter desktop apps to resizing and repositioning the window.', homepage: 'https://leanflutter.dev', repository: 'https://github.com/leanflutter/window_manager', authors: [], - version: '0.5.0', + version: '0.5.1', license: '''MIT License Copyright (c) 2022-present LiJianying @@ -11300,13 +13311,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter'), PackageRef('path'), PackageRef('screen_retriever')] - ); + dependencies: [ + PackageRef('flutter'), + PackageRef('path'), + PackageRef('screen_retriever') + ]); /// window_size 0.1.0 const _window_size = Package( name: 'window_size', - description: 'Allows resizing and repositioning the window containing Flutter.', + description: + 'Allows resizing and repositioning the window containing Flutter.', authors: [], version: '0.1.0', license: '''Apache License @@ -11512,14 +13527,15 @@ const _window_size = Package( limitations under the License.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')] - ); + dependencies: [PackageRef('flutter')]); /// xdg_directories 1.1.0 const _xdg_directories = Package( name: 'xdg_directories', - description: 'A Dart package for reading XDG directory configuration information on Linux.', - repository: 'https://github.com/flutter/packages/tree/main/packages/xdg_directories', + description: + 'A Dart package for reading XDG directory configuration information on Linux.', + repository: + 'https://github.com/flutter/packages/tree/main/packages/xdg_directories', authors: [], version: '1.1.0', license: '''Copyright 2013 The Flutter Authors. All rights reserved. @@ -11549,19 +13565,19 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta'), PackageRef('path')] - ); + dependencies: [PackageRef('meta'), PackageRef('path')]); -/// xml 6.5.0 +/// xml 6.6.1 const _xml = Package( name: 'xml', - description: 'A lightweight library for parsing, traversing, querying, transforming and building XML documents.', + description: + 'A lightweight library for parsing, traversing, querying, transforming and building XML documents.', homepage: 'https://github.com/renggli/dart-xml', authors: [], - version: '6.5.0', + version: '6.6.1', license: '''The MIT License -Copyright (c) 2006-2023 Lukas Renggli. +Copyright (c) 2006-2025 Lukas Renggli. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy @@ -11583,13 +13599,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('meta'), PackageRef('petitparser')] - ); + dependencies: [ + PackageRef('collection'), + PackageRef('meta'), + PackageRef('petitparser') + ]); /// yaml 3.1.3 const _yaml = Package( name: 'yaml', - description: 'A parser for YAML, a human-friendly data serialization standard', + description: + 'A parser for YAML, a human-friendly data serialization standard', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/yaml', authors: [], version: '3.1.3', @@ -11615,15 +13635,21 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('source_span'), PackageRef('string_scanner')] - ); + dependencies: [ + PackageRef('collection'), + PackageRef('source_span'), + PackageRef('string_scanner') + ]); /// zustand 0.0.5 const _zustand = Package( name: 'zustand', - description: "Brings zustand's bear necessities for state management to Dart", - homepage: 'https://github.com/josiahsrc/flutter_zustand/tree/main/packages/zustand', - repository: 'https://github.com/josiahsrc/flutter_zustand/tree/main/packages/zustand', + description: + "Brings zustand's bear necessities for state management to Dart", + homepage: + 'https://github.com/josiahsrc/flutter_zustand/tree/main/packages/zustand', + repository: + 'https://github.com/josiahsrc/flutter_zustand/tree/main/packages/zustand', authors: [], version: '0.0.5', license: '''MIT License @@ -11649,6 +13675,4 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', isMarkdown: false, isSdk: false, - dependencies: [PackageRef('meta')] - ); - + dependencies: [PackageRef('meta')]); diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index 50c2dc5..c6a9d36 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; -import 'package:iris/hooks/use_fvp_player.dart'; -import 'package:iris/hooks/use_media_kit_player.dart'; -import 'package:iris/models/store/app_state.dart'; -import 'package:iris/pages/player/iris_player.dart'; +import 'package:iris/hooks/ui/use_full_screen.dart'; +import 'package:iris/hooks/ui/use_orientation.dart'; +import 'package:iris/hooks/ui/use_resize_window.dart'; +import 'package:iris/pages/player/player_view.dart'; import 'package:iris/store/use_app_store.dart'; class Home extends HookWidget { @@ -12,24 +12,16 @@ class Home extends HookWidget { @override Widget build(BuildContext context) { + useFullScreen(); + useOrientation(); + useResizeWindow(); + final playerBackend = useAppStore().select(context, (state) => state.playerBackend); - final player = () { - switch (playerBackend) { - case PlayerBackend.mediaKit: - return IrisPlayer( - key: const ValueKey('media-kit'), - playerHooks: useMediaKitPlayer, - ); - case PlayerBackend.fvp: - return IrisPlayer( - key: const ValueKey('fvp'), - playerHooks: useFvpPlayer, - ); - } - }(); - - return Scaffold(body: player); + return Scaffold( + backgroundColor: Colors.black, + body: PlayerView(playerBackend: playerBackend), + ); } } diff --git a/lib/pages/player/audio.dart b/lib/pages/player/audio.dart index 2ccede9..264ce8a 100644 --- a/lib/pages/player/audio.dart +++ b/lib/pages/player/audio.dart @@ -6,6 +6,37 @@ import 'package:iris/models/file.dart'; import 'package:iris/models/storages/storage.dart'; import 'package:iris/store/use_storage_store.dart'; +class _CoverImage extends StatelessWidget { + final FileItem cover; + final String? auth; + final BoxFit fit; + + const _CoverImage({ + required this.cover, + required this.auth, + required this.fit, + }); + + @override + Widget build(BuildContext context) { + final isLocal = cover.storageId == localStorageId; + if (isLocal) { + return Image.file( + File(cover.uri), + fit: fit, + gaplessPlayback: true, + ); + } else { + return Image.network( + cover.uri, + headers: auth != null ? {'authorization': auth!} : null, + fit: fit, + gaplessPlayback: true, + ); + } + } +} + class Audio extends HookWidget { const Audio({ super.key, @@ -22,61 +53,159 @@ class Audio extends HookWidget { : useStorageStore().findById(cover!.storageId), [cover?.storageId]); final auth = useMemoized(() => storage?.getAuth(), [storage]); + return IgnorePointer( child: Stack( + fit: StackFit.expand, children: [ - Container( - color: Colors.grey[800], - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - child: cover != null - ? cover?.storageId == localStorageId - ? Image.file( - File(cover!.uri), - fit: BoxFit.cover, - ) - : Image.network( - cover!.uri, - headers: auth != null ? {'authorization': auth} : null, - fit: BoxFit.cover, - ) - : null, - ), + if (cover != null) + _CoverImage(cover: cover!, auth: auth, fit: BoxFit.cover), BackdropFilter( - filter: ImageFilter.blur(sigmaX: 16.0, sigmaY: 16.0), - child: Container(color: Colors.transparent), + filter: ImageFilter.blur(sigmaX: 24.0, sigmaY: 24.0), + child: DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Theme.of(context) + .colorScheme + .surface + .withValues(alpha: 0.6), + Theme.of(context) + .colorScheme + .surface + .withValues(alpha: 0.2), + ], + ), + ), + ), ), - Positioned( - left: 0, - top: 0, - right: MediaQuery.of(context).size.width > 800 - ? MediaQuery.of(context).size.width / 2 - : 0, - bottom: 0, - child: Center( - child: SizedBox( - height: MediaQuery.of(context).size.height / 2, - child: ClipRRect( - borderRadius: BorderRadius.circular(8), - child: cover != null - ? cover!.storageId == localStorageId - ? Image.file( - File(cover!.uri), - fit: BoxFit.contain, - ) - : Image.network( - cover!.uri, - headers: - auth != null ? {'authorization': auth} : null, - fit: BoxFit.contain, - ) - : null, + LayoutBuilder( + builder: (context, constraints) { + const double wideLayoutThreshold = 600; + final isWideScreen = constraints.maxWidth >= wideLayoutThreshold; + + if (cover == null) { + return const SizedBox(); + } + + if (isWideScreen) { + return _buildWideLayout(context, constraints, cover!, auth); + } else { + return _buildNarrowLayout(context, constraints, cover!, auth); + } + }, + ), + ], + ), + ); + } + + Widget _buildNarrowLayout( + BuildContext context, + BoxConstraints constraints, + FileItem cover, + String? auth, + ) { + return Center( + child: Padding( + padding: const EdgeInsets.fromLTRB(48, 56, 48, 96), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 400.0, + maxHeight: 400.0, + ), + child: AspectRatio( + aspectRatio: 1.0, + child: _buildCoverCard( + cover: cover, + auth: auth, + shadowColor: Theme.of(context) + .colorScheme + .onSurface + .withValues(alpha: 0.15), + ), + ), + ), + ), + ); + } + + Widget _buildWideLayout( + BuildContext context, + BoxConstraints constraints, + FileItem cover, + String? auth, + ) { + return Row( + children: [ + Expanded( + flex: 5, + child: Center( + child: Padding( + padding: EdgeInsets.fromLTRB( + 48, + 56, + 24, + constraints.maxWidth > 1024 ? 64 : 96, + ), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 320.0, + maxHeight: 320.0, + ), + child: AspectRatio( + aspectRatio: 1.0, + child: _buildCoverCard( + cover: cover, + auth: auth, + shadowColor: Theme.of(context) + .colorScheme + .onSurface + .withValues(alpha: 0.15), + ), ), ), ), ), + ), + Expanded( + flex: 5, + child: Container( + padding: + const EdgeInsets.symmetric(horizontal: 48.0, vertical: 24.0), + ), + ), + ], + ); + } + + Widget _buildCoverCard({ + required FileItem cover, + required String? auth, + required Color shadowColor, + }) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: shadowColor, + blurRadius: 32, + spreadRadius: 2, + offset: const Offset(0, 8), + ), ], ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: _CoverImage( + cover: cover, + auth: auth, + fit: BoxFit.cover, + ), + ), ); } } diff --git a/lib/pages/player/control_bar/control_bar.dart b/lib/pages/player/control_bar/control_bar.dart index 4a4ba5b..a9f5ada 100644 --- a/lib/pages/player/control_bar/control_bar.dart +++ b/lib/pages/player/control_bar/control_bar.dart @@ -1,40 +1,40 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; -import 'package:iris/globals.dart'; +import 'package:iris/globals.dart' show rateMenuKey, speedStops, moreMenuKey; import 'package:iris/models/player.dart'; import 'package:iris/models/storages/local.dart'; import 'package:iris/models/store/app_state.dart'; -import 'package:iris/store/use_ui_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; import 'package:iris/widgets/dialogs/show_open_link_dialog.dart'; import 'package:iris/widgets/dialogs/show_rate_dialog.dart'; import 'package:iris/pages/player/control_bar/control_bar_slider.dart'; -import 'package:iris/pages/home/history.dart'; +import 'package:iris/pages/player/overlays/history.dart'; import 'package:iris/widgets/bottom_sheets/show_open_link_bottom_sheet.dart'; import 'package:iris/pages/settings/settings.dart'; import 'package:iris/pages/player/control_bar/volume_control.dart'; -import 'package:iris/pages/player/subtitle_and_audio_track.dart'; +import 'package:iris/pages/player/overlays/track/subtitle_and_audio_track.dart'; import 'package:iris/store/use_app_store.dart'; import 'package:iris/store/use_play_queue_store.dart'; import 'package:iris/utils/get_localizations.dart'; -import 'package:iris/pages/player/play_queue.dart'; +import 'package:iris/pages/player/overlays/play_queue.dart'; import 'package:iris/utils/platform.dart'; -import 'package:iris/utils/resize_window.dart'; import 'package:iris/widgets/popup.dart'; import 'package:iris/pages/storages/storages.dart'; +import 'package:provider/provider.dart'; +import 'package:window_manager/window_manager.dart'; class ControlBar extends HookWidget { const ControlBar({ super.key, - required this.player, required this.showControl, required this.showControlForHover, this.color, this.overlayColor, }); - final MediaPlayer player; final void Function() showControl; final Future Function(Future callback) showControlForHover; final Color? color; @@ -42,22 +42,25 @@ class ControlBar extends HookWidget { @override Widget build(BuildContext context) { + final width = MediaQuery.sizeOf(context).width; final t = getLocalizations(context); + final isPlaying = + context.select((player) => player.isPlaying); + + final isInitializing = + context.select((player) => player.isInitializing); + + final player = context.read(); + final rate = useAppStore().select(context, (state) => state.rate); final volume = useAppStore().select(context, (state) => state.volume); final isMuted = useAppStore().select(context, (state) => state.isMuted); + final isFullScreen = - useUiStore().select(context, (state) => state.isFullScreen); + usePlayerUiStore().select(context, (state) => state.isFullScreen); final int playQueueLength = usePlayQueueStore().select(context, (state) => state.playQueue.length); - final playQueue = - usePlayQueueStore().select(context, (state) => state.playQueue); - final currentIndex = - usePlayQueueStore().select(context, (state) => state.currentIndex); - final currentPlayIndex = useMemoized( - () => playQueue.indexWhere((element) => element.index == currentIndex), - [playQueue, currentIndex]); final bool shuffle = useAppStore().select(context, (state) => state.shuffle); @@ -65,6 +68,18 @@ class ControlBar extends HookWidget { useAppStore().select(context, (state) => state.repeat); final BoxFit fit = useAppStore().select(context, (state) => state.fit); + final isSeeking = + usePlayerUiStore().select(context, (state) => state.isSeeking); + + final displayIsPlaying = useState(isPlaying); + + useEffect(() { + if (!isSeeking) { + displayIsPlaying.value = isPlaying; + } + return null; + }, [isPlaying]); + return Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( @@ -72,561 +87,498 @@ class ControlBar extends HookWidget { begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ - Colors.black87.withValues(alpha: 0), - Colors.black87.withValues(alpha: 0.3), - Colors.black87.withValues(alpha: 0.8), + Colors.black.withValues(alpha: 0), + Colors.black.withValues(alpha: 0.25), + Colors.black.withValues(alpha: 0.65), ], ), ), - child: SafeArea( - child: Column( - children: [ - Visibility( - visible: MediaQuery.of(context).size.width < 1024 || !isDesktop, - child: ControlBarSlider( - player: player, - showControl: showControl, - color: color, - ), + child: Column( + children: [ + Visibility( + visible: width < 1024 || !isDesktop, + child: ControlBarSlider( + showControl: showControl, + color: color, ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(width: 8), - if (playQueueLength > 1) + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(width: 2), + Stack( + alignment: Alignment.center, + children: [ IconButton( - tooltip: currentPlayIndex == 0 - ? null - : '${t.previous} ( Ctrl + ← )', + tooltip: + '${displayIsPlaying.value ? t.pause : t.play} ( Space )', icon: Icon( - Icons.skip_previous_rounded, - size: 28, - color: - currentPlayIndex == 0 ? color?.withAlpha(153) : color, + displayIsPlaying.value + ? Icons.pause_rounded + : Icons.play_arrow_rounded, + size: 32, + color: color, ), - onPressed: currentPlayIndex == 0 - ? null - : () { - showControl(); - usePlayQueueStore().previous(); - }, + onPressed: () { + showControl(); + if (isPlaying == true) { + useAppStore().updateAutoPlay(false); + player.pause(); + } else { + useAppStore().updateAutoPlay(true); + player.play(); + } + }, style: ButtonStyle(overlayColor: overlayColor), ), - Stack( - alignment: Alignment.center, - children: [ - IconButton( - tooltip: - '${player.isPlaying == true ? t.pause : t.play} ( Space )', - icon: Icon( - player.isPlaying == true - ? Icons.pause_rounded - : Icons.play_arrow_rounded, - size: 36, - color: color, + if (isInitializing) + SizedBox( + width: 32, + height: 32, + child: CircularProgressIndicator( + strokeWidth: 4, + color: Theme.of(context).colorScheme.surface, ), - onPressed: () { - showControl(); - if (player.isPlaying == true) { - player.pause(); - } else { - player.play(); - } - }, - style: ButtonStyle(overlayColor: overlayColor), ), - if (player.isInitializing) - SizedBox( - width: 36, - height: 36, - child: CircularProgressIndicator( - strokeWidth: 4, - color: Theme.of(context).colorScheme.surface, - ), - ), - ], + ], + ), + IconButton( + tooltip: '${t.stop} ( Ctrl + C )', + icon: Icon( + Icons.stop_rounded, + size: 26, + color: color, ), - if (playQueueLength > 1) - IconButton( - tooltip: currentPlayIndex == playQueueLength - 1 - ? null - : '${t.next} ( Ctrl + → )', - icon: Icon( - Icons.skip_next_rounded, - size: 28, - color: currentPlayIndex == playQueueLength - 1 - ? color?.withAlpha(153) - : color, - ), - onPressed: currentPlayIndex == playQueueLength - 1 - ? null - : () { - showControl(); - usePlayQueueStore().next(); - }, - style: ButtonStyle(overlayColor: overlayColor), + onPressed: () { + showControl(); + useAppStore().updateAutoPlay(false); + player.pause(); + usePlayQueueStore().updateCurrentIndex(-1); + }, + style: ButtonStyle(overlayColor: overlayColor), + ), + if (playQueueLength > 1) + IconButton( + tooltip: '${t.previous} ( Ctrl + ← )', + icon: Icon( + Icons.skip_previous_rounded, + size: 26, + color: color, ), - if (MediaQuery.of(context).size.width >= 768) - Builder( - builder: (context) => IconButton( - tooltip: - '${t.shuffle}: ${shuffle ? t.on : t.off} ( Ctrl + X )', - icon: Icon( - Icons.shuffle_rounded, - size: 20, - color: !shuffle ? color?.withAlpha(153) : color, - ), - onPressed: () { - showControl(); - shuffle - ? usePlayQueueStore().sort() - : usePlayQueueStore().shuffle(); - useAppStore().updateShuffle(!shuffle); - }, - style: ButtonStyle(overlayColor: overlayColor), - ), + onPressed: () { + showControl(); + usePlayQueueStore().previous(); + }, + style: ButtonStyle(overlayColor: overlayColor), + ), + if (playQueueLength > 1) + IconButton( + tooltip: '${t.next} ( Ctrl + → )', + icon: Icon( + Icons.skip_next_rounded, + size: 26, + color: color, ), - if (MediaQuery.of(context).size.width >= 768) - Builder( - builder: (context) => IconButton( - tooltip: - '${repeat == Repeat.one ? t.repeat_one : repeat == Repeat.all ? t.repeat_all : t.repeat_none} ( Ctrl + R )', - icon: Icon( - repeat == Repeat.one - ? Icons.repeat_one_rounded - : Icons.repeat_rounded, - size: 20, - color: repeat == Repeat.none - ? color?.withAlpha(153) - : color, - ), - onPressed: () { - showControl(); - useAppStore().toggleRepeat(); - }, - style: ButtonStyle(overlayColor: overlayColor), + onPressed: () { + showControl(); + usePlayQueueStore().next(); + }, + style: ButtonStyle(overlayColor: overlayColor), + ), + if (width >= 768) + Builder( + builder: (context) => IconButton( + tooltip: + '${t.shuffle}: ${shuffle ? t.on : t.off} ( Ctrl + X )', + icon: Icon( + Icons.shuffle_rounded, + size: 20, + color: !shuffle ? color?.withAlpha(153) : color, ), + onPressed: () { + showControl(); + shuffle + ? usePlayQueueStore().sort() + : usePlayQueueStore().shuffle(); + useAppStore().updateShuffle(!shuffle); + }, + style: ButtonStyle(overlayColor: overlayColor), ), - if (MediaQuery.of(context).size.width >= 768) - IconButton( + ), + if (width >= 768) + Builder( + builder: (context) => IconButton( tooltip: - '${t.video_zoom}: ${fit == BoxFit.contain ? t.fit : fit == BoxFit.fill ? t.stretch : fit == BoxFit.cover ? t.crop : '100%'} ( Ctrl + V )', + '${repeat == Repeat.one ? t.repeat_one : repeat == Repeat.all ? t.repeat_all : t.repeat_none} ( Ctrl + R )', icon: Icon( - fit == BoxFit.contain - ? Icons.fit_screen_rounded - : fit == BoxFit.fill - ? Icons.aspect_ratio_rounded - : fit == BoxFit.cover - ? Icons.crop_landscape_rounded - : Icons.crop_free_rounded, + repeat == Repeat.one + ? Icons.repeat_one_rounded + : Icons.repeat_rounded, size: 20, - color: color, + color: + repeat == Repeat.none ? color?.withAlpha(153) : color, ), onPressed: () { showControl(); - useAppStore().toggleFit(); + useAppStore().toggleRepeat(); }, style: ButtonStyle(overlayColor: overlayColor), ), - if (MediaQuery.of(context).size.width > 600) - PopupMenuButton( - key: rateMenuKey, - clipBehavior: Clip.hardEdge, - constraints: const BoxConstraints(minWidth: 0), - itemBuilder: (BuildContext context) => [ - 0.25, - 0.5, - 0.75, - 1.0, - 1.25, - 1.5, - 1.75, - 2.0, - 3.0, - 4.0, - 5.0, - ] - .map( - (item) => PopupMenuItem( - child: Text( - '${item}X', - style: TextStyle( - color: item == rate - ? Theme.of(context).colorScheme.primary - : null, - fontWeight: item == rate - ? FontWeight.bold - : FontWeight.w100, - ), + ), + if (width >= 768) + IconButton( + tooltip: + '${t.video_zoom}: ${fit == BoxFit.contain ? t.fit : fit == BoxFit.fill ? t.stretch : fit == BoxFit.cover ? t.crop : '100%'} ( Ctrl + V )', + icon: Icon( + fit == BoxFit.contain + ? Icons.fit_screen_rounded + : fit == BoxFit.fill + ? Icons.aspect_ratio_rounded + : fit == BoxFit.cover + ? Icons.crop_landscape_rounded + : Icons.crop_free_rounded, + size: 20, + color: color, + ), + onPressed: () { + showControl(); + useAppStore().toggleFit(); + }, + style: ButtonStyle(overlayColor: overlayColor), + ), + if (width > 600) + PopupMenuButton( + key: rateMenuKey, + clipBehavior: Clip.hardEdge, + constraints: const BoxConstraints(minWidth: 0), + itemBuilder: (BuildContext context) => speedStops + .map( + (item) => PopupMenuItem( + child: Text( + '${item}X', + style: TextStyle( + color: item == rate + ? Theme.of(context).colorScheme.primary + : null, + fontWeight: item == rate + ? FontWeight.bold + : FontWeight.w100, + height: 1, ), - onTap: () async { - showControl(); - useAppStore().updateRate(item); - }, - ), - ) - .toList(), - child: Tooltip( - message: t.playback_speed, - child: TextButton( - onPressed: () => - rateMenuKey.currentState?.showButtonMenu(), - style: ButtonStyle(overlayColor: overlayColor), - child: Text( - '${rate}X', - style: TextStyle( - fontWeight: FontWeight.bold, - color: color, ), + onTap: () async { + showControl(); + useAppStore().updateRate(item); + }, ), - ), - ), - ), - if (MediaQuery.of(context).size.width < 600) - Builder( - builder: (context) => IconButton( - tooltip: '${t.volume}: $volume', - icon: Icon( - isMuted || volume == 0 - ? Icons.volume_off_rounded - : volume < 50 - ? Icons.volume_down_rounded - : Icons.volume_up_rounded, - size: 20, - color: color, - ), - onPressed: () => showControlForHover( - showVolumePopover(context, showControl), - ), + ) + .toList(), + child: Tooltip( + message: t.playback_speed, + child: TextButton( + onPressed: () => + rateMenuKey.currentState?.showButtonMenu(), style: ButtonStyle(overlayColor: overlayColor), - ), - ), - if (MediaQuery.of(context).size.width >= 600) - SizedBox( - width: 160, - child: VolumeControl( - showControl: showControl, - showVolumeText: false, - color: color, - overlayColor: overlayColor, - ), - ), - Expanded( - child: Visibility( - visible: - MediaQuery.of(context).size.width >= 1024 && isDesktop, - child: ControlBarSlider( - player: player, - showControl: showControl, - color: color, + child: Text( + '${rate}X', + style: TextStyle( + fontWeight: FontWeight.bold, + color: color, + ), + ), ), ), ), - if (MediaQuery.of(context).size.width >= 420) - IconButton( - tooltip: '${t.subtitle_and_audio_track} ( S )', + if (width < 640) + Builder( + builder: (context) => IconButton( + tooltip: '${t.volume}: $volume', icon: Icon( - Icons.subtitles_rounded, + isMuted || volume == 0 + ? Icons.volume_off_rounded + : volume < 50 + ? Icons.volume_down_rounded + : Icons.volume_up_rounded, size: 20, color: color, ), - onPressed: () async { - showControlForHover( - showPopup( - context: context, - child: SubtitleAndAudioTrack(player: player), - direction: PopupDirection.right, - ), - ); - }, + onPressed: () => showControlForHover( + showVolumePopover(context, showControl), + ), style: ButtonStyle(overlayColor: overlayColor), ), + ), + if (width >= 640) + SizedBox( + width: 160, + child: VolumeControl( + showControl: showControl, + showVolumeText: false, + color: color, + overlayColor: overlayColor, + ), + ), + Expanded( + child: Visibility( + visible: + width >= 1024 && isDesktop, + child: ControlBarSlider( + showControl: showControl, + color: color, + ), + ), + ), + if (width >= 420) IconButton( - tooltip: '${t.play_queue} ( P )', - icon: Transform.translate( - offset: const Offset(0, 1.5), - child: Icon( - Icons.playlist_play_rounded, - size: 28, - color: color, - ), + tooltip: '${t.subtitle_and_audio_track} ( S )', + icon: Icon( + Icons.subtitles_rounded, + size: 20, + color: color, ), onPressed: () async { showControlForHover( showPopup( context: context, - child: const PlayQueue(), + child: Provider.value( + value: context.read(), + child: const SubtitleAndAudioTrack(), + ), direction: PopupDirection.right, ), ); }, style: ButtonStyle(overlayColor: overlayColor), ), - IconButton( - tooltip: '${t.storage} ( F )', - icon: Icon( - Icons.storage_rounded, - size: 18, + IconButton( + tooltip: '${t.play_queue} ( P )', + icon: Transform.translate( + offset: const Offset(0, 1.5), + child: Icon( + Icons.playlist_play_rounded, + size: 28, color: color, ), - onPressed: () => showControlForHover( + ), + onPressed: () async { + showControlForHover( showPopup( context: context, - child: const Storages(), + child: const PlayQueue(), direction: PopupDirection.right, ), + ); + }, + style: ButtonStyle(overlayColor: overlayColor), + ), + IconButton( + tooltip: '${t.storage} ( F )', + icon: Icon( + Icons.storage_rounded, + size: 18, + color: color, + ), + onPressed: () => showControlForHover( + showPopup( + context: context, + child: const Storages(), + direction: PopupDirection.right, + ), + ), + style: ButtonStyle(overlayColor: overlayColor), + ), + Visibility( + visible: isDesktop, + child: IconButton( + tooltip: isFullScreen + ? '${t.exit_fullscreen} ( Escape, F11, Enter )' + : '${t.enter_fullscreen} ( F11, Enter )', + icon: Icon( + isFullScreen + ? Icons.close_fullscreen_rounded + : Icons.open_in_full_rounded, + size: 19, + color: color, ), + onPressed: () async { + showControl(); + usePlayerUiStore().updateFullScreen(!isFullScreen); + }, style: ButtonStyle(overlayColor: overlayColor), ), - Visibility( - visible: isDesktop, - child: IconButton( - tooltip: isFullScreen - ? '${t.exit_fullscreen} ( Escape, F11, Enter )' - : '${t.enter_fullscreen} ( F11, Enter )', - icon: Icon( - isFullScreen - ? Icons.close_fullscreen_rounded - : Icons.open_in_full_rounded, - size: 19, - color: color, + ), + PopupMenuButton( + key: moreMenuKey, + icon: Icon( + Icons.more_vert_rounded, + size: 20, + color: color, + ), + style: ButtonStyle(overlayColor: overlayColor), + clipBehavior: Clip.hardEdge, + constraints: const BoxConstraints(minWidth: 200), + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + child: ListTile( + mouseCursor: SystemMouseCursors.click, + leading: const Icon( + Icons.file_open_rounded, + size: 16.5, + ), + title: Text(t.open_file), + trailing: Text( + 'Ctrl + O', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).dividerColor, + ), + ), ), - onPressed: () async { + onTap: () async { showControl(); - if (isFullScreen) { - await resizeWindow(player.aspect); + if (Platform.isAndroid) { + await pickContentFile(); + } else { + await pickLocalFile(); } - useUiStore().updateFullScreen(!isFullScreen); + showControl(); }, - style: ButtonStyle(overlayColor: overlayColor), ), - ), - PopupMenuButton( - key: moreMenuKey, - icon: Icon( - Icons.more_vert_rounded, - size: 20, - color: color, + PopupMenuItem( + child: ListTile( + mouseCursor: SystemMouseCursors.click, + leading: const Icon( + Icons.file_present_rounded, + size: 16.5, + ), + title: Text(t.open_link), + trailing: Text( + 'Ctrl + L', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).dividerColor, + ), + ), + ), + onTap: () async { + isDesktop + ? await showOpenLinkDialog(context) + : await showOpenLinkBottomSheet(context); + showControl(); + }, ), - style: ButtonStyle(overlayColor: overlayColor), - clipBehavior: Clip.hardEdge, - constraints: const BoxConstraints(minWidth: 200), - itemBuilder: (BuildContext context) => [ + if (width < 768) PopupMenuItem( child: ListTile( mouseCursor: SystemMouseCursors.click, - leading: const Icon( - Icons.file_open_rounded, - size: 16.5, + leading: Icon( + Icons.shuffle_rounded, + size: 20, + color: !shuffle + ? Theme.of(context).disabledColor + : Theme.of(context).colorScheme.onSurfaceVariant, ), - title: Text(t.open_file), + title: Text('${t.shuffle}: ${shuffle ? t.on : t.off}'), trailing: Text( - 'Ctrl + O', + 'Ctrl + X', style: TextStyle( fontSize: 12, color: Theme.of(context).dividerColor, ), ), ), - onTap: () async { - showControl(); - if (Platform.isAndroid) { - await pickContentFile(); - } else { - await pickLocalFile(); - } + onTap: () { showControl(); + shuffle + ? usePlayQueueStore().sort() + : usePlayQueueStore().shuffle(); + useAppStore().updateShuffle(!shuffle); }, ), + if (width < 768) PopupMenuItem( child: ListTile( mouseCursor: SystemMouseCursors.click, - leading: const Icon( - Icons.file_present_rounded, - size: 16.5, + leading: Icon( + repeat == Repeat.one + ? Icons.repeat_one_rounded + : Icons.repeat_rounded, + size: 20, + color: repeat == Repeat.none + ? Theme.of(context).disabledColor + : Theme.of(context).colorScheme.onSurfaceVariant, ), - title: Text(t.open_link), + title: Text(repeat == Repeat.one + ? t.repeat_one + : repeat == Repeat.all + ? t.repeat_all + : t.repeat_none), trailing: Text( - 'Ctrl + L', + 'Ctrl + R', style: TextStyle( fontSize: 12, color: Theme.of(context).dividerColor, ), ), ), - onTap: () async { - isDesktop - ? await showOpenLinkDialog(context) - : await showOpenLinkBottomSheet(context); + onTap: () { showControl(); + useAppStore().toggleRepeat(); }, ), - if (MediaQuery.of(context).size.width < 768) - PopupMenuItem( - child: ListTile( - mouseCursor: SystemMouseCursors.click, - leading: Icon( - Icons.shuffle_rounded, - size: 20, - color: !shuffle - ? Theme.of(context).disabledColor - : Theme.of(context) - .colorScheme - .onSurfaceVariant, - ), - title: - Text('${t.shuffle}: ${shuffle ? t.on : t.off}'), - trailing: Text( - 'Ctrl + X', - style: TextStyle( - fontSize: 12, - color: Theme.of(context).dividerColor, - ), - ), - ), - onTap: () { - showControl(); - shuffle - ? usePlayQueueStore().sort() - : usePlayQueueStore().shuffle(); - useAppStore().updateShuffle(!shuffle); - }, - ), - if (MediaQuery.of(context).size.width < 768) - PopupMenuItem( - child: ListTile( - mouseCursor: SystemMouseCursors.click, - leading: Icon( - repeat == Repeat.one - ? Icons.repeat_one_rounded - : Icons.repeat_rounded, - size: 20, - color: repeat == Repeat.none - ? Theme.of(context).disabledColor - : Theme.of(context) - .colorScheme - .onSurfaceVariant, - ), - title: Text(repeat == Repeat.one - ? t.repeat_one - : repeat == Repeat.all - ? t.repeat_all - : t.repeat_none), - trailing: Text( - 'Ctrl + R', - style: TextStyle( - fontSize: 12, - color: Theme.of(context).dividerColor, - ), - ), - ), - onTap: () { - showControl(); - useAppStore().toggleRepeat(); - }, - ), - if (MediaQuery.of(context).size.width < 768) - PopupMenuItem( - child: ListTile( - mouseCursor: SystemMouseCursors.click, - leading: Icon( - fit == BoxFit.contain - ? Icons.fit_screen_rounded - : fit == BoxFit.fill - ? Icons.aspect_ratio_rounded - : fit == BoxFit.cover - ? Icons.crop_landscape_rounded - : Icons.crop_free_rounded, - size: 20, - ), - title: Text( - '${t.video_zoom}: ${fit == BoxFit.contain ? t.fit : fit == BoxFit.fill ? t.stretch : fit == BoxFit.cover ? t.crop : '100%'}'), - trailing: Text( - 'Ctrl + V', - style: TextStyle( - fontSize: 12, - color: Theme.of(context).dividerColor, - ), - ), - ), - onTap: () { - showControl(); - useAppStore().toggleFit(); - }, - ), - if (MediaQuery.of(context).size.width <= 460) - PopupMenuItem( - child: ListTile( - mouseCursor: SystemMouseCursors.click, - leading: const Icon( - Icons.speed_rounded, - size: 20, - ), - title: Text('${t.playback_speed}: ${rate}X'), - ), - onTap: () => - showControlForHover(showRateDialog(context)), - ), - if (MediaQuery.of(context).size.width < 420) - PopupMenuItem( - child: ListTile( - mouseCursor: SystemMouseCursors.click, - leading: const Icon( - Icons.subtitles_rounded, - size: 20, - ), - title: Text(t.subtitle_and_audio_track), - trailing: Text( - 'S', - style: TextStyle( - fontSize: 12, - color: Theme.of(context).dividerColor, - ), - ), - ), - onTap: () => showControlForHover( - showPopup( - context: context, - child: SubtitleAndAudioTrack(player: player), - direction: PopupDirection.right, - ), - ), - ), + if (width < 768) PopupMenuItem( child: ListTile( mouseCursor: SystemMouseCursors.click, - leading: const Icon( - Icons.history_rounded, + leading: Icon( + fit == BoxFit.contain + ? Icons.fit_screen_rounded + : fit == BoxFit.fill + ? Icons.aspect_ratio_rounded + : fit == BoxFit.cover + ? Icons.crop_landscape_rounded + : Icons.crop_free_rounded, size: 20, ), - title: Text(t.history), + title: Text( + '${t.video_zoom}: ${fit == BoxFit.contain ? t.fit : fit == BoxFit.fill ? t.stretch : fit == BoxFit.cover ? t.crop : '100%'}'), trailing: Text( - 'Ctirl + H', + 'Ctrl + V', style: TextStyle( fontSize: 12, color: Theme.of(context).dividerColor, ), ), ), - onTap: () => showControlForHover( - showPopup( - context: context, - child: const History(), - direction: PopupDirection.right, + onTap: () { + showControl(); + useAppStore().toggleFit(); + }, + ), + if (width <= 460) + PopupMenuItem( + child: ListTile( + mouseCursor: SystemMouseCursors.click, + leading: const Icon( + Icons.speed_rounded, + size: 20, ), + title: Text('${t.playback_speed}: ${rate}X'), ), + onTap: () => showControlForHover(showRateDialog(context)), ), + if (width < 420) PopupMenuItem( child: ListTile( mouseCursor: SystemMouseCursors.click, leading: const Icon( - Icons.settings_rounded, + Icons.subtitles_rounded, size: 20, ), - title: Text(t.settings), + title: Text(t.subtitle_and_audio_track), trailing: Text( - 'Ctirl + P', + 'S', style: TextStyle( fontSize: 12, color: Theme.of(context).dividerColor, @@ -636,18 +588,94 @@ class ControlBar extends HookWidget { onTap: () => showControlForHover( showPopup( context: context, - child: const Settings(), + child: Provider.value( + value: context.read(), + child: const SubtitleAndAudioTrack(), + ), direction: PopupDirection.right, ), ), ), - ], - ), - const SizedBox(width: 8), - ], - ), - ], - ), + PopupMenuItem( + child: ListTile( + mouseCursor: SystemMouseCursors.click, + leading: const Icon( + Icons.history_rounded, + size: 20, + ), + title: Text(t.history), + trailing: Text( + 'Ctirl + H', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).dividerColor, + ), + ), + ), + onTap: () => showControlForHover( + showPopup( + context: context, + child: const History(), + direction: PopupDirection.right, + ), + ), + ), + PopupMenuItem( + child: ListTile( + mouseCursor: SystemMouseCursors.click, + leading: const Icon( + Icons.settings_rounded, + size: 20, + ), + title: Text(t.settings), + trailing: Text( + 'Ctirl + P', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).dividerColor, + ), + ), + ), + onTap: () => showControlForHover( + showPopup( + context: context, + child: const Settings(), + direction: PopupDirection.right, + ), + ), + ), + PopupMenuItem( + child: ListTile( + mouseCursor: SystemMouseCursors.click, + leading: const Icon( + Icons.exit_to_app_rounded, + size: 20, + ), + title: Text(t.exit), + trailing: Text( + 'Alt + X', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).dividerColor, + ), + ), + ), + onTap: () async { + await player.saveProgress(); + if (isDesktop) { + windowManager.close(); + } else { + SystemNavigator.pop(); + exit(0); + } + }, + ), + ], + ), + const SizedBox(width: 2), + ], + ), + ], ), ); } diff --git a/lib/pages/player/control_bar/control_bar_slider.dart b/lib/pages/player/control_bar/control_bar_slider.dart index c1cadc2..d068f20 100644 --- a/lib/pages/player/control_bar/control_bar_slider.dart +++ b/lib/pages/player/control_bar/control_bar_slider.dart @@ -1,25 +1,52 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_zustand/flutter_zustand.dart'; import 'package:iris/models/player.dart'; +import 'package:iris/store/use_app_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; import 'package:iris/utils/format_duration_to_minutes.dart'; +import 'package:provider/provider.dart'; class ControlBarSlider extends HookWidget { const ControlBarSlider({ super.key, - required this.player, required this.showControl, this.disabled = false, this.color, }); - final MediaPlayer player; - final void Function() showControl; final bool disabled; final Color? color; @override Widget build(BuildContext context) { + final autoPlay = useAppStore().select(context, (state) => state.autoPlay); + + final progress = context.select< + MediaPlayer, + ({ + Duration position, + Duration duration, + Duration buffer, + })>( + (player) => ( + position: player.position, + duration: player.duration, + buffer: player.buffer, + ), + ); + + final play = context.read().play; + final pause = context.read().pause; + final seek = context.read().seek; + + final double max = progress.duration.inMilliseconds.toDouble(); + final double positionValue = + progress.position.inMilliseconds.toDouble().clamp(0.0, max); + final double bufferValue = + progress.buffer.inMilliseconds.toDouble().clamp(0.0, max); + return ExcludeFocus( child: Container( padding: const EdgeInsets.fromLTRB(12, 0, 12, 0), @@ -28,93 +55,68 @@ class ControlBarSlider extends HookWidget { Visibility( visible: !disabled, child: Text( - formatDurationToMinutes(player.position), - style: TextStyle( - color: color, - height: 2, - ), + formatDurationToMinutes(progress.position), + style: TextStyle(color: color, height: 2), ), ), Expanded( - child: Stack( - children: [ - SliderTheme( - data: SliderTheme.of(context).copyWith( - disabledActiveTrackColor: color?.withAlpha(111), - thumbShape: const RoundSliderThumbShape( - disabledThumbRadius: 0, - elevation: 0, - pressedElevation: 0, - ), - overlayShape: const RoundSliderOverlayShape( - overlayRadius: 12, - ), - trackShape: const RoundedActiveTrackShape(), - trackHeight: 3, - ), - child: Slider( - value: player.buffer.inSeconds.toDouble() > - player.duration.inSeconds.toDouble() - ? 0 - : player.buffer.inSeconds.toDouble(), - min: 0, - max: player.duration.inSeconds.toDouble(), - onChanged: null, - ), + child: SliderTheme( + data: SliderTheme.of(context).copyWith( + activeTrackColor: color?.withAlpha(222) ?? + Theme.of(context).colorScheme.primary, + inactiveTrackColor: color?.withAlpha(70) ?? + Theme.of(context) + .colorScheme + .onSurface + .withValues(alpha: 0.25), + secondaryActiveTrackColor: color?.withAlpha(120) ?? + Theme.of(context) + .colorScheme + .onSurface + .withValues(alpha: 0.4), + thumbColor: color ?? Theme.of(context).colorScheme.primary, + thumbShape: RoundSliderThumbShape( + enabledThumbRadius: disabled ? 0 : 6, ), - SliderTheme( - data: SliderTheme.of(context).copyWith( - thumbColor: color, - activeTrackColor: color?.withAlpha(222), - inactiveTrackColor: color?.withAlpha(99), - thumbShape: RoundSliderThumbShape( - enabledThumbRadius: disabled ? 0 : 6, - ), - overlayShape: const RoundSliderOverlayShape( - overlayRadius: 12, - ), - trackShape: - disabled ? const RoundedActiveTrackShape() : null, - trackHeight: 4, - ), - child: Slider( - value: player.position.inSeconds.toDouble() > - player.duration.inSeconds.toDouble() - ? 0 - : player.position.inSeconds.toDouble(), - min: 0, - max: player.duration.inSeconds.toDouble(), - onChangeStart: (value) { - player.updateSeeking(true); - }, - onChanged: (value) { - showControl(); - if (player is MediaKitPlayer) { - player - .updatePosition(Duration(seconds: value.toInt())); - } else if (player is FvpPlayer) { - player.seekTo(Duration(seconds: value.toInt())); - } - }, - onChangeEnd: (value) async { - if (player is MediaKitPlayer) { - await player.seekTo(Duration(seconds: value.toInt())); - } - player.updateSeeking(false); - }, - ), + overlayShape: const RoundSliderOverlayShape( + overlayRadius: 12, ), - ], + trackHeight: 4, + trackShape: const _CustomTrackShape(), + ), + child: Slider( + value: positionValue, + secondaryTrackValue: bufferValue, + min: 0, + max: max > 0 ? max : 1.0, + onChanged: disabled + ? null + : (value) { + showControl(); + seek(Duration(milliseconds: value.toInt())); + }, + onChangeStart: disabled + ? null + : (value) { + usePlayerUiStore().updateIsSeeking(true); + pause(); + }, + onChangeEnd: disabled + ? null + : (value) async { + if (autoPlay) { + play(); + } + usePlayerUiStore().updateIsSeeking(false); + }, + ), ), ), Visibility( visible: !disabled, child: Text( - formatDurationToMinutes(player.duration), - style: TextStyle( - color: color, - height: 2, - ), + formatDurationToMinutes(progress.duration), + style: TextStyle(color: color, height: 2), ), ), ], @@ -124,9 +126,8 @@ class ControlBarSlider extends HookWidget { } } -class RoundedActiveTrackShape extends SliderTrackShape - with BaseSliderTrackShape { - const RoundedActiveTrackShape(); +class _CustomTrackShape extends RoundedRectSliderTrackShape { + const _CustomTrackShape(); @override void paint( @@ -142,21 +143,10 @@ class RoundedActiveTrackShape extends SliderTrackShape bool isEnabled = false, double additionalActiveTrackHeight = 2, }) { - assert(sliderTheme.disabledActiveTrackColor != null); - assert(sliderTheme.disabledInactiveTrackColor != null); - assert(sliderTheme.activeTrackColor != null); - assert(sliderTheme.inactiveTrackColor != null); - assert(sliderTheme.thumbShape != null); if (sliderTheme.trackHeight == null || sliderTheme.trackHeight! <= 0) { return; } - final ColorTween activeTrackColorTween = ColorTween( - begin: sliderTheme.disabledActiveTrackColor, - end: sliderTheme.activeTrackColor); - final Paint activePaint = Paint() - ..color = activeTrackColorTween.evaluate(enableAnimation)!; - final Rect trackRect = getPreferredRect( parentBox: parentBox, offset: offset, @@ -164,20 +154,40 @@ class RoundedActiveTrackShape extends SliderTrackShape isEnabled: isEnabled, isDiscrete: isDiscrete, ); - final Radius activeTrackRadius = - Radius.circular((trackRect.height + additionalActiveTrackHeight) / 2); + final Radius trackRadius = Radius.circular(trackRect.height / 2); + + final Paint inactivePaint = Paint() + ..color = sliderTheme.inactiveTrackColor!; context.canvas.drawRRect( - RRect.fromLTRBAndCorners( + RRect.fromRectAndRadius(trackRect, trackRadius), + inactivePaint, + ); + + if (secondaryOffset != null) { + final Paint secondaryPaint = Paint() + ..color = sliderTheme.secondaryActiveTrackColor!; + final Rect secondaryRect = Rect.fromLTRB( trackRect.left, - trackRect.top - (additionalActiveTrackHeight / 2), - thumbCenter.dx, - trackRect.bottom + (additionalActiveTrackHeight / 2), - topLeft: activeTrackRadius, - bottomLeft: activeTrackRadius, - topRight: activeTrackRadius, - bottomRight: activeTrackRadius, - ), + trackRect.top, + secondaryOffset.dx, + trackRect.bottom, + ); + context.canvas.drawRRect( + RRect.fromRectAndRadius(secondaryRect, trackRadius), + secondaryPaint, + ); + } + + final Paint activePaint = Paint()..color = sliderTheme.activeTrackColor!; + final Rect activeRect = Rect.fromLTRB( + trackRect.left, + trackRect.top, + thumbCenter.dx, + trackRect.bottom, + ); + context.canvas.drawRRect( + RRect.fromRectAndRadius(activeRect, trackRadius), activePaint, ); } diff --git a/lib/pages/player/control_bar/volume_slider.dart b/lib/pages/player/control_bar/volume_slider.dart index 7fcacde..17d4052 100644 --- a/lib/pages/player/control_bar/volume_slider.dart +++ b/lib/pages/player/control_bar/volume_slider.dart @@ -32,7 +32,7 @@ class VolumeSlider extends HookWidget { overlayShape: const RoundSliderOverlayShape( overlayRadius: 4, ), - trackHeight: 3.6, + trackHeight: 2.4, ), child: Slider( value: isMuted ? 0 : volume.toDouble(), diff --git a/lib/pages/player/iris_player.dart b/lib/pages/player/iris_player.dart deleted file mode 100644 index 5fcdc51..0000000 --- a/lib/pages/player/iris_player.dart +++ /dev/null @@ -1,1089 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'dart:ui'; -import 'package:desktop_drop/desktop_drop.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:flutter_zustand/flutter_zustand.dart'; -import 'package:iris/globals.dart'; -import 'package:iris/hooks/use_app_lifecycle.dart'; -import 'package:iris/hooks/use_brightness.dart'; -import 'package:iris/hooks/use_cover.dart'; -import 'package:iris/hooks/use_full_screen.dart'; -import 'package:iris/hooks/use_orientation.dart'; -import 'package:iris/hooks/use_volume.dart'; -import 'package:iris/info.dart'; -import 'package:iris/models/file.dart'; -import 'package:iris/models/player.dart'; -import 'package:iris/models/storages/local.dart'; -import 'package:iris/widgets/dialogs/show_open_link_dialog.dart'; -import 'package:iris/pages/home/history.dart'; -import 'package:iris/pages/player/play_queue.dart'; -import 'package:iris/pages/player/audio.dart'; -import 'package:iris/pages/player/control_bar/control_bar_slider.dart'; -import 'package:iris/widgets/bottom_sheets/show_open_link_bottom_sheet.dart'; -import 'package:iris/pages/settings/settings.dart'; -import 'package:iris/pages/player/subtitle_and_audio_track.dart'; -import 'package:iris/store/use_ui_store.dart'; -import 'package:iris/utils/check_content_type.dart'; -import 'package:iris/utils/logger.dart'; -import 'package:iris/utils/platform.dart'; -import 'package:iris/widgets/popup.dart'; -import 'package:iris/pages/storages/storages.dart'; -import 'package:iris/store/use_app_store.dart'; -import 'package:iris/store/use_play_queue_store.dart'; -import 'package:iris/utils/format_duration_to_minutes.dart'; -import 'package:iris/utils/get_localizations.dart'; -import 'package:iris/utils/resize_window.dart'; -import 'package:iris/widgets/title_bar.dart'; -import 'package:iris/pages/player/control_bar/control_bar.dart'; -import 'package:media_kit_video/media_kit_video.dart'; -import 'package:video_player/video_player.dart'; -import 'package:window_manager/window_manager.dart'; - -enum MediaType { - video, - audio, -} - -class IrisPlayer extends HookWidget { - const IrisPlayer({super.key, required this.playerHooks}); - - final MediaPlayer Function(BuildContext) playerHooks; - - @override - Widget build(BuildContext context) { - final MediaPlayer player = playerHooks(context); - - useAppLifecycle(player); - useFullScreen(context); - useOrientation(context, player); - final cover = useCover(context, player); - - final isHover = useState(false); - final isTouch = useState(false); - final isLongPress = useState(false); - final startPosition = useState(null); - final isHorizontalGesture = useState(false); - final isVerticalGesture = useState(false); - final isLeftGesture = useState(false); - final isRightGesture = useState(false); - - final isShowControl = useState(true); - final isShowProgress = useState(false); - - final controlHideTimer = useRef(null); - final progressHideTimer = useRef(null); - - final brightness = useBrightness(isLeftGesture.value); - final volume = useVolume(isRightGesture.value); - - final t = getLocalizations(context); - final rate = useAppStore().select(context, (state) => state.rate); - final shuffle = useAppStore().select(context, (state) => state.shuffle); - final fit = useAppStore().select(context, (state) => state.fit); - final autoResize = - useAppStore().select(context, (state) => state.autoResize); - - final isFullScreen = - useUiStore().select(context, (state) => state.isFullScreen); - - final playQueue = - usePlayQueueStore().select(context, (state) => state.playQueue); - final currentIndex = - usePlayQueueStore().select(context, (state) => state.currentIndex); - - final int currentPlayIndex = useMemoized( - () => playQueue.indexWhere((element) => element.index == currentIndex), - [playQueue, currentIndex]); - - final PlayQueueItem? currentPlay = useMemoized( - () => playQueue.isEmpty || currentPlayIndex < 0 - ? null - : playQueue[currentPlayIndex], - [playQueue, currentPlayIndex]); - - final title = useMemoized( - () => currentPlay != null - ? playQueue.length > 1 - ? '[${currentPlayIndex + 1}/${playQueue.length}] ${currentPlay.file.name}' - : currentPlay.file.name - : INFO.title, - [currentPlay, currentPlayIndex, playQueue]); - - final mediaType = useMemoized( - () => player.width == null || - player.height == null || - player.width == 0 || - player.height == 0 || - (currentPlay != null && - checkContentType(currentPlay.file.name) == - ContentType.audio) - ? MediaType.audio - : MediaType.video, - [player]); - - final contentColor = useMemoized( - () => Theme.of(context).brightness == Brightness.dark - ? Theme.of(context).colorScheme.onSurface - : Theme.of(context).colorScheme.surface, - [context]); - - final overlayColor = useMemoized( - () => - WidgetStateProperty.resolveWith((Set states) { - if (states.contains(WidgetState.pressed)) { - return contentColor.withValues(alpha: 0.2); - } else if (states.contains(WidgetState.hovered)) { - return contentColor.withValues(alpha: 0.2); - } - return null; - }), - [contentColor]); - - useEffect(() { - if (isDesktop) { - resizeWindow(!autoResize ? 0 : player.aspect); - } - return; - }, [player.aspect, autoResize]); - - final focusNode = useFocusNode(); - - useEffect(() { - focusNode.requestFocus(); - return; - }, []); - - final canPop = useState(false); - - useEffect(() { - final timer = Future.delayed(Duration(seconds: 4), () { - canPop.value = false; - }); - return () { - timer.ignore(); - }; - }, [canPop.value]); - - void startControlHideTimer() { - controlHideTimer.value = Timer( - const Duration(seconds: 5), - () { - if (isShowControl.value && !isHover.value) { - isShowControl.value = false; - } - }, - ); - } - - void startProgressHideTimer() { - progressHideTimer.value = Timer( - const Duration(seconds: 5), - () { - if (isShowProgress.value) { - isShowProgress.value = false; - } - }, - ); - } - - void resetControlHideTimer() { - controlHideTimer.value?.cancel(); - startControlHideTimer(); - } - - void resetBottomProgressTimer() { - progressHideTimer.value?.cancel(); - startProgressHideTimer(); - } - - void showControl() { - isShowControl.value = true; - isHover.value = false; - resetControlHideTimer(); - } - - void hideControl() { - isShowControl.value = false; - isHover.value = false; - controlHideTimer.value?.cancel(); - } - - Future showControlForHover(Future callback) async { - try { - player.saveProgress(); - showControl(); - isHover.value = true; - await callback; - showControl(); - } catch (e) { - logger(e.toString()); - } - } - - void showProgress() { - isShowProgress.value = true; - resetBottomProgressTimer(); - } - - useEffect(() { - startControlHideTimer(); - return () => controlHideTimer.value?.cancel(); - }, []); - - useEffect(() { - return () => progressHideTimer.value?.cancel(); - }, []); - - useEffect(() { - if (isDesktop) { - windowManager.setTitle(title); - } - return; - }, [title, player.isPlaying]); - - useEffect(() { - if (isShowControl.value || mediaType != MediaType.video) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - } else { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); - } - return; - }, [isShowControl.value, currentPlay?.file.type]); - - useEffect(() { - SystemChrome.setSystemUIChangeCallback((value) async { - if (value) { - showControl(); - } - }); - return () { - SystemChrome.setSystemUIChangeCallback(null); - }; - }, []); - - void onKeyEvent(KeyEvent event) async { - if (event.runtimeType == KeyDownEvent) { - if (HardwareKeyboard.instance.isAltPressed) { - switch (event.logicalKey) { - // 退出 - case LogicalKeyboardKey.keyX: - showControl(); - await player.saveProgress(); - exit(0); - } - return; - } - - if (HardwareKeyboard.instance.isControlPressed) { - switch (event.logicalKey) { - // 上一个 - case LogicalKeyboardKey.arrowLeft: - showControl(); - usePlayQueueStore().previous(); - break; - // 下一个 - case LogicalKeyboardKey.arrowRight: - showControl(); - usePlayQueueStore().next(); - break; - // 设置 - case LogicalKeyboardKey.keyP: - showControlForHover( - showPopup( - context: context, - child: const Settings(), - direction: PopupDirection.right, - ), - ); - break; - // 打开文件 - case LogicalKeyboardKey.keyO: - showControl(); - await pickLocalFile(); - showControl(); - break; - // 随机 - case LogicalKeyboardKey.keyX: - showControl(); - shuffle - ? usePlayQueueStore().sort() - : usePlayQueueStore().shuffle(); - useAppStore().updateShuffle(!shuffle); - break; - // 循环 - case LogicalKeyboardKey.keyR: - showControl(); - useAppStore().toggleRepeat(); - break; - // 视频缩放 - case LogicalKeyboardKey.keyV: - showControl(); - useAppStore().toggleFit(); - break; - // 历史 - case LogicalKeyboardKey.keyH: - showControlForHover( - showPopup( - context: context, - child: const History(), - direction: PopupDirection.right, - ), - ); - break; - // 打开链接 - case LogicalKeyboardKey.keyL: - showControl(); - isDesktop - ? await showOpenLinkDialog(context) - : await showOpenLinkBottomSheet(context); - showControl(); - break; - // 关闭当前播放媒体文件 - case LogicalKeyboardKey.keyC: - showControl(); - player.pause(); - usePlayQueueStore().updateCurrentIndex(-1); - break; - // 静音 - case LogicalKeyboardKey.keyM: - showControl(); - useAppStore().toggleMute(); - break; - default: - break; - } - return; - } - - switch (event.logicalKey) { - // 播放 | 暂停 - case LogicalKeyboardKey.space: - case LogicalKeyboardKey.mediaPlayPause: - showControl(); - if (player.isPlaying) { - player.pause(); - } else { - player.play(); - } - break; - // 上一个 - case LogicalKeyboardKey.mediaTrackPrevious: - usePlayQueueStore().previous(); - showControl(); - break; - // 下一个 - case LogicalKeyboardKey.mediaTrackNext: - showControl(); - usePlayQueueStore().next(); - break; - // 存储 - case LogicalKeyboardKey.keyF: - showControlForHover( - showPopup( - context: context, - child: const Storages(), - direction: PopupDirection.right, - ), - ); - break; - // 播放队列 - case LogicalKeyboardKey.keyP: - showControlForHover( - showPopup( - context: context, - child: const PlayQueue(), - direction: PopupDirection.right, - ), - ); - break; - // 字幕和音轨 - case LogicalKeyboardKey.keyS: - showControlForHover( - showPopup( - context: context, - child: SubtitleAndAudioTrack(player: player), - direction: PopupDirection.right, - ), - ); - break; - // 退出全屏 - case LogicalKeyboardKey.escape: - if (isDesktop && isFullScreen) { - useUiStore().updateFullScreen(false); - } - break; - // 全屏 - case LogicalKeyboardKey.enter: - case LogicalKeyboardKey.f11: - if (isDesktop) { - useUiStore().updateFullScreen(!isFullScreen); - } - break; - case LogicalKeyboardKey.tab: - showControl(); - break; - case LogicalKeyboardKey.f10: - showControl(); - await useUiStore().toggleIsAlwaysOnTop(); - break; - case LogicalKeyboardKey.equal: - await player.stepForward(); - break; - case LogicalKeyboardKey.minus: - await player.stepBackward(); - break; - case LogicalKeyboardKey.contextMenu: - showControl(); - moreMenuKey.currentState?.showButtonMenu(); - break; - default: - break; - } - } - - if (event.runtimeType == KeyDownEvent || - event.runtimeType == KeyRepeatEvent) { - switch (event.logicalKey) { - // 快退 - case LogicalKeyboardKey.arrowLeft: - if (isShowControl.value) { - showControl(); - } else { - showProgress(); - } - player.backward(10); - break; - // 快进 - case LogicalKeyboardKey.arrowRight: - if (isShowControl.value) { - showControl(); - } else { - showProgress(); - } - player.forward(10); - break; - // 提升音量 - case LogicalKeyboardKey.arrowUp: - showControl(); - await useAppStore().updateVolume(useAppStore().state.volume + 1); - break; - // 降低音量 - case LogicalKeyboardKey.arrowDown: - showControl(); - await useAppStore().updateVolume(useAppStore().state.volume - 1); - break; - default: - break; - } - } - } - - final scaleFactor = useMemoized( - () => - View.of(context).physicalSize.width / - MediaQuery.of(context).size.width, - [View.of(context).physicalSize.width, MediaQuery.of(context).size.width], - ); - - final videoViewSize = useMemoized(() { - if (fit != BoxFit.none || player.width == 0 || player.height == 0) { - return MediaQuery.of(context).size; - } else { - return Size(player.width! / scaleFactor, player.height! / scaleFactor); - } - }, [ - fit, - MediaQuery.of(context).size, - player.width, - player.height, - scaleFactor - ]); - - final videoViewOffset = useMemoized( - () => fit == BoxFit.none - ? Offset( - (MediaQuery.of(context).size.width - videoViewSize.width) / 2, - (MediaQuery.of(context).size.height - videoViewSize.height) / 2, - ) - : Offset(0, 0), - [fit, MediaQuery.of(context).size, videoViewSize]); - - return DropTarget( - onDragDone: (details) async { - final files = details.files - .map((file) => checkContentType(file.path) == ContentType.video || - checkContentType(file.path) == ContentType.audio - ? file.path - : null) - .where((element) => element != null) - .toList() as List; - if (files.isNotEmpty) { - final firstFile = files[0]; - if (firstFile.isEmpty) return; - final playQueue = await getLocalPlayQueue(firstFile); - if (playQueue == null || playQueue.playQueue.isEmpty) return; - final List filteredPlayQueue = []; - for (final item in playQueue.playQueue) { - final file = item.file; - if (files.contains(file.uri)) { - filteredPlayQueue.add(item); - } - } - if (filteredPlayQueue.isEmpty) return; - useAppStore().updateAutoPlay(true); - usePlayQueueStore().update( - playQueue: filteredPlayQueue, index: playQueue.currentIndex); - } - }, - child: PopScope( - canPop: false, - onPopInvokedWithResult: (bool didPop, Object? result) async { - if (!didPop) { - await player.saveProgress(); - if (!canPop.value) { - canPop.value = true; - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(t.exit_app_back_again)), - ); - } - } else { - exit(0); - } - } - }, - child: KeyboardListener( - focusNode: focusNode, - onKeyEvent: onKeyEvent, - child: Stack( - children: [ - // Video - Positioned( - left: 0, - top: 0, - right: 0, - bottom: 0, - child: MouseRegion( - cursor: isShowControl.value || player.isPlaying == false - ? SystemMouseCursors.basic - : SystemMouseCursors.none, - onHover: (event) { - if (event.kind != PointerDeviceKind.touch) { - showControl(); - } - }, - child: GestureDetector( - onTap: () { - if (isShowControl.value) { - hideControl(); - } else { - showControl(); - } - }, - onTapDown: (details) { - if (details.kind == PointerDeviceKind.touch) { - isTouch.value = true; - } - }, - onDoubleTapDown: (details) async { - if (details.kind == PointerDeviceKind.touch) { - double position = details.globalPosition.dx / - MediaQuery.of(context).size.width; - if (position > 0.75) { - if (isShowControl.value) { - showControl(); - } else { - showProgress(); - } - await player.forward(10); - } else if (position < 0.25) { - if (isShowControl.value) { - showControl(); - } else { - showProgress(); - } - player.backward(10); - } else { - if (player.isPlaying == true) { - player.pause(); - showControl(); - } else { - player.play(); - } - } - } else { - if (isDesktop) { - if (isFullScreen) { - await resizeWindow(player.aspect); - } - useUiStore().updateFullScreen(!isFullScreen); - } - } - }, - onLongPressStart: (details) { - if (isTouch.value && player.isPlaying == true) { - isLongPress.value = true; - useAppStore().updateRate(2.0); - } - }, - onLongPressMoveUpdate: (details) { - int fast = (details.offsetFromOrigin.dx / 50).toInt(); - if (fast >= 1) { - useAppStore() - .updateRate(fast > 4 ? 5.0 : (1 + fast).toDouble()); - } else if (fast <= -1) { - useAppStore().updateRate(fast < -3 - ? 0.25 - : (1 - 0.25 * fast.abs()).toDouble()); - } - }, - onLongPressEnd: (details) { - if (isLongPress.value) { - useAppStore().updateRate(1.0); - } - isLongPress.value = false; - isTouch.value = false; - }, - onLongPressCancel: () { - if (isLongPress.value) { - useAppStore().updateRate(1.0); - } - isLongPress.value = false; - isTouch.value = false; - }, - onPanStart: (details) async { - if (isDesktop && - details.kind != PointerDeviceKind.touch) { - windowManager.startDragging(); - } else if (details.kind == PointerDeviceKind.touch) { - isTouch.value = true; - startPosition.value = details.globalPosition; - } - }, - onPanUpdate: (details) async { - if (isTouch.value && startPosition.value != null) { - double dx = (details.globalPosition.dx - - startPosition.value!.dx) - .abs(); - double dy = (details.globalPosition.dy - - startPosition.value!.dy) - .abs(); - if (!isHorizontalGesture.value && - !isVerticalGesture.value) { - if (dx > dy) { - isHorizontalGesture.value = true; - player.updateSeeking(true); - } else { - isVerticalGesture.value = true; - } - } - - // 调整进度 - if (isHorizontalGesture.value && player.seeking) { - double dx = details.delta.dx; - int seconds = - (dx * 2 + player.position.inSeconds).toInt(); - Duration position = Duration( - seconds: seconds < 0 - ? 0 - : seconds > player.duration.inSeconds - ? player.duration.inSeconds - : seconds); - player.updatePosition(position); - if (isShowControl.value) { - showControl(); - } else { - showProgress(); - } - } - - // 亮度和音量 - final startDX = startPosition.value?.dx; - if (isVerticalGesture.value && startDX != null) { - if (!isLeftGesture.value && !isRightGesture.value) { - if (startDX < - (MediaQuery.of(context).size.width / 2)) { - isLeftGesture.value = true; - } else { - isRightGesture.value = true; - } - } - - double dy = details.delta.dy; - - if (isLeftGesture.value && brightness.value != null) { - final newBrightness = brightness.value! - dy / 200; - brightness.value = newBrightness > 1 - ? 1 - : newBrightness < 0 - ? 0 - : newBrightness; - } - - if (isRightGesture.value && volume.value != null) { - final newVolume = volume.value! - dy / 200; - volume.value = newVolume > 1 - ? 1 - : newVolume < 0 - ? 0 - : newVolume; - } - } - } - }, - onPanEnd: (details) async { - isTouch.value = false; - isHorizontalGesture.value = false; - isVerticalGesture.value = false; - isLeftGesture.value = false; - isRightGesture.value = false; - startPosition.value = null; - if (player.seeking) { - await player.seekTo(player.position); - player.updateSeeking(false); - } - }, - onPanCancel: () async { - isHorizontalGesture.value = false; - isVerticalGesture.value = false; - isLeftGesture.value = false; - isRightGesture.value = false; - startPosition.value = null; - if (player.seeking) { - isTouch.value = false; - await player.seekTo(player.position); - player.updateSeeking(false); - } - }, - child: Stack( - children: [ - Positioned( - left: 0, - top: 0, - right: 0, - bottom: 0, - child: Container( - color: Colors.black, - ), - ), - Positioned( - left: videoViewOffset.dx, - top: videoViewOffset.dy, - width: videoViewSize.width, - height: videoViewSize.height, - child: player is FvpPlayer - ? FittedBox( - fit: fit, - child: SizedBox( - width: player.width, - height: player.height, - child: VideoPlayer(player.controller), - ), - ) - : player is MediaKitPlayer - ? Video( - key: ValueKey(currentPlay?.file.uri), - controller: player.controller, - controls: NoVideoControls, - fit: fit == BoxFit.none - ? BoxFit.contain - : fit, - // wakelock: mediaType == 'video', - ) - : Container(), - ), - ], - ), - ), - ), - ), - // Audio - if (mediaType == MediaType.audio) - Positioned( - left: 0, - top: 0, - right: 0, - bottom: 0, - child: Audio(cover: cover)), - // 播放速度 - if (rate != 1.0 && isLongPress.value) - Positioned( - left: 0, - top: 0, - right: 0, - bottom: 0, - child: Center( - child: Container( - padding: const EdgeInsets.fromLTRB(12, 12, 18, 12), - decoration: BoxDecoration( - color: Colors.black54, - borderRadius: BorderRadius.circular(8), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Transform.translate( - offset: const Offset(0, 1.5), - child: Icon( - Icons.speed_rounded, - color: Colors.white, - size: 24, - ), - ), - const SizedBox(width: 10), - Text( - rate.toString(), - style: const TextStyle( - color: Colors.white, - fontSize: 20, - height: 1, - ), - ), - ], - ), - ), - ), - ), - // 屏幕亮度 - if (isLeftGesture.value && brightness.value != null) - Positioned( - left: 0, - top: 0, - right: 0, - bottom: 0, - child: Center( - child: Container( - padding: const EdgeInsets.fromLTRB(12, 12, 18, 12), - decoration: BoxDecoration( - color: Colors.black54, - borderRadius: BorderRadius.circular(8), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - brightness.value == 0 - ? Icons.brightness_low_rounded - : brightness.value! < 1 - ? Icons.brightness_medium_rounded - : Icons.brightness_high_rounded, - color: Colors.white, - size: 24, - ), - const SizedBox(width: 12), - SizedBox( - width: 100, - child: LinearProgressIndicator( - value: brightness.value, - borderRadius: BorderRadius.circular(4), - backgroundColor: Colors.grey, - valueColor: - AlwaysStoppedAnimation(Colors.white), - ), - ), - ], - ), - ), - ), - ), - // 音量 - if (isRightGesture.value && volume.value != null) - Positioned( - left: 0, - top: 0, - right: 0, - bottom: 0, - child: Center( - child: Container( - padding: const EdgeInsets.fromLTRB(12, 12, 18, 12), - decoration: BoxDecoration( - color: Colors.black54, - borderRadius: BorderRadius.circular(8), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - volume.value == 0 - ? Icons.volume_mute_rounded - : volume.value! < 0.5 - ? Icons.volume_down_rounded - : Icons.volume_up_rounded, - color: Colors.white, - size: 24, - ), - const SizedBox(width: 12), - SizedBox( - width: 100, - child: LinearProgressIndicator( - value: volume.value, - borderRadius: BorderRadius.circular(4), - backgroundColor: Colors.grey, - valueColor: AlwaysStoppedAnimation( - Colors.white, - ), - ), - ), - ], - ), - ), - ), - ), - if (isShowProgress.value && - !isShowControl.value && - mediaType != MediaType.audio) - Positioned( - left: -28, - right: -28, - bottom: -16, - height: 32, - child: ControlBarSlider( - player: player, - showControl: showControl, - disabled: true, - color: contentColor, - ), - ), - if (isShowProgress.value && - !isShowControl.value && - mediaType != MediaType.audio) - Positioned( - left: 12, - top: 12, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - currentPlay != null ? title : '', - style: TextStyle( - color: Colors.white, - fontSize: 20, - height: 1, - decoration: TextDecoration.none, - shadows: const [ - Shadow( - color: Colors.black, - offset: Offset(0, 0), - blurRadius: 1, - ), - ], - ), - ), - ], - ), - ), - if (isShowProgress.value && - !isShowControl.value && - mediaType != MediaType.audio) - Positioned( - left: 12, - bottom: 6, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '${formatDurationToMinutes(player.position)} / ${formatDurationToMinutes(player.duration)}', - style: TextStyle( - color: Colors.white, - fontSize: 16, - height: 2, - decoration: TextDecoration.none, - shadows: const [ - Shadow( - color: Colors.black, - offset: Offset(0, 0), - blurRadius: 1, - ), - ], - ), - ), - ], - ), - ), - // 标题栏 - AnimatedPositioned( - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOutCubicEmphasized, - top: isShowControl.value || mediaType != MediaType.video - ? 0 - : -72, - left: 0, - right: 0, - child: MouseRegion( - onHover: (event) { - if (event.kind != PointerDeviceKind.touch) { - isHover.value = true; - showControl(); - } - }, - child: GestureDetector( - onTap: () => showControl(), - onDoubleTap: () async { - if (isDesktop && await windowManager.isMaximized()) { - await windowManager.unmaximize(); - await resizeWindow(player.aspect); - } else { - await windowManager.maximize(); - } - }, - onPanStart: (details) async { - if (isDesktop) { - windowManager.startDragging(); - } - }, - child: TitleBar( - title: title, - actions: [const SizedBox(width: 8)], - color: contentColor, - overlayColor: overlayColor, - saveProgress: () => player.saveProgress(), - resizeWindow: () => resizeWindow(player.aspect), - ), - ), - ), - ), - // 控制栏 - AnimatedPositioned( - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOutCubicEmphasized, - bottom: isShowControl.value || mediaType != MediaType.video - ? 0 - : -96, - left: 0, - right: 0, - child: Align( - alignment: Alignment.bottomCenter, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: MouseRegion( - onHover: (event) { - if (event.kind != PointerDeviceKind.touch) { - isHover.value = true; - showControl(); - } - }, - child: GestureDetector( - onTap: () => showControl(), - child: ControlBar( - player: player, - showControl: showControl, - showControlForHover: showControlForHover, - color: contentColor, - overlayColor: overlayColor, - ), - ), - ), - ), - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/pages/player/overlays/controls_overlay.dart b/lib/pages/player/overlays/controls_overlay.dart new file mode 100644 index 0000000..b6c49d4 --- /dev/null +++ b/lib/pages/player/overlays/controls_overlay.dart @@ -0,0 +1,349 @@ +import 'dart:async'; +import 'package:flutter_zustand/flutter_zustand.dart'; +import 'package:iris/globals.dart' show speedStops, speedSelectorItemWidth; +import 'package:iris/hooks/use_gesture.dart'; +import 'package:iris/models/file.dart'; +import 'package:iris/models/player.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:iris/pages/player/control_bar/control_bar.dart'; +import 'package:iris/pages/player/control_bar/control_bar_slider.dart'; +import 'package:iris/pages/player/overlays/speed_selector.dart'; +import 'package:iris/store/use_app_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; +import 'package:iris/utils/format_duration_to_minutes.dart'; +import 'package:iris/widgets/drag_area.dart'; +import 'package:iris/widgets/title_bar.dart'; +import 'package:provider/provider.dart'; + +class ControlsOverlay extends HookWidget { + const ControlsOverlay({ + super.key, + required this.file, + required this.title, + required this.showControl, + required this.showControlForHover, + required this.hideControl, + required this.showProgress, + }); + + final FileItem? file; + final String title; + final Function() showControl; + final Future Function(Future callback) showControlForHover; + final Function() hideControl; + final Function() showProgress; + + @override + Widget build(BuildContext context) { + final isPlaying = + context.select((player) => player.isPlaying); + + final progress = + context.select( + (player) => (position: player.position, duration: player.duration), + ); + + final saveProgress = context.read().saveProgress; + + final isShowControl = + usePlayerUiStore().select(context, (state) => state.isShowControl); + final isShowProgress = + usePlayerUiStore().select(context, (state) => state.isShowProgress); + + final isSpeedSelectorVisible = useState(false); + final selectedSpeed = useState(1.0); + final speedSelectorPosition = useState(Offset.zero); + final visualOffset = useState(0.0); + final initialSpeed = useRef(1.0); + + void showSpeedSelectorCallback(Offset position) { + isSpeedSelectorVisible.value = true; + speedSelectorPosition.value = position; + visualOffset.value = 0.0; + initialSpeed.value = useAppStore().state.rate; + } + + void hideSpeedSelectorCallback(double finalSpeed) { + final initialIndex = speedStops.indexOf(initialSpeed.value); + final finalIndex = speedStops.indexOf(finalSpeed); + + if (initialIndex == -1 || finalIndex == -1) return; + + visualOffset.value = (initialIndex - finalIndex) * speedSelectorItemWidth; + + Future.delayed( + const Duration(milliseconds: 200), + () { + if (context.mounted) { + isSpeedSelectorVisible.value = false; + } + }, + ); + } + + void updateSelectedSpeedCallback(double speed, double newVisualOffset) { + selectedSpeed.value = speed; + visualOffset.value = newVisualOffset; + } + + final gesture = useGesture( + showControl: showControl, + hideControl: hideControl, + showProgress: showProgress, + showSpeedSelector: showSpeedSelectorCallback, + hideSpeedSelector: hideSpeedSelectorCallback, + updateSelectedSpeed: updateSelectedSpeedCallback, + ); + + final contentColor = useMemoized( + () => Theme.of(context).brightness == Brightness.dark + ? Theme.of(context).colorScheme.onSurface + : Theme.of(context).colorScheme.surface, + [context]); + + final overlayColor = useMemoized( + () => + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.pressed)) { + return contentColor.withValues(alpha: 0.2); + } else if (states.contains(WidgetState.hovered)) { + return contentColor.withValues(alpha: 0.2); + } + return null; + }), + [contentColor]); + + return Stack( + children: [ + Positioned.fill( + child: MouseRegion( + cursor: isShowControl || isPlaying == false + ? SystemMouseCursors.basic + : SystemMouseCursors.none, + onHover: gesture.onHover, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: gesture.onTap, + onTapDown: gesture.onTapDown, + onDoubleTapDown: gesture.onDoubleTapDown, + onLongPressStart: gesture.onLongPressStart, + onLongPressMoveUpdate: gesture.onLongPressMoveUpdate, + onLongPressEnd: gesture.onLongPressEnd, + onLongPressCancel: gesture.onLongPressCancel, + onPanStart: gesture.onPanStart, + onPanUpdate: gesture.onPanUpdate, + onPanEnd: gesture.onPanEnd, + onPanCancel: gesture.onPanCancel, + child: Stack( + children: [ + // 播放速度 + if (isSpeedSelectorVisible.value) + Positioned.fill( + child: SpeedSelector( + selectedSpeed: selectedSpeed.value, + visualOffset: visualOffset.value, + initialSpeed: initialSpeed.value, + ), + ), + // 屏幕亮度 + if (gesture.isLeftGesture && gesture.brightness != null) + Positioned.fill( + child: Center( + child: Container( + padding: const EdgeInsets.fromLTRB(12, 12, 18, 12), + decoration: BoxDecoration( + color: Colors.black54, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + gesture.brightness == 0 + ? Icons.brightness_low_rounded + : gesture.brightness! < 1 + ? Icons.brightness_medium_rounded + : Icons.brightness_high_rounded, + color: Colors.white, + size: 24, + ), + const SizedBox(width: 12), + SizedBox( + width: 100, + child: LinearProgressIndicator( + value: gesture.brightness, + borderRadius: BorderRadius.circular(4), + backgroundColor: Colors.grey, + valueColor: AlwaysStoppedAnimation( + Colors.white), + ), + ), + ], + ), + ), + ), + ), + // 音量 + if (gesture.isRightGesture && gesture.volume != null) + Positioned.fill( + child: Center( + child: Container( + padding: const EdgeInsets.fromLTRB(12, 12, 18, 12), + decoration: BoxDecoration( + color: Colors.black54, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + gesture.volume == 0 + ? Icons.volume_mute_rounded + : gesture.volume! < 0.5 + ? Icons.volume_down_rounded + : Icons.volume_up_rounded, + color: Colors.white, + size: 24, + ), + const SizedBox(width: 12), + SizedBox( + width: 100, + child: LinearProgressIndicator( + value: gesture.volume, + borderRadius: BorderRadius.circular(4), + backgroundColor: Colors.grey, + valueColor: AlwaysStoppedAnimation( + Colors.white, + ), + ), + ), + ], + ), + ), + ), + ), + if (isShowProgress && + !isShowControl && + file?.type == ContentType.video) + Positioned( + left: -28, + right: -28, + bottom: -16, + height: 32, + child: ControlBarSlider( + showControl: showControl, + disabled: true, + ), + ), + if (isShowProgress && + !isShowControl && + file?.type == ContentType.video) + Positioned( + left: 12, + top: 12, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + file != null ? title : '', + style: TextStyle( + color: Colors.white, + fontSize: 20, + height: 1, + decoration: TextDecoration.none, + shadows: const [ + Shadow( + color: Colors.black, + offset: Offset(0, 0), + blurRadius: 1, + ), + ], + ), + ), + ], + ), + ), + if (isShowProgress && + !isShowControl && + file?.type == ContentType.video) + Positioned( + left: 12, + bottom: 6, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${formatDurationToMinutes(progress.position)} / ${formatDurationToMinutes(progress.duration)}', + style: TextStyle( + color: Colors.white, + fontSize: 16, + height: 2, + decoration: TextDecoration.none, + shadows: const [ + Shadow( + color: Colors.black, + offset: Offset(0, 0), + blurRadius: 1, + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + // 标题栏 + AnimatedPositioned( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOutCubicEmphasized, + top: isShowControl || file?.type != ContentType.video ? 0 : -72, + left: 0, + right: 0, + child: MouseRegion( + onHover: gesture.onHover, + child: GestureDetector( + onTap: () => showControl(), + child: DragArea( + child: TitleBar( + title: title, + actions: [const SizedBox(width: 8)], + color: contentColor, + overlayColor: overlayColor, + saveProgress: () => saveProgress(), + ), + ), + ), + ), + ), + // 控制栏 + AnimatedPositioned( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOutCubicEmphasized, + bottom: isShowControl || file?.type != ContentType.video ? 0 : -128, + left: 0, + right: 0, + child: Align( + alignment: Alignment.bottomCenter, + child: MouseRegion( + onHover: gesture.onHover, + child: GestureDetector( + onTap: () => showControl(), + child: ControlBar( + showControl: showControl, + showControlForHover: showControlForHover, + color: contentColor, + overlayColor: overlayColor, + ), + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/pages/home/history.dart b/lib/pages/player/overlays/history.dart similarity index 96% rename from lib/pages/home/history.dart rename to lib/pages/player/overlays/history.dart index 7e5f8ba..ea9dcdb 100644 --- a/lib/pages/home/history.dart +++ b/lib/pages/player/overlays/history.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide Chip; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; import 'package:iris/models/file.dart'; @@ -11,7 +11,7 @@ import 'package:iris/store/use_history_store.dart'; import 'package:iris/store/use_play_queue_store.dart'; import 'package:iris/utils/file_size_convert.dart'; import 'package:iris/utils/get_localizations.dart'; -import 'package:iris/widgets/app_chip.dart'; +import 'package:iris/widgets/chip.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; class History extends HookWidget { @@ -84,14 +84,14 @@ class History extends HookWidget { if ((progress.duration.inMilliseconds - progress.position.inMilliseconds) <= 5000) { - return AppChip(text: '100%'); + return Chip(text: '100%'); } final String progressString = (progress.position.inMilliseconds / progress.duration.inMilliseconds * 100) .toStringAsFixed(0); - return AppChip(text: '$progressString %'); + return Chip(text: '$progressString %'); } else { return const SizedBox(); } @@ -109,7 +109,7 @@ class History extends HookWidget { mainAxisSize: MainAxisSize.min, children: [ const SizedBox(width: 4), - AppChip( + Chip( text: subtitleType, primary: true, ), diff --git a/lib/pages/player/play_queue.dart b/lib/pages/player/overlays/play_queue.dart similarity index 92% rename from lib/pages/player/play_queue.dart rename to lib/pages/player/overlays/play_queue.dart index 5328102..c77bd6c 100644 --- a/lib/pages/player/play_queue.dart +++ b/lib/pages/player/overlays/play_queue.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide Chip; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; import 'package:iris/models/file.dart'; @@ -9,7 +9,7 @@ import 'package:iris/store/use_history_store.dart'; import 'package:iris/store/use_play_queue_store.dart'; import 'package:iris/utils/file_size_convert.dart'; import 'package:iris/utils/get_localizations.dart'; -import 'package:iris/widgets/app_chip.dart'; +import 'package:iris/widgets/chip.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; class PlayQueue extends HookWidget { @@ -27,11 +27,13 @@ class PlayQueue extends HookWidget { () => playQueue.indexWhere((element) => element.index == currentIndex), [playQueue, currentIndex]); - ItemScrollController itemScrollController = ItemScrollController(); - ScrollOffsetController scrollOffsetController = ScrollOffsetController(); - ItemPositionsListener itemPositionsListener = - ItemPositionsListener.create(); - ScrollOffsetListener scrollOffsetListener = ScrollOffsetListener.create(); + final itemScrollController = useMemoized(() => ItemScrollController(), []); + final scrollOffsetController = + useMemoized(() => ScrollOffsetController(), []); + final itemPositionsListener = + useMemoized(() => ItemPositionsListener.create(), []); + final scrollOffsetListener = + useMemoized(() => ScrollOffsetListener.create(), []); useEffect(() { WidgetsBinding.instance.addPostFrameCallback((_) { @@ -99,14 +101,14 @@ class PlayQueue extends HookWidget { if ((progress.duration.inMilliseconds - progress.position.inMilliseconds) <= 5000) { - return AppChip(text: '100%'); + return Chip(text: '100%'); } final String progressString = (progress.position.inMilliseconds / progress.duration.inMilliseconds * 100) .toStringAsFixed(0); - return AppChip(text: '$progressString %'); + return Chip(text: '$progressString %'); } else { return const SizedBox(); } @@ -123,7 +125,7 @@ class PlayQueue extends HookWidget { mainAxisSize: MainAxisSize.min, children: [ const SizedBox(width: 4), - AppChip( + Chip( text: subtitleType, primary: true, ), diff --git a/lib/pages/player/overlays/speed_selector.dart b/lib/pages/player/overlays/speed_selector.dart new file mode 100644 index 0000000..7eac33b --- /dev/null +++ b/lib/pages/player/overlays/speed_selector.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:iris/globals.dart' show speedStops, speedSelectorItemWidth; + +class SpeedSelector extends HookWidget { + const SpeedSelector({ + super.key, + required this.selectedSpeed, + required this.visualOffset, + required this.initialSpeed, + }); + + final double selectedSpeed; + final double visualOffset; + final double initialSpeed; + + @override + Widget build(BuildContext context) { + final screenSize = MediaQuery.sizeOf(context); + + const double itemWidth = speedSelectorItemWidth; + const double horizontalPadding = 12.0; + + final initialIndex = speedStops.indexOf(initialSpeed); + final double initialCenterOffset = (screenSize.width / 2) - + (initialIndex * itemWidth) - + (itemWidth / 2) - + horizontalPadding; + + final double targetOffset = initialCenterOffset + visualOffset; + + final double topPosition = screenSize.height / 2 - 30; + + return IgnorePointer( + child: Stack( + children: [ + AnimatedPositioned( + duration: const Duration(milliseconds: 150), + curve: Curves.easeOutCubic, + left: targetOffset, + top: topPosition, + child: Container( + height: 60, + decoration: BoxDecoration( + color: Colors.black54, + borderRadius: BorderRadius.circular(30), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10, + spreadRadius: 2, + ) + ], + ), + child: Row( + children: [ + const SizedBox(width: horizontalPadding), + ...speedStops.map((speed) { + final bool isSelected = speed == selectedSpeed; + return SizedBox( + width: itemWidth, + child: Center( + child: AnimatedDefaultTextStyle( + duration: const Duration(milliseconds: 150), + style: TextStyle( + fontSize: isSelected ? 20 : 16, + fontWeight: isSelected + ? FontWeight.bold + : FontWeight.normal, + color: isSelected ? Colors.white : Colors.white70, + height: 1.0, + ), + child: Text('${speed}x'), + ), + ), + ); + }), + const SizedBox(width: horizontalPadding), + ], + ), + ), + ), + Positioned( + left: screenSize.width / 2 - 1.5, + top: topPosition - 10, + child: Container( + width: 3, + height: 80, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(2), + boxShadow: [ + BoxShadow( + color: Colors.white.withAlpha(100), + blurRadius: 5, + ) + ]), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/player/audio_track_list.dart b/lib/pages/player/overlays/track/audio_track_list.dart similarity index 59% rename from lib/pages/player/audio_track_list.dart rename to lib/pages/player/overlays/track/audio_track_list.dart index 128c3e6..5a8e3e3 100644 --- a/lib/pages/player/audio_track_list.dart +++ b/lib/pages/player/overlays/track/audio_track_list.dart @@ -5,16 +5,17 @@ import 'package:iris/models/player.dart'; import 'package:iris/utils/get_localizations.dart'; import 'package:iris/utils/logger.dart'; import 'package:media_kit/media_kit.dart'; +import 'package:provider/provider.dart'; class AudioTrackList extends HookWidget { - const AudioTrackList({super.key, required this.player}); - - final MediaPlayer player; + const AudioTrackList({super.key}); @override Widget build(BuildContext context) { final t = getLocalizations(context); + final player = context.read(); + final focusNode = useFocusNode(); useEffect(() { @@ -25,47 +26,41 @@ class AudioTrackList extends HookWidget { if (player is MediaKitPlayer) { return ListView( children: [ - ...(player as MediaKitPlayer).audios.map( - (audio) => ListTile( - focusNode: (player as MediaKitPlayer).audio == audio - ? focusNode - : null, - title: Text( - audio == AudioTrack.auto() - ? t.auto - : audio == AudioTrack.no() - ? t.off - : audio.title ?? audio.language ?? audio.id, - style: (player as MediaKitPlayer).audio == audio - ? TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.primary, - ) - : TextStyle( - color: - Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - tileColor: (player as MediaKitPlayer).audio == audio - ? Theme.of(context).hoverColor - : null, - onTap: () { - logger( - 'Set audio track: ${audio.title ?? audio.language ?? audio.id}'); - (player as MediaKitPlayer).player.setAudioTrack(audio); - Navigator.of(context).pop(); - }, - ), + ...player.audios.map( + (audio) => ListTile( + focusNode: player.audio == audio ? focusNode : null, + title: Text( + audio == AudioTrack.auto() + ? t.auto + : audio == AudioTrack.no() + ? t.off + : audio.title ?? audio.language ?? audio.id, + style: player.audio == audio + ? TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ) + : TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), + tileColor: + player.audio == audio ? Theme.of(context).hoverColor : null, + onTap: () { + logger( + 'Set audio track: ${audio.title ?? audio.language ?? audio.id}'); + player.player.setAudioTrack(audio); + Navigator.of(context).pop(); + }, + ), + ), ], ); } if (player is FvpPlayer) { - final audios = - (player as FvpPlayer).controller.getMediaInfo()?.audio ?? []; - final activeAudioTracks = - (player as FvpPlayer).controller.getActiveAudioTracks() ?? []; + final audios = player.controller.getMediaInfo()?.audio ?? []; + final activeAudioTracks = player.controller.getActiveAudioTracks() ?? []; return ListView( children: [ ListTile( @@ -85,7 +80,7 @@ class AudioTrackList extends HookWidget { activeAudioTracks.isEmpty ? Theme.of(context).hoverColor : null, onTap: () { logger('Set audio track: ${t.off}'); - (player as FvpPlayer).controller.setAudioTracks([]); + player.controller.setAudioTracks([]); Navigator.of(context).pop(); }, ), @@ -113,9 +108,7 @@ class AudioTrackList extends HookWidget { onTap: () { logger( 'Set audio track: ${audio.metadata['title'] ?? audio.metadata['language'] ?? audios.indexOf(audio).toString()}'); - (player as FvpPlayer) - .controller - .setAudioTracks([audios.indexOf(audio)]); + player.controller.setAudioTracks([audios.indexOf(audio)]); Navigator.of(context).pop(); }, ), diff --git a/lib/pages/player/subtitle_and_audio_track.dart b/lib/pages/player/overlays/track/subtitle_and_audio_track.dart similarity index 84% rename from lib/pages/player/subtitle_and_audio_track.dart rename to lib/pages/player/overlays/track/subtitle_and_audio_track.dart index 21d5605..dc5ea12 100644 --- a/lib/pages/player/subtitle_and_audio_track.dart +++ b/lib/pages/player/overlays/track/subtitle_and_audio_track.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:iris/models/player.dart'; -import 'package:iris/pages/player/audio_track_list.dart'; -import 'package:iris/pages/player/subtitle_list.dart'; +import 'package:iris/pages/player/overlays/track/audio_track_list.dart'; +import 'package:iris/pages/player/overlays/track/subtitle_list.dart'; import 'package:iris/utils/get_localizations.dart'; class ITab { @@ -16,17 +15,15 @@ class ITab { } class SubtitleAndAudioTrack extends HookWidget { - const SubtitleAndAudioTrack({super.key, required this.player}); - - final MediaPlayer player; + const SubtitleAndAudioTrack({super.key}); @override Widget build(BuildContext context) { final t = getLocalizations(context); List tabs = [ - ITab(title: t.subtitle, child: SubtitleList(player: player)), - ITab(title: t.audio_track, child: AudioTrackList(player: player)), + ITab(title: t.subtitle, child: SubtitleList()), + ITab(title: t.audio_track, child: AudioTrackList()), ]; final tabController = useTabController(initialLength: tabs.length); diff --git a/lib/pages/player/overlays/track/subtitle_list.dart b/lib/pages/player/overlays/track/subtitle_list.dart new file mode 100644 index 0000000..ee93c93 --- /dev/null +++ b/lib/pages/player/overlays/track/subtitle_list.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_zustand/flutter_zustand.dart'; +import 'package:fvp/fvp.dart'; +import 'package:iris/models/file.dart'; +import 'package:iris/models/player.dart'; +import 'package:iris/models/storages/storage.dart'; +import 'package:iris/store/use_play_queue_store.dart'; +import 'package:iris/utils/get_localizations.dart'; +import 'package:iris/utils/logger.dart'; +import 'package:media_kit/media_kit.dart'; +import 'package:media_stream/media_stream.dart'; +import 'package:provider/provider.dart'; + +class SubtitleList extends HookWidget { + const SubtitleList({super.key}); + + @override + Widget build(BuildContext context) { + final t = getLocalizations(context); + final focusNode = useFocusNode(); + + final player = context.watch(); + + final playQueue = + usePlayQueueStore().select(context, (state) => state.playQueue); + final currentIndex = + usePlayQueueStore().select(context, (state) => state.currentIndex); + + final int currentPlayIndex = useMemoized( + () => playQueue.indexWhere((element) => element.index == currentIndex), + [playQueue, currentIndex]); + + final file = useMemoized( + () => playQueue.isEmpty || currentPlayIndex < 0 + ? null + : playQueue[currentPlayIndex].file, + [playQueue, currentPlayIndex]); + + useEffect(() { + focusNode.requestFocus(); + return focusNode.unfocus; + }, []); + + if (player is MediaKitPlayer) { + final activeSubtitle = context.select( + (p) => p is MediaKitPlayer ? p.subtitle : SubtitleTrack.no()); + final subtitles = context.select>( + (p) => p is MediaKitPlayer ? p.subtitles : []); + final externalSubtitles = context.select>( + (p) => p is MediaKitPlayer ? p.externalSubtitles : []); + + return ListView( + children: [ + ...subtitles.map((subtitle) { + final bool isActive = activeSubtitle == subtitle; + return ListTile( + focusNode: isActive ? focusNode : null, + title: Text( + subtitle == SubtitleTrack.no() + ? t.off + : subtitle.title ?? subtitle.language ?? subtitle.id, + style: isActive + ? TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary) + : TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + tileColor: isActive ? Theme.of(context).hoverColor : null, + onTap: () { + logger('Set subtitle: ${subtitle.id}'); + player.player.setSubtitleTrack(subtitle); + Navigator.of(context).pop(); + }, + ); + }), + ...externalSubtitles.map((subtitle) { + return ListTile( + title: Text(subtitle.name, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant)), + onTap: () { + logger('Set external subtitle: $subtitle'); + final mediaStream = MediaStream(); + final uri = file?.storageType == StorageType.ftp + ? '${mediaStream.url}/${subtitle.uri}' + : subtitle.uri; + player.player.setSubtitleTrack( + SubtitleTrack.uri(uri, title: subtitle.name)); + Navigator.of(context).pop(); + }, + ); + }), + ], + ); + } + + if (player is FvpPlayer) { + final activeSubtitles = useListenableSelector(player.controller, + () => player.controller.getActiveSubtitleTracks() ?? []); + final externalSubtitleIndex = useValueListenable(player.externalSubtitle); + + final subtitles = player.controller.getMediaInfo()?.subtitle ?? []; + final externalSubtitles = player.externalSubtitles; + + return ListView( + children: [ + ListTile( + selected: externalSubtitleIndex == null && activeSubtitles.isEmpty, + focusNode: externalSubtitleIndex == null && activeSubtitles.isEmpty + ? focusNode + : null, + title: Text(t.off), + onTap: () { + logger('Set subtitle: ${t.off}'); + player.externalSubtitle.value = null; + player.controller.setSubtitleTracks([]); + Navigator.of(context).pop(); + }, + ), + ...subtitles.map((subtitle) { + final int index = subtitles.indexOf(subtitle); + final bool isActive = externalSubtitleIndex == null && + activeSubtitles.contains(index); + return ListTile( + selected: isActive, + focusNode: isActive ? focusNode : null, + title: Text(subtitle.metadata['title'] ?? + subtitle.metadata['language'] ?? + subtitle.index.toString()), + onTap: () { + player.externalSubtitle.value = null; + player.controller.setSubtitleTracks([index]); + Navigator.of(context).pop(); + }, + ); + }), + ...externalSubtitles.map((subtitle) { + final int index = externalSubtitles.indexOf(subtitle); + final bool isActive = externalSubtitleIndex == index; + return ListTile( + selected: isActive, + focusNode: isActive ? focusNode : null, + title: Text(subtitle.name), + onTap: () { + player.externalSubtitle.value = index; + Navigator.of(context).pop(); + }, + ); + }), + ], + ); + } + + return Container(); + } +} diff --git a/lib/pages/player/player.dart b/lib/pages/player/player.dart new file mode 100644 index 0000000..f6966e9 --- /dev/null +++ b/lib/pages/player/player.dart @@ -0,0 +1,263 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:desktop_drop/desktop_drop.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_zustand/flutter_zustand.dart'; +import 'package:iris/hooks/use_app_lifecycle.dart'; +import 'package:iris/hooks/use_cover.dart'; +import 'package:iris/hooks/use_keyboard.dart'; +import 'package:iris/info.dart'; +import 'package:iris/models/file.dart'; +import 'package:iris/models/player.dart'; +import 'package:iris/models/storages/local.dart'; +import 'package:iris/pages/player/audio.dart'; +import 'package:iris/pages/player/overlays/controls_overlay.dart'; +import 'package:iris/pages/player/video_view.dart'; +import 'package:iris/store/use_player_ui_store.dart'; +import 'package:iris/utils/check_content_type.dart'; +import 'package:iris/utils/logger.dart'; +import 'package:iris/utils/platform.dart'; +import 'package:iris/store/use_app_store.dart'; +import 'package:iris/store/use_play_queue_store.dart'; +import 'package:provider/provider.dart'; +import 'package:window_manager/window_manager.dart'; + +class Player extends HookWidget { + const Player({super.key}); + + @override + Widget build(BuildContext context) { + final width = context.select((player) => player.width); + final height = + context.select((player) => player.height); + + final saveProgress = context.read().saveProgress; + + useAppLifecycle(); + + final cover = useCover(); + + final controlHideTimer = useRef(null); + final progressHideTimer = useRef(null); + + final fit = useAppStore().select(context, (state) => state.fit); + + final playQueue = + usePlayQueueStore().select(context, (state) => state.playQueue); + final currentIndex = + usePlayQueueStore().select(context, (state) => state.currentIndex); + + final int currentPlayIndex = useMemoized( + () => playQueue.indexWhere((element) => element.index == currentIndex), + [playQueue, currentIndex]); + + final FileItem? file = useMemoized( + () => playQueue.isEmpty || currentPlayIndex < 0 + ? null + : playQueue[currentPlayIndex].file, + [playQueue, currentPlayIndex]); + + final title = useMemoized( + () => file != null + ? playQueue.length > 1 + ? '[${currentPlayIndex + 1}/${playQueue.length}] ${file.name}' + : file.name + : INFO.title, + [file, currentPlayIndex, playQueue]); + + final focusNode = useFocusNode(); + + useEffect(() { + focusNode.requestFocus(); + return; + }, []); + + void startControlHideTimer() { + controlHideTimer.value = Timer( + const Duration(seconds: 5), + () { + if (usePlayerUiStore().state.isShowControl && + !usePlayerUiStore().state.isHovering) { + usePlayerUiStore().updateIsShowControl(false); + } + }, + ); + } + + void startProgressHideTimer() { + progressHideTimer.value = Timer( + const Duration(seconds: 5), + () { + if (usePlayerUiStore().state.isShowProgress) { + usePlayerUiStore().updateIsShowProgress(false); + } + }, + ); + } + + void resetControlHideTimer() { + controlHideTimer.value?.cancel(); + startControlHideTimer(); + } + + void resetBottomProgressTimer() { + progressHideTimer.value?.cancel(); + startProgressHideTimer(); + } + + void showControl() { + usePlayerUiStore().updateIsShowControl(true); + usePlayerUiStore().updateIsHovering(false); + resetControlHideTimer(); + } + + void hideControl() { + usePlayerUiStore().updateIsShowControl(false); + usePlayerUiStore().updateIsHovering(false); + controlHideTimer.value?.cancel(); + } + + Future showControlForHover(Future callback) async { + try { + saveProgress(); + showControl(); + usePlayerUiStore().updateIsHovering(true); + await callback; + showControl(); + } catch (e) { + logger(e.toString()); + } + } + + void showProgress() { + usePlayerUiStore().updateIsShowProgress(true); + resetBottomProgressTimer(); + } + + final onKeyEvent = useKeyboard( + showControl: showControl, + showControlForHover: showControlForHover, + showProgress: showProgress, + ); + + useEffect(() { + startControlHideTimer(); + return () => controlHideTimer.value?.cancel(); + }, []); + + useEffect(() { + return () => progressHideTimer.value?.cancel(); + }, []); + + useEffect(() { + if (isDesktop) { + windowManager.setTitle(title); + } + return; + }, [title]); + + final Size windowSize = useMemoized( + () => MediaQuery.sizeOf(context), [MediaQuery.sizeOf(context)]); + + final scaleFactor = useMemoized( + () => View.of(context).physicalSize.width / windowSize.width, + [View.of(context).physicalSize.width], + ); + + final videoViewSize = useMemoized(() { + if (fit != BoxFit.none || width == 0 || height == 0) { + return windowSize; + } else { + return Size(width / scaleFactor, height / scaleFactor); + } + }, [fit, windowSize, width, height, scaleFactor]); + + final videoViewOffset = useMemoized( + () => fit == BoxFit.none + ? Offset( + (windowSize.width - videoViewSize.width) / 2, + (windowSize.height - videoViewSize.height) / 2, + ) + : Offset(0, 0), + [fit, windowSize, videoViewSize]); + + return DropTarget( + onDragDone: (details) async { + final files = details.files + .map((file) => checkContentType(file.path) == ContentType.video || + checkContentType(file.path) == ContentType.audio + ? file.path + : null) + .where((element) => element != null) + .toList() as List; + if (files.isNotEmpty) { + final firstFile = files[0]; + if (firstFile.isEmpty) return; + final playQueue = await getLocalPlayQueue(firstFile); + if (playQueue == null || playQueue.playQueue.isEmpty) return; + final List filteredPlayQueue = []; + for (final item in playQueue.playQueue) { + final file = item.file; + if (files.contains(file.uri)) { + filteredPlayQueue.add(item); + } + } + if (filteredPlayQueue.isEmpty) return; + useAppStore().updateAutoPlay(true); + usePlayQueueStore().update( + playQueue: filteredPlayQueue, index: playQueue.currentIndex); + } + }, + child: PopScope( + canPop: false, + onPopInvokedWithResult: (bool didPop, Object? result) async { + if (!didPop) { + await saveProgress(); + if (isDesktop) { + windowManager.close(); + } else { + SystemNavigator.pop(); + exit(0); + } + } + }, + child: KeyboardListener( + focusNode: focusNode, + onKeyEvent: onKeyEvent, + child: Stack( + children: [ + // Video + Positioned( + left: videoViewOffset.dx, + top: videoViewOffset.dy, + width: videoViewSize.width, + height: videoViewSize.height, + child: VideoView( + key: ValueKey(file?.uri), + fit: fit, + ), + ), + // Audio + if (file?.type == ContentType.audio) + Positioned.fill( + child: Audio(cover: cover), + ), + Positioned.fill( + child: ControlsOverlay( + file: file, + title: title, + showControl: showControl, + showControlForHover: showControlForHover, + hideControl: hideControl, + showProgress: showProgress, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/player/player_view.dart b/lib/pages/player/player_view.dart new file mode 100644 index 0000000..784e6b9 --- /dev/null +++ b/lib/pages/player/player_view.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:iris/hooks/player/use_fvp_player.dart'; +import 'package:iris/hooks/player/use_media_kit_player.dart'; +import 'package:iris/models/player.dart'; +import 'package:iris/models/store/app_state.dart'; +import 'package:iris/pages/player/player.dart'; +import 'package:provider/provider.dart'; + +class PlayerView extends HookWidget { + const PlayerView({super.key, required this.playerBackend}); + + final PlayerBackend playerBackend; + + @override + Widget build(BuildContext context) { + switch (playerBackend) { + case PlayerBackend.mediaKit: + return const _MediaKitPlayerHost(); + case PlayerBackend.fvp: + return const _FvpPlayerHost(); + } + } +} + +class _MediaKitPlayerHost extends HookWidget { + const _MediaKitPlayerHost(); + + @override + Widget build(BuildContext context) { + final player = useMediaKitPlayer(context); + return Provider.value( + value: player, + child: const Player(key: ValueKey('media_kit_player')), + ); + } +} + +class _FvpPlayerHost extends HookWidget { + const _FvpPlayerHost(); + + @override + Widget build(BuildContext context) { + final player = useFvpPlayer(context); + return Provider.value( + value: player, + child: const Player(key: ValueKey('fvp_player')), + ); + } +} diff --git a/lib/pages/player/subtitle_list.dart b/lib/pages/player/subtitle_list.dart deleted file mode 100644 index 36b2bbb..0000000 --- a/lib/pages/player/subtitle_list.dart +++ /dev/null @@ -1,228 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:flutter_zustand/flutter_zustand.dart'; -import 'package:fvp/fvp.dart'; -import 'package:iris/models/file.dart'; -import 'package:iris/models/player.dart'; -import 'package:iris/models/storages/storage.dart'; -import 'package:iris/store/use_play_queue_store.dart'; -import 'package:iris/utils/get_localizations.dart'; -import 'package:iris/utils/logger.dart'; -import 'package:media_kit/media_kit.dart'; -import 'package:media_stream/media_stream.dart'; - -class SubtitleList extends HookWidget { - const SubtitleList({super.key, required this.player}); - - final MediaPlayer player; - - @override - Widget build(BuildContext context) { - final t = getLocalizations(context); - - final focusNode = useFocusNode(); - - MediaStream mediaStream = MediaStream(); - final streamUrl = mediaStream.url; - - final playQueue = - usePlayQueueStore().select(context, (state) => state.playQueue); - final currentIndex = - usePlayQueueStore().select(context, (state) => state.currentIndex); - - final int currentPlayIndex = useMemoized( - () => playQueue.indexWhere((element) => element.index == currentIndex), - [playQueue, currentIndex]); - - final PlayQueueItem? currentPlay = useMemoized( - () => playQueue.isEmpty || currentPlayIndex < 0 - ? null - : playQueue[currentPlayIndex], - [playQueue, currentPlayIndex]); - - useEffect(() { - focusNode.requestFocus(); - return () => focusNode.unfocus(); - }, []); - - if (player is MediaKitPlayer) { - return ListView( - children: [ - ...(player as MediaKitPlayer).subtitles.map( - (subtitle) => ListTile( - focusNode: (player as MediaKitPlayer).subtitle == subtitle - ? focusNode - : null, - title: Text( - subtitle == SubtitleTrack.no() - ? t.off - : subtitle.title ?? subtitle.language ?? subtitle.id, - style: (player as MediaKitPlayer).subtitle == subtitle - ? TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.primary, - ) - : TextStyle( - color: - Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - tileColor: (player as MediaKitPlayer).subtitle == subtitle - ? Theme.of(context).hoverColor - : null, - onTap: () { - logger( - 'Set subtitle: ${subtitle.title ?? subtitle.language ?? subtitle.id}'); - (player as MediaKitPlayer) - .player - .setSubtitleTrack(subtitle); - Navigator.of(context).pop(); - }, - ), - ), - ...(player as MediaKitPlayer).externalSubtitles.map( - (subtitle) => ListTile( - title: Text( - subtitle.name, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - onTap: () { - logger('Set external subtitle: $subtitle'); - final uri = currentPlay?.file.storageType == StorageType.ftp - ? '$streamUrl/${subtitle.uri}' - : subtitle.uri; - logger('External subtitle uri: $uri'); - (player as MediaKitPlayer).player.setSubtitleTrack( - SubtitleTrack.uri( - uri, - title: subtitle.name, - ), - ); - Navigator.of(context).pop(); - }, - ), - ), - ], - ); - } - - if (player is FvpPlayer) { - final subtitles = - (player as FvpPlayer).controller.getMediaInfo()?.subtitle ?? []; - final activeSubtitles = - (player as FvpPlayer).controller.getActiveSubtitleTracks() ?? []; - return ListView( - children: [ - ListTile( - focusNode: (player as FvpPlayer).externalSubtitle.value == null && - activeSubtitles.isEmpty - ? focusNode - : null, - title: Text( - t.off, - style: (player as FvpPlayer).externalSubtitle.value == null && - activeSubtitles.isEmpty - ? TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.primary, - ) - : TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - tileColor: (player as FvpPlayer).externalSubtitle.value == null && - activeSubtitles.isEmpty - ? Theme.of(context).hoverColor - : null, - onTap: () { - logger('Set subtitle: ${t.off}'); - (player as FvpPlayer).externalSubtitle.value = null; - (player as FvpPlayer).controller.setSubtitleTracks([]); - Navigator.of(context).pop(); - }, - ), - ...subtitles.map( - (subtitle) => ListTile( - focusNode: (player as FvpPlayer).externalSubtitle.value == null && - activeSubtitles.contains(subtitles.indexOf(subtitle)) - ? focusNode - : null, - title: Text( - subtitle.metadata['title'] ?? - subtitle.metadata['language'] ?? - subtitle.index.toString(), - style: (player as FvpPlayer).externalSubtitle.value == null && - activeSubtitles.contains(subtitles.indexOf(subtitle)) - ? TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.primary, - ) - : TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - tileColor: (player as FvpPlayer).externalSubtitle.value == null && - activeSubtitles.contains(subtitles.indexOf(subtitle)) - ? Theme.of(context).hoverColor - : null, - onTap: () { - logger( - 'Set subtitle: ${subtitle.metadata['title'] ?? subtitle.metadata['language'] ?? subtitle.index.toString()}'); - (player as FvpPlayer).externalSubtitle.value = null; - (player as FvpPlayer) - .controller - .setSubtitleTracks([subtitles.indexOf(subtitle)]); - Navigator.of(context).pop(); - }, - ), - ), - ...(player as FvpPlayer).externalSubtitles.map( - (subtitle) => ListTile( - focusNode: (player as FvpPlayer).externalSubtitle.value == - (player as FvpPlayer) - .externalSubtitles - .indexOf(subtitle) - ? focusNode - : null, - title: (player as FvpPlayer).externalSubtitle.value == - (player as FvpPlayer) - .externalSubtitles - .indexOf(subtitle) - ? Text( - subtitle.name, - style: TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.primary), - ) - : Text( - subtitle.name, - style: TextStyle( - color: - Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - tileColor: (player as FvpPlayer).externalSubtitle.value == - (player as FvpPlayer) - .externalSubtitles - .indexOf(subtitle) - ? Theme.of(context).hoverColor - : null, - onTap: () { - logger('Set external subtitle: $subtitle'); - (player as FvpPlayer).externalSubtitle.value = - (player as FvpPlayer) - .externalSubtitles - .indexOf(subtitle); - Navigator.of(context).pop(); - }, - ), - ), - ], - ); - } - - return Container(); - } -} diff --git a/lib/pages/player/video_view.dart b/lib/pages/player/video_view.dart new file mode 100644 index 0000000..d85b474 --- /dev/null +++ b/lib/pages/player/video_view.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:iris/models/player.dart'; +import 'package:media_kit_video/media_kit_video.dart'; +import 'package:provider/provider.dart'; +import 'package:video_player/video_player.dart'; + +class VideoView extends HookWidget { + const VideoView({ + super.key, + required this.fit, + }); + + final BoxFit fit; + + @override + Widget build(BuildContext context) { + final player = context.read(); + + return switch (player) { + MediaKitPlayer player => Video( + controller: player.controller, + controls: NoVideoControls, + fit: fit == BoxFit.none ? BoxFit.contain : fit, + ), + FvpPlayer player => FittedBox( + fit: fit, + child: SizedBox( + width: player.width, + height: player.height, + child: VideoPlayer(player.controller), + ), + ), + _ => Container(), + }; + } +} diff --git a/lib/pages/settings/about.dart b/lib/pages/settings/about.dart index 2325185..93cf20f 100644 --- a/lib/pages/settings/about.dart +++ b/lib/pages/settings/about.dart @@ -31,7 +31,7 @@ class About extends HookWidget { children: [ ListTile( leading: - Image.asset('assets/images/icon.png', width: 24, height: 24), + Image.asset('assets/images/logo.png', width: 24, height: 24), title: const Text(INFO.title), subtitle: Text(t.app_description), ), diff --git a/lib/pages/settings/libraries.dart b/lib/pages/settings/dependencies.dart similarity index 96% rename from lib/pages/settings/libraries.dart rename to lib/pages/settings/dependencies.dart index 193e914..a04ea79 100644 --- a/lib/pages/settings/libraries.dart +++ b/lib/pages/settings/dependencies.dart @@ -3,8 +3,8 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:iris/oss_licenses.dart'; import 'package:iris/utils/url.dart'; -class Libraries extends HookWidget { - const Libraries({super.key}); +class Dependencies extends HookWidget { + const Dependencies({super.key}); static const title = 'Libraries'; diff --git a/lib/pages/settings/play.dart b/lib/pages/settings/play.dart index 51d9198..7f77b2b 100644 --- a/lib/pages/settings/play.dart +++ b/lib/pages/settings/play.dart @@ -48,8 +48,7 @@ class Play extends HookWidget { DropdownMenuItem( value: PlayerBackend.mediaKit, child: Text('Media Kit')), DropdownMenuItem( - value: PlayerBackend.fvp, - child: Text('FVP (${t.experimental})')), + value: PlayerBackend.fvp, child: Text('FVP')), ], )), Visibility( diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index 02cfdd6..8066735 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:iris/pages/settings/about.dart'; import 'package:iris/pages/settings/general.dart'; -import 'package:iris/pages/settings/libraries.dart'; +import 'package:iris/pages/settings/dependencies.dart'; import 'package:iris/pages/settings/play.dart'; import 'package:iris/utils/get_localizations.dart'; @@ -29,7 +29,7 @@ class Settings extends HookWidget { ITab(title: t.general, child: const General()), ITab(title: t.play, child: const Play()), ITab(title: t.about, child: const About()), - ITab(title: t.libraries, child: const Libraries()), + ITab(title: t.dependencies, child: const Dependencies()), ]; final tabController = useTabController(initialLength: tabs.length); diff --git a/lib/pages/storages/favorites.dart b/lib/pages/storages/favorites.dart index 740079b..bf1b611 100644 --- a/lib/pages/storages/favorites.dart +++ b/lib/pages/storages/favorites.dart @@ -47,7 +47,7 @@ class Favorites extends HookWidget { ); } else if (storage is WebDAVStorage) { return Text( - 'http${storage.https ? 's' : ''}://${storage.host}${favorites[index].path.join('/')}'); + 'http${storage.https ? 's' : ''}://${storage.host}${storage.port.isNotEmpty && storage.port != '80' && storage.port != '443' ? ':${storage.port}' : ''}${favorites[index].path.join('/')}'); } else if (storage is FTPStorage) { return Text( 'ftp://${storage.username.isNotEmpty ? '${storage.username}@' : ''}${storage.host}:${storage.port}${favorites[index].path.join('/').replaceFirst('//', '/')}'); diff --git a/lib/pages/storages/files.dart b/lib/pages/storages/files.dart index 56e9e7e..904f96d 100644 --- a/lib/pages/storages/files.dart +++ b/lib/pages/storages/files.dart @@ -1,6 +1,6 @@ import 'dart:io'; import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide Chip; import 'package:flutter_breadcrumb/flutter_breadcrumb.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; @@ -14,12 +14,11 @@ import 'package:iris/store/use_app_store.dart'; import 'package:iris/store/use_history_store.dart'; import 'package:iris/store/use_play_queue_store.dart'; import 'package:iris/store/use_storage_store.dart'; -import 'package:iris/utils/files_filter.dart'; import 'package:iris/utils/file_size_convert.dart'; import 'package:iris/utils/files_sort.dart'; import 'package:iris/utils/get_localizations.dart'; import 'package:iris/utils/request_storage_permission.dart'; -import 'package:iris/widgets/app_chip.dart'; +import 'package:iris/widgets/chip.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -68,8 +67,11 @@ class Files extends HookWidget { final isError = result.error != null; final filteredFiles = useMemoized( - () => filesFilter(result.data ?? [], - [ContentType.dir, ContentType.video, ContentType.audio]), + () => (result.data ?? []) + .where((file) => + [ContentType.video, ContentType.audio].contains(file.type) || + file.isDir) + .toList(), [result.data]); final files = useMemoized( @@ -81,16 +83,21 @@ class Files extends HookWidget { ), [filteredFiles, sortBy, sortOrder, folderFirst]); - ItemScrollController itemScrollController = ItemScrollController(); - ScrollOffsetController scrollOffsetController = ScrollOffsetController(); - ItemPositionsListener itemPositionsListener = - ItemPositionsListener.create(); - ScrollOffsetListener scrollOffsetListener = ScrollOffsetListener.create(); + final itemScrollController = useMemoized(() => ItemScrollController(), []); + final scrollOffsetController = + useMemoized(() => ScrollOffsetController(), []); + final itemPositionsListener = + useMemoized(() => ItemPositionsListener.create(), []); + final scrollOffsetListener = + useMemoized(() => ScrollOffsetListener.create(), []); void play(List files, int index) async { final clickedFile = files[index]; - final List filteredFiles = - filesFilter(files, [ContentType.video, ContentType.audio]); + final List filteredFiles = files + .where((file) => + [ContentType.video, ContentType.audio].contains(file.type)) + .toList(); + final List playQueue = filteredFiles .asMap() .entries @@ -152,9 +159,11 @@ class Files extends HookWidget { visualDensity: const VisualDensity( horizontal: 0, vertical: -4), leading: () { + if (files[index].isDir == true && + files[index].name.isNotEmpty) { + return const Icon(Icons.folder_rounded); + } switch (files[index].type) { - case ContentType.dir: - return const Icon(Icons.folder_rounded); case ContentType.video: return const Icon(Icons.movie_rounded); case ContentType.audio: @@ -217,7 +226,7 @@ class Files extends HookWidget { progress.position .inMilliseconds) <= 5000) { - return AppChip(text: '100%'); + return Chip(text: '100%'); } final String progressString = (progress.position @@ -226,7 +235,7 @@ class Files extends HookWidget { .inMilliseconds * 100) .toStringAsFixed(0); - return AppChip( + return Chip( text: '$progressString %'); } else { return const SizedBox(); @@ -245,7 +254,7 @@ class Files extends HookWidget { mainAxisSize: MainAxisSize.min, children: [ const SizedBox(width: 4), - AppChip( + Chip( text: subtitleType, primary: true, ), diff --git a/lib/pages/storages/storages_list.dart b/lib/pages/storages/storages_list.dart index c68aa5d..7761c15 100644 --- a/lib/pages/storages/storages_list.dart +++ b/lib/pages/storages/storages_list.dart @@ -54,7 +54,7 @@ class StoragesList extends HookWidget { case StorageType.webdav: final storage = allStorages[index] as WebDAVStorage; subtitle = - 'http${storage.https ? 's' : ''}://${storage.host}${storage.basePath.join('/')}'; + 'http${storage.https ? 's' : ''}://${storage.host}${storage.port.isNotEmpty && storage.port != '80' && storage.port != '443' ? ':${storage.port}' : ''}${storage.basePath.join('/')}'; break; case StorageType.ftp: final storage = allStorages[index] as FTPStorage; diff --git a/lib/store/use_play_queue_store.dart b/lib/store/use_play_queue_store.dart index 3816573..36bb6bd 100644 --- a/lib/store/use_play_queue_store.dart +++ b/lib/store/use_play_queue_store.dart @@ -1,6 +1,6 @@ import 'dart:convert'; import 'dart:io'; -import 'package:collection/collection.dart'; +import 'dart:math'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; import 'package:iris/models/file.dart'; @@ -45,13 +45,10 @@ class PlayQueueStore extends PersistentStore { } Future add(List files) async { - final int startIndex = state.playQueue.isEmpty - ? 0 - : state.playQueue - .sorted((a, b) => a.index.compareTo(b.index)) - .last - .index + - 1; + final int maxIndex = state.playQueue.isEmpty + ? -1 + : state.playQueue.map((e) => e.index).reduce(max); + final int startIndex = maxIndex + 1; final List playQueue = files .asMap() @@ -103,15 +100,21 @@ class PlayQueueStore extends PersistentStore { Future previous() async { final int currentPlayIndex = state.playQueue .indexWhere((element) => element.index == state.currentIndex); - if (currentPlayIndex <= 0) return; - await updateCurrentIndex(state.playQueue[currentPlayIndex - 1].index); + if (currentPlayIndex <= 0) { + await updateCurrentIndex(state.playQueue.last.index); + } else { + await updateCurrentIndex(state.playQueue[currentPlayIndex - 1].index); + } } Future next() async { final int currentPlayIndex = state.playQueue .indexWhere((element) => element.index == state.currentIndex); - if (currentPlayIndex >= state.playQueue.length - 1) return; - await updateCurrentIndex(state.playQueue[currentPlayIndex + 1].index); + if (currentPlayIndex >= state.playQueue.length - 1) { + await updateCurrentIndex(state.playQueue.first.index); + } else { + await updateCurrentIndex(state.playQueue[currentPlayIndex + 1].index); + } } Future shuffle() async => update( diff --git a/lib/store/use_player_ui_store.dart b/lib/store/use_player_ui_store.dart new file mode 100644 index 0000000..6484548 --- /dev/null +++ b/lib/store/use_player_ui_store.dart @@ -0,0 +1,44 @@ +import 'package:flutter_zustand/flutter_zustand.dart'; +import 'package:iris/models/store/player_ui_state.dart'; +import 'package:iris/utils/platform.dart'; +import 'package:window_manager/window_manager.dart'; + +class PlayerUiStore extends Store { + PlayerUiStore() : super(const PlayerUiState()); + + void updateAspectRatio(double ratio) { + set(state.copyWith(aspectRatio: ratio)); + } + + Future toggleIsAlwaysOnTop() async { + if (isDesktop) { + windowManager.setAlwaysOnTop(!state.isAlwaysOnTop); + set(state.copyWith(isAlwaysOnTop: !state.isAlwaysOnTop)); + } + } + + Future updateFullScreen(bool bool) async { + if (isDesktop) { + windowManager.setFullScreen(!state.isFullScreen); + set(state.copyWith(isFullScreen: !state.isFullScreen)); + } + } + + void updateIsSeeking(bool bool) { + set(state.copyWith(isSeeking: bool)); + } + + void updateIsHovering(bool bool) { + set(state.copyWith(isHovering: bool)); + } + + void updateIsShowControl(bool bool) { + set(state.copyWith(isShowControl: bool)); + } + + void updateIsShowProgress(bool bool) { + set(state.copyWith(isShowProgress: bool)); + } +} + +PlayerUiStore usePlayerUiStore() => create(() => PlayerUiStore()); diff --git a/lib/store/use_ui_store.dart b/lib/store/use_ui_store.dart deleted file mode 100644 index 3a81fad..0000000 --- a/lib/store/use_ui_store.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter_zustand/flutter_zustand.dart'; -import 'package:iris/models/store/ui_state.dart'; -import 'package:iris/utils/platform.dart'; -import 'package:window_manager/window_manager.dart'; - -class UiStore extends Store { - UiStore() : super(const UiState()); - - Future toggleIsAlwaysOnTop() async { - if (isDesktop) { - windowManager.setAlwaysOnTop(!state.isAlwaysOnTop); - set(state.copyWith(isAlwaysOnTop: !state.isAlwaysOnTop)); - } - } - - Future updateFullScreen(bool bool) async { - if (isDesktop) { - windowManager.setFullScreen(!state.isFullScreen); - set(state.copyWith(isFullScreen: !state.isFullScreen)); - } - } -} - -UiStore useUiStore() => create(() => UiStore()); diff --git a/lib/theme.dart b/lib/theme.dart index 2830045..e8c24fa 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -21,9 +21,9 @@ ThemeData baseTheme(BuildContext context) { } ColorScheme customColorScheme = - ColorScheme.fromSeed(seedColor: Color(0xFFB3BCDF)); + ColorScheme.fromSeed(seedColor: const Color(0xFFB3BCDF)); ColorScheme customDarkColorScheme = ColorScheme.fromSeed( - seedColor: Color(0xFFB3BCDF), brightness: Brightness.dark); + seedColor: const Color(0xFFB3BCDF), brightness: Brightness.dark); class CustomTheme { final ThemeData light; @@ -42,13 +42,15 @@ CustomTheme getTheme({ ColorScheme darkColorScheme = darkDynamic != null ? darkDynamic.harmonized() : customDarkColorScheme; + final base = baseTheme(context); + final lightTheme = ThemeData( colorScheme: colorScheme, useMaterial3: true, textTheme: GoogleFonts.notoSansScTextTheme(), - popupMenuTheme: baseTheme(context).popupMenuTheme, - dropdownMenuTheme: baseTheme(context).dropdownMenuTheme, - listTileTheme: baseTheme(context).listTileTheme, + popupMenuTheme: base.popupMenuTheme, + dropdownMenuTheme: base.dropdownMenuTheme, + listTileTheme: base.listTileTheme, ); final darkTheme = ThemeData.dark(useMaterial3: true).copyWith( @@ -58,9 +60,9 @@ CustomTheme getTheme({ .copyWith(colorScheme: darkColorScheme) .textTheme, ), - popupMenuTheme: baseTheme(context).popupMenuTheme, - dropdownMenuTheme: baseTheme(context).dropdownMenuTheme, - listTileTheme: baseTheme(context).listTileTheme, + popupMenuTheme: base.popupMenuTheme, + dropdownMenuTheme: base.dropdownMenuTheme, + listTileTheme: base.listTileTheme, ); return CustomTheme(light: lightTheme, dark: darkTheme); diff --git a/lib/utils/check_content_type.dart b/lib/utils/check_content_type.dart index 2c9b8bc..21b8b54 100644 --- a/lib/utils/check_content_type.dart +++ b/lib/utils/check_content_type.dart @@ -81,3 +81,7 @@ ContentType checkContentType(String name) { bool isMediaFile(String name) => checkContentType(name) == ContentType.video || checkContentType(name) == ContentType.audio; + +bool isVideoFile(String name) => checkContentType(name) == ContentType.video; +bool isAudioFile(String name) => checkContentType(name) == ContentType.audio; +bool isImageFile(String name) => checkContentType(name) == ContentType.image; diff --git a/lib/utils/files_filter.dart b/lib/utils/files_filter.dart deleted file mode 100644 index 14e7da6..0000000 --- a/lib/utils/files_filter.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:iris/models/file.dart'; - -List filesFilter(List files, List types) { - return files.where((file) => types.contains(file.type)).toList(); -} diff --git a/lib/utils/files_sort.dart b/lib/utils/files_sort.dart index f123815..cb4d74c 100644 --- a/lib/utils/files_sort.dart +++ b/lib/utils/files_sort.dart @@ -7,59 +7,28 @@ List filesSort({ SortOrder sortOrder = SortOrder.asc, bool folderFirst = true, }) { - final dirs_ = files.where((file) => file.isDir).toList(); - final files_ = files.where((file) => !file.isDir).toList(); - - int compare(dynamic a, dynamic b) { - int result; - if (a is String && b is String) { - result = a.toLowerCase().compareTo(b.toLowerCase()); - } else if (a is Comparable && b is Comparable) { - result = a.compareTo(b); - } else { - result = 0; + final sortedFiles = files.toList(); + sortedFiles.sort((a, b) { + if (folderFirst) { + if (a.isDir && !b.isDir) return -1; + if (!a.isDir && b.isDir) return 1; } - return sortOrder == SortOrder.asc ? result : -result; - } - - if (folderFirst) { - switch (sortBy) { - case SortBy.name: - dirs_.sort((a, b) => compare(a.name, b.name)); - files_.sort((a, b) => compare(a.name, b.name)); - break; - case SortBy.size: - dirs_.sort((a, b) => compare(a.size, b.size)); - files_.sort((a, b) => compare(a.size, b.size)); - break; - case SortBy.lastModified: - dirs_.sort((a, b) => compare( - a.lastModified ?? DateTime(0), - b.lastModified ?? DateTime(0), - )); - files_.sort((a, b) => compare( - a.lastModified ?? DateTime(0), - b.lastModified ?? DateTime(0), - )); - break; - } - return [...dirs_, ...files_]; - } else { + int result; switch (sortBy) { case SortBy.name: - files.sort((a, b) => compare(a.name, b.name)); + result = a.name.toLowerCase().compareTo(b.name.toLowerCase()); break; case SortBy.size: - files.sort((a, b) => compare(a.size, b.size)); + result = a.size.compareTo(b.size); break; case SortBy.lastModified: - files.sort((a, b) => compare( - a.lastModified ?? DateTime(0), - b.lastModified ?? DateTime(0), - )); + result = (a.lastModified ?? DateTime(0)) + .compareTo(b.lastModified ?? DateTime(0)); break; } - return files; - } + + return sortOrder == SortOrder.asc ? result : -result; + }); + return sortedFiles; } diff --git a/lib/utils/find_subtitle.dart b/lib/utils/find_subtitle.dart deleted file mode 100644 index 78e9e75..0000000 --- a/lib/utils/find_subtitle.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'dart:io'; -import 'package:iris/utils/platform.dart'; -import 'package:path/path.dart' as p; -import 'package:iris/models/file.dart'; -import 'package:iris/utils/check_content_type.dart'; - -Future> findSubtitle( - List fileNames, String name, String baseUri, - {bool encodeUri = true}) async { - if (checkContentType(name) == ContentType.video) { - List foundSubTitles = []; - - String baseName = - name.split('.').sublist(0, name.split('.').length - 1).join('.'); - - List subtitleExtensions = ['ass', 'srt', 'vtt', 'sub']; - - for (String fileName in fileNames) { - if (fileName.startsWith(baseName) && - subtitleExtensions.any((ext) => fileName.endsWith(ext))) { - String subTitleName = fileName - .replaceAll(baseName, '') - .split('.') - .where((e) => e.isNotEmpty && !subtitleExtensions.contains(e)) - .join('.'); - - foundSubTitles.add(Subtitle( - name: subTitleName.isEmpty ? fileName : subTitleName, - uri: isAndroid && baseUri.startsWith('content://') - ? '$baseUri${Uri.encodeComponent('/$fileName')}' - : encodeUri - ? Uri.encodeFull('$baseUri/$fileName') - : '$baseUri/$fileName', - )); - } - } - return foundSubTitles; - } else { - return []; - } -} - -Future> findLocalSubtitle(String uri) async { - if (checkContentType(uri) == ContentType.video) { - final baseUri = - uri.split('.').sublist(0, uri.split('.').length - 1).join('.'); - - final directory = Directory(p.dirname(uri)); - - List foundSubTitles = []; - - List subtitleExtensions = ['ass', 'srt', 'vtt', 'sub']; - - final entities = directory.list(); - await for (final entity in entities) { - if (entity.path.startsWith(baseUri) && - subtitleExtensions.any((ext) => entity.path.endsWith(ext))) { - String subTitleName = entity.path - .replaceAll(baseUri, '') - .split('.') - .where((e) => e.isNotEmpty && !subtitleExtensions.contains(e)) - .join('.'); - - final fileName = p.basename(entity.path); - - foundSubTitles.add(Subtitle( - name: subTitleName.isEmpty ? fileName : subTitleName, - uri: entity.path, - )); - } - } - - return foundSubTitles; - } else { - return []; - } -} diff --git a/lib/utils/get_subtitle_map.dart b/lib/utils/get_subtitle_map.dart new file mode 100644 index 0000000..8dd661f --- /dev/null +++ b/lib/utils/get_subtitle_map.dart @@ -0,0 +1,34 @@ +import 'package:iris/models/file.dart'; +import 'package:path/path.dart' as path; + +Map> getSubtitleMap({ + required List files, + required String Function(T) getName, + required String Function(T) getUri, +}) { + final subtitleExtensions = {'ass', 'srt', 'vtt', 'sub'}; + final Map> subtitleMap = {}; + + for (final file in files) { + final fileName = getName(file); + final fileExt = + path.extension(fileName).replaceFirst('.', '').toLowerCase(); + if (subtitleExtensions.contains(fileExt)) { + final mediaBaseName = + path.basenameWithoutExtension(fileName).split('.').first; + + final subBaseName = path.basenameWithoutExtension(fileName); + final regex = RegExp(r'^' + RegExp.escape(mediaBaseName) + r'\.(.+?)$', + caseSensitive: false); + final match = regex.firstMatch(subBaseName); + final subTitleName = match?.group(1) ?? subBaseName; + + final subtitle = Subtitle( + name: subTitleName, + uri: getUri(file), + ); + subtitleMap.putIfAbsent(mediaBaseName, () => []).add(subtitle); + } + } + return subtitleMap; +} diff --git a/lib/utils/get_subtitle_title.dart b/lib/utils/get_subtitle_title.dart deleted file mode 100644 index 94db3cc..0000000 --- a/lib/utils/get_subtitle_title.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:iris/l10n/iso_639.dart'; -import 'package:media_kit/media_kit.dart'; - -String getTrackTitle(dynamic track) { - if (track is SubtitleTrack || track is AudioTrack) { - if (track.title != null) { - final lowerCaseTitle = track.title!.toLowerCase(); - final languageFromTitle = getTrackLanguage(lowerCaseTitle); - if (languageFromTitle != null) { - return languageFromTitle; - } - return track.title!; - } - - if (track.language != null) { - final lowerCaseLanguage = track.language!.toLowerCase(); - final languageFromLanguage = getTrackLanguage(lowerCaseLanguage); - if (languageFromLanguage != null) { - return languageFromLanguage; - } - return track.language!; - } - return track.id; - } - return ''; -} - -String? getTrackLanguage(String languageCode) { - if (customLanguageCodes[languageCode] != null) { - return '${(customLanguageCodes[languageCode]!.en).join(', ')}, $languageCode'; - } - - if (iso_639_1[languageCode] != null) { - return '${(iso_639_1[languageCode]!.en).join(', ')}, $languageCode'; - } - - if (iso_639_2[languageCode] != null) { - return '${(iso_639_2[languageCode]!.en).join(', ')}, $languageCode'; - } - - return null; -} diff --git a/lib/utils/resize_window.dart b/lib/utils/resize_window.dart deleted file mode 100644 index 12b0944..0000000 --- a/lib/utils/resize_window.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'dart:math' as math; -import 'package:flutter/material.dart'; -import 'package:iris/store/use_app_store.dart'; -import 'package:iris/utils/logger.dart'; -import 'package:window_manager/window_manager.dart'; -import 'package:window_size/window_size.dart'; - -Future resizeWindow(double? videoAspectRatio) async { - if (await windowManager.isFullScreen() || await windowManager.isMaximized()) { - return; - } - - if (videoAspectRatio == null || videoAspectRatio == 0) { - windowManager.setAspectRatio(0); - return; - } - - bool autoResize = useAppStore().state.autoResize; - - if (!autoResize) return; - - windowManager.setAspectRatio(videoAspectRatio); - - Rect oldBounds = await windowManager.getBounds(); - - if (oldBounds.size.aspectRatio.toStringAsFixed(2) == - videoAspectRatio.toStringAsFixed(2)) { - return; - } - - Screen? screen = await getCurrentScreen(); - - if (screen == null) return; - - double screenWidth = screen.frame.size.width; - double screenHeight = screen.frame.size.height; - double screenAspectRatio = screen.frame.size.aspectRatio; - - double oldArea = oldBounds.size.width * oldBounds.size.height; - - double newHeight = math.sqrt(oldArea / videoAspectRatio); - double newWidth = newHeight * videoAspectRatio; - - Size size = oldBounds.size.aspectRatio < 1 - ? Size(oldBounds.size.height * videoAspectRatio, oldBounds.size.height) - : Size(newWidth, newHeight); - - if (size.width < screenWidth / screen.scaleFactor && - size.height < screenHeight / screen.scaleFactor) { - logger('Window resize: $size'); - - windowManager.setBounds( - null, - position: Offset( - oldBounds.left < 0 - ? 0 - : screenWidth / screen.scaleFactor - oldBounds.left < size.width / 2 - ? screenWidth / screen.scaleFactor - size.width - : oldBounds.left + oldBounds.size.width / 2 - size.width / 2 < 0 - ? 0 - : oldBounds.left + - oldBounds.size.width / 2 - - size.width / 2, - oldBounds.top + oldBounds.size.height / 2 - size.height / 2 <= 0 - ? 0 - : oldBounds.top + oldBounds.size.height / 2 - size.height / 2, - ), - size: size, - animate: true, - ); - } else { - if (screenAspectRatio > videoAspectRatio) { - double height = screenHeight * 0.9 / screen.scaleFactor; - double width = height * videoAspectRatio; - Size size = Size(width, height); - - Offset position = await calcWindowPosition( - size, - Alignment.center, - ); - - logger('Window resize to center: $size'); - - windowManager.setBounds( - null, - position: position, - size: size, - animate: true, - ); - } else { - double width = screenWidth * 0.9 / screen.scaleFactor; - double height = width / videoAspectRatio; - Size size = Size(width, height); - - Offset position = await calcWindowPosition( - size, - Alignment.center, - ); - - logger('Window resize to center: $size'); - - windowManager.setBounds( - null, - position: position, - size: size, - animate: true, - ); - } - } -} diff --git a/lib/widgets/app_menu.dart b/lib/widgets/app_menu.dart deleted file mode 100644 index ec65a1f..0000000 --- a/lib/widgets/app_menu.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'dart:math'; -import 'package:flutter/material.dart'; -import 'package:popover/popover.dart'; - -Future showCustomMenu( - BuildContext context, { - required List items, - double? width = 128, -}) async => - showPopover( - context: context, - bodyBuilder: (context) => SingleChildScrollView( - child: Column(children: items), - ), - width: width, - height: min(MediaQuery.of(context).size.height * 0.75, - items.length * kMinInteractiveDimension), - radius: 16, - arrowHeight: 0, - arrowWidth: 0, - backgroundColor: - Theme.of(context).colorScheme.surfaceDim.withValues(alpha: 0.9), - barrierColor: Colors.transparent, - ); diff --git a/lib/widgets/bottom_sheets/show_open_link_bottom_sheet.dart b/lib/widgets/bottom_sheets/show_open_link_bottom_sheet.dart index c06dc1e..2007988 100644 --- a/lib/widgets/bottom_sheets/show_open_link_bottom_sheet.dart +++ b/lib/widgets/bottom_sheets/show_open_link_bottom_sheet.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:iris/models/file.dart'; +import 'package:iris/store/use_app_store.dart'; import 'package:iris/store/use_play_queue_store.dart'; import 'package:iris/utils/get_localizations.dart'; @@ -28,6 +29,7 @@ class OpenLinkBottomSheet extends HookWidget { void play() { if (url.value.isNotEmpty && RegExp(r'^(http://|https://)').hasMatch(url.value)) { + useAppStore().updateAutoPlay(true); usePlayQueueStore().update( playQueue: [ PlayQueueItem( diff --git a/lib/widgets/card.dart b/lib/widgets/card.dart new file mode 100644 index 0000000..ef62cb4 --- /dev/null +++ b/lib/widgets/card.dart @@ -0,0 +1,66 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; + +class Card extends StatelessWidget { + const Card({ + super.key, + required this.child, + this.padding, + this.borderRadius, + this.color, + this.border, + }); + + final Widget child; + final EdgeInsetsGeometry? padding; + final BorderRadius? borderRadius; + final Color? color; + final Border? border; + + @override + Widget build(BuildContext context) { + final effectiveBorderRadius = borderRadius ?? BorderRadius.circular(16); + final effectiveBorder = border ?? + Border.all( + color: Theme.of(context) + .colorScheme + .onSurfaceVariant + .withValues(alpha: 0.125), + width: 1, + ); + + return Stack( + children: [ + ClipRRect( + borderRadius: effectiveBorderRadius, + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container( + padding: padding ?? const EdgeInsets.all(0), + decoration: BoxDecoration( + borderRadius: effectiveBorderRadius, + color: color ?? + Theme.of(context) + .colorScheme + .surfaceContainer + .withValues(alpha: 0.75), + ), + child: child, + ), + ), + ), + Positioned.fill( + child: IgnorePointer( + child: Container( + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: effectiveBorderRadius, + border: effectiveBorder, + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/widgets/app_chip.dart b/lib/widgets/chip.dart similarity index 86% rename from lib/widgets/app_chip.dart rename to lib/widgets/chip.dart index c4a4a75..6e2203a 100644 --- a/lib/widgets/app_chip.dart +++ b/lib/widgets/chip.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -class AppChip extends StatelessWidget { +class Chip extends StatelessWidget { final String text; final bool primary; - const AppChip({super.key, required this.text, this.primary = false}); + const Chip({super.key, required this.text, this.primary = false}); @override Widget build(BuildContext context) { diff --git a/lib/widgets/dialogs/show_language_dialog.dart b/lib/widgets/dialogs/show_language_dialog.dart index ee6c484..38c374f 100644 --- a/lib/widgets/dialogs/show_language_dialog.dart +++ b/lib/widgets/dialogs/show_language_dialog.dart @@ -17,45 +17,47 @@ class LanguageDialog extends HookWidget { @override Widget build(BuildContext context) { final t = getLocalizations(context); - String language = useAppStore().select(context, (state) => state.language); + final String language = + useAppStore().select(context, (state) => state.language); - void updateLanguage(String language) { - useAppStore().updateLanguage(language); + void updateLanguage(String? newLanguage) { + if (newLanguage == null) return; + useAppStore().updateLanguage(newLanguage); Navigator.pop(context); } + final Map languageOptions = { + 'system': t.system, + ...languages, + }; + return AlertDialog( title: Text(t.select_language), content: SingleChildScrollView( + child: RadioGroup( + groupValue: language, + onChanged: updateLanguage, child: Column( - children: [ - ListTile( - title: Text(t.system), - contentPadding: const EdgeInsets.only(left: 8), - leading: Radio( - value: 'system', - groupValue: language, - onChanged: (_) => updateLanguage('system'), - ), - onTap: () => updateLanguage('system'), - ), - ...languages.entries.map( - (e) => ListTile( - title: Text(e.value), - contentPadding: const EdgeInsets.only(left: 8), - leading: Radio( - value: e.key, - groupValue: language, - onChanged: (_) => updateLanguage(e.key), - ), - onTap: () => updateLanguage(e.key), - ), + mainAxisSize: MainAxisSize.min, + children: languageOptions.entries.map((entry) { + final String langCode = entry.key; + final String langName = entry.value; + + return ListTile( + title: Text(langName), + leading: Radio( + value: langCode, + ), + onTap: () => updateLanguage(langCode), + contentPadding: const EdgeInsets.only(left: 8), + ); + }).toList(), ), - ], - )), + ), + ), actions: [ TextButton( - onPressed: () => Navigator.pop(context, 'Cancel'), + onPressed: () => Navigator.pop(context), child: Text(t.cancel), ), ], diff --git a/lib/widgets/dialogs/show_open_link_dialog.dart b/lib/widgets/dialogs/show_open_link_dialog.dart index c4829f2..bf2dafd 100644 --- a/lib/widgets/dialogs/show_open_link_dialog.dart +++ b/lib/widgets/dialogs/show_open_link_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:iris/models/file.dart'; +import 'package:iris/store/use_app_store.dart'; import 'package:iris/store/use_play_queue_store.dart'; import 'package:iris/utils/get_localizations.dart'; @@ -21,6 +22,7 @@ class OpenLinkDialog extends HookWidget { void play() { if (url.value.isNotEmpty && RegExp(r'^(http://|https://)').hasMatch(url.value)) { + useAppStore().updateAutoPlay(true); usePlayQueueStore().update( playQueue: [ PlayQueueItem( diff --git a/lib/widgets/dialogs/show_orientation_dialog.dart b/lib/widgets/dialogs/show_orientation_dialog.dart index 7af901d..d2315d0 100644 --- a/lib/widgets/dialogs/show_orientation_dialog.dart +++ b/lib/widgets/dialogs/show_orientation_dialog.dart @@ -20,8 +20,9 @@ class OrientationDialog extends HookWidget { final orientation = useAppStore().select(context, (state) => state.orientation); - void updateOrientation(ScreenOrientation orientation) { - useAppStore().updateOrientation(orientation); + void updateOrientation(ScreenOrientation? newOrientation) { + if (newOrientation == null) return; + useAppStore().updateOrientation(newOrientation); Navigator.pop(context); } @@ -34,24 +35,26 @@ class OrientationDialog extends HookWidget { return AlertDialog( title: Text(t.screen_orientation), content: SingleChildScrollView( + child: RadioGroup( + groupValue: orientation, + onChanged: updateOrientation, child: Column( - children: ScreenOrientation.values - .map( - (e) => ListTile( + mainAxisSize: MainAxisSize.min, + children: ScreenOrientation.values.map((e) { + return ListTile( title: Text(orientationMap[e] ?? e.name), - leading: Radio( + leading: Radio( value: e, - groupValue: orientation, - onChanged: (_) => updateOrientation(e), ), onTap: () => updateOrientation(e), - ), - ) - .toList(), - )), + ); + }).toList(), + ), + ), + ), actions: [ TextButton( - onPressed: () => Navigator.pop(context, 'Cancel'), + onPressed: () => Navigator.pop(context), child: Text(t.cancel), ), ], diff --git a/lib/widgets/dialogs/show_rate_dialog.dart b/lib/widgets/dialogs/show_rate_dialog.dart index aae7e7f..2670a81 100644 --- a/lib/widgets/dialogs/show_rate_dialog.dart +++ b/lib/widgets/dialogs/show_rate_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; +import 'package:iris/globals.dart' show speedStops; import 'package:iris/store/use_app_store.dart'; import 'package:iris/utils/get_localizations.dart'; @@ -18,45 +19,35 @@ class RateDialog extends HookWidget { final t = getLocalizations(context); final rate = useAppStore().select(context, (state) => state.rate); - void updateRate(double rate) { - useAppStore().updateRate(rate); + void updateRate(double? newRate) { + if (newRate == null) return; + useAppStore().updateRate(newRate); Navigator.pop(context); } return AlertDialog( title: Text(t.playback_speed), content: SingleChildScrollView( - child: Column( - children: [ - 0.25, - 0.5, - 0.75, - 1.0, - 1.25, - 1.5, - 1.75, - 2.0, - 3.0, - 4.0, - 5.0, - ] - .map( - (item) => ListTile( - title: Text('${item}X'), - leading: Radio( - value: item, - groupValue: rate, - onChanged: (_) => updateRate(item), - ), - onTap: () => updateRate(item), + child: RadioGroup( + groupValue: rate, + onChanged: updateRate, + child: Column( + mainAxisSize: MainAxisSize.min, + children: speedStops.map((item) { + return ListTile( + title: Text('${item}X'), + leading: Radio( + value: item, ), - ) - .toList(), + onTap: () => updateRate(item), + ); + }).toList(), + ), ), ), actions: [ TextButton( - onPressed: () => Navigator.pop(context, 'Cancel'), + onPressed: () => Navigator.pop(context), child: Text(t.cancel), ), ], diff --git a/lib/widgets/dialogs/show_theme_mode_dialog.dart b/lib/widgets/dialogs/show_theme_mode_dialog.dart index 9538778..510cb8d 100644 --- a/lib/widgets/dialogs/show_theme_mode_dialog.dart +++ b/lib/widgets/dialogs/show_theme_mode_dialog.dart @@ -18,51 +18,45 @@ class ThemeModeDialog extends HookWidget { final t = getLocalizations(context); final themeMode = useAppStore().select(context, (state) => state.themeMode); - void updateThemeMode(ThemeMode themeMode) { - useAppStore().updateThemeMode(themeMode); + void updateThemeMode(ThemeMode? newThemeMode) { + if (newThemeMode == null) return; + useAppStore().updateThemeMode(newThemeMode); Navigator.pop(context); } + final Map themeOptions = { + t.system: ThemeMode.system, + t.light: ThemeMode.light, + t.dark: ThemeMode.dark, + }; + return AlertDialog( title: Text(t.theme_mode), content: SingleChildScrollView( + child: RadioGroup( + groupValue: themeMode, + onChanged: updateThemeMode, child: Column( - children: [ - ListTile( - title: Text(t.system), - contentPadding: const EdgeInsets.only(left: 8), - leading: Radio( - value: ThemeMode.system, - groupValue: themeMode, - onChanged: (_) => updateThemeMode(ThemeMode.system), - ), - onTap: () => updateThemeMode(ThemeMode.system), - ), - ListTile( - title: Text(t.light), - contentPadding: const EdgeInsets.only(left: 8), - leading: Radio( - value: ThemeMode.light, - groupValue: themeMode, - onChanged: (_) => updateThemeMode(ThemeMode.light), - ), - onTap: () => updateThemeMode(ThemeMode.light), - ), - ListTile( - title: Text(t.dark), - contentPadding: const EdgeInsets.only(left: 8), - leading: Radio( - value: ThemeMode.dark, - groupValue: themeMode, - onChanged: (_) => updateThemeMode(ThemeMode.dark), - ), - onTap: () => updateThemeMode(ThemeMode.dark), + mainAxisSize: MainAxisSize.min, + children: themeOptions.entries.map((entry) { + final String label = entry.key; + final ThemeMode value = entry.value; + + return ListTile( + title: Text(label), + leading: Radio( + value: value, + ), + onTap: () => updateThemeMode(value), + contentPadding: const EdgeInsets.only(left: 8), + ); + }).toList(), ), - ], - )), + ), + ), actions: [ TextButton( - onPressed: () => Navigator.pop(context, 'Cancel'), + onPressed: () => Navigator.pop(context), child: Text(t.cancel), ), ], diff --git a/lib/widgets/dialogs/show_webdav_dialog.dart b/lib/widgets/dialogs/show_webdav_dialog.dart index 5f4afa1..b560747 100644 --- a/lib/widgets/dialogs/show_webdav_dialog.dart +++ b/lib/widgets/dialogs/show_webdav_dialog.dart @@ -174,9 +174,11 @@ class WebDAVDialog extends HookWidget { value: https.value, onChanged: (_) { isTested.value = false; - if (portController.text == '80') { + if (portController.text == '80' && + https.value == false) { portController.text = '443'; - } else if (portController.text == '443') { + } else if (portController.text == '443' && + https.value == true) { portController.text = '80'; } https.value = !https.value; diff --git a/lib/widgets/drag_area.dart b/lib/widgets/drag_area.dart new file mode 100644 index 0000000..fcd8c74 --- /dev/null +++ b/lib/widgets/drag_area.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_zustand/flutter_zustand.dart'; +import 'package:iris/store/use_player_ui_store.dart'; +import 'package:iris/utils/platform.dart'; +import 'package:window_manager/window_manager.dart'; + +class DragArea extends StatelessWidget { + const DragArea({ + super.key, + required this.child, + }); + + final Widget child; + @override + Widget build(BuildContext context) { + final isFullScreen = + usePlayerUiStore().select(context, (state) => state.isFullScreen); + + return GestureDetector( + onDoubleTap: () async { + if (!isDesktop) return; + if (isFullScreen) { + await usePlayerUiStore().updateFullScreen(false); + } else { + if (await windowManager.isMaximized()) { + await windowManager.unmaximize(); + } else { + await windowManager.maximize(); + } + } + }, + onPanStart: (details) async { + if (isDesktop) { + windowManager.startDragging(); + } + }, + child: child, + ); + } +} diff --git a/lib/widgets/popup.dart b/lib/widgets/popup.dart index 95e46c2..8c0f11b 100644 --- a/lib/widgets/popup.dart +++ b/lib/widgets/popup.dart @@ -1,6 +1,7 @@ import 'dart:io'; -import 'dart:ui'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide Card; +import 'package:iris/utils/platform.dart'; +import 'package:iris/widgets/card.dart'; import 'package:window_manager/window_manager.dart'; enum PopupDirection { left, right } @@ -29,98 +30,114 @@ class Popup extends PopupRoute { final Widget child; final PopupDirection direction; + bool _isPopping = false; + + void _popOnce(BuildContext context) { + if (_isPopping) return; + _isPopping = true; + Navigator.of(context).pop(); + } + @override Color? get barrierColor => Colors.transparent; @override - bool get barrierDismissible => true; + bool get barrierDismissible => false; @override String? get barrierLabel => 'Dismiss'; @override - Duration get transitionDuration => const Duration(milliseconds: 300); + Duration get transitionDuration => const Duration(milliseconds: 250); @override Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { - double screenWidth = MediaQuery.of(context).size.width; - double screenHeight = MediaQuery.of(context).size.height; - int size = screenWidth > 1200 - ? 3 - : screenWidth > 720 - ? 2 - : 1; - - return SafeArea( - child: Stack( - children: [ - Positioned.fill( - child: GestureDetector( - onPanStart: (details) { - if (Platform.isWindows || - Platform.isLinux || - Platform.isMacOS) { - windowManager.startDragging(); - } - }, - onTap: () => Navigator.of(context).pop(), - ), + return Stack( + children: [ + Positioned.fill( + child: GestureDetector( + onPanStart: (details) { + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { + windowManager.startDragging(); + } + }, + onTap: () => _popOnce(context), ), - Align( - alignment: direction == PopupDirection.left - ? Alignment.bottomLeft - : Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only( - top: 0, - bottom: 8, - left: direction == PopupDirection.left ? 8 : 0, - right: direction == PopupDirection.right ? 8 : 0, - ), - child: AnimatedBuilder( - animation: animation, - builder: (context, child) { - return SlideTransition( - position: Tween( - begin: direction == PopupDirection.left - ? const Offset(-1.0, 0.0) - : const Offset(1.0, 0.0), - end: Offset.zero, - ).animate(CurvedAnimation( + ), + Align( + alignment: direction == PopupDirection.left + ? Alignment.bottomLeft + : Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only( + bottom: 8, + left: direction == PopupDirection.left ? 8 : 0, + right: direction == PopupDirection.right ? 8 : 0, + ), + child: AnimatedBuilder( + animation: animation, + builder: (context, child) { + return SlideTransition( + position: Tween( + begin: direction == PopupDirection.left + ? const Offset(-1.0, 0.0) + : const Offset(1.0, 0.0), + end: Offset.zero, + ).animate( + CurvedAnimation( parent: animation, curve: Curves.easeInOutCubicEmphasized, - )), - child: child, - ); + ), + ), + child: child, + ); + }, + child: Dismissible( + key: UniqueKey(), + direction: direction == PopupDirection.left + ? DismissDirection.endToStart + : DismissDirection.startToEnd, + onUpdate: (details) { + if (details.previousReached) { + _popOnce(context); + } }, - child: ClipRRect( - borderRadius: BorderRadius.circular(16), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), - child: Material( - color: Theme.of(context) - .colorScheme - .surface - .withValues(alpha: 0.75), - child: UnconstrainedBox( - child: LimitedBox( - maxWidth: screenWidth / size - 16, - maxHeight: screenHeight - 18 - 48, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [Expanded(child: child)], + child: LayoutBuilder( + builder: (context, constraints) { + final screenWidth = constraints.maxWidth; + final screenHeight = constraints.maxHeight; + final int size = screenWidth > 1200 + ? 3 + : screenWidth > 720 + ? 2 + : 1; + + return UnconstrainedBox( + child: LimitedBox( + maxWidth: screenWidth / size - 16, + maxHeight: + isDesktop ? screenHeight - 48 : screenHeight - 16, + child: Card( + child: Material( + color: Colors.transparent, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded(child: child), + ], + ), ), ), ), - ), - ), + ); + }, ), ), ), ), - ], - ), + ), + ], ); } } diff --git a/lib/widgets/title_bar.dart b/lib/widgets/title_bar.dart index ca9bcb7..42637d5 100644 --- a/lib/widgets/title_bar.dart +++ b/lib/widgets/title_bar.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_zustand/flutter_zustand.dart'; import 'package:iris/info.dart'; -import 'package:iris/store/use_ui_store.dart'; +import 'package:iris/store/use_player_ui_store.dart'; import 'package:iris/utils/get_localizations.dart'; import 'package:iris/utils/platform.dart'; import 'package:window_manager/window_manager.dart'; @@ -15,7 +15,6 @@ class TitleBar extends HookWidget { this.color, this.overlayColor, this.saveProgress, - this.resizeWindow, }); final String? title; @@ -23,15 +22,14 @@ class TitleBar extends HookWidget { final Color? color; final WidgetStateProperty? overlayColor; final Future Function()? saveProgress; - final Future Function()? resizeWindow; @override Widget build(BuildContext context) { final t = getLocalizations(context); final isAlwaysOnTop = - useUiStore().select(context, (state) => state.isAlwaysOnTop); + usePlayerUiStore().select(context, (state) => state.isAlwaysOnTop); final isFullScreen = - useUiStore().select(context, (state) => state.isFullScreen); + usePlayerUiStore().select(context, (state) => state.isFullScreen); return Container( padding: isDesktop @@ -42,158 +40,157 @@ class TitleBar extends HookWidget { begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ - Colors.black87.withValues(alpha: 0.8), - Colors.black87.withValues(alpha: 0.3), + Colors.black87.withValues(alpha: 0.6), + Colors.black87.withValues(alpha: 0.25), Colors.black87.withValues(alpha: 0), ], ), ), child: ExcludeFocus( - child: SafeArea( - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image.asset('assets/images/icon.png', width: 24, height: 24), - const SizedBox(width: 8), - Expanded( - child: Text( - title!.isEmpty ? INFO.title : title!, - maxLines: 1, - textAlign: TextAlign.start, - style: TextStyle( - fontSize: 16, - overflow: TextOverflow.ellipsis, - color: color, - ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + 'assets/images/logo_transparent.png', + width: 32, + height: 32, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + title!.isEmpty ? INFO.title : title!, + maxLines: 1, + textAlign: TextAlign.start, + style: TextStyle( + fontSize: 16, + overflow: TextOverflow.ellipsis, + color: color, ), ), - Row( - children: [ - ...actions ?? [], - if (isDesktop) ...[ - FutureBuilder( - future: () async { - final isMaximized = - isDesktop && await windowManager.isMaximized(); + ), + Row( + children: [ + ...actions ?? [], + if (isDesktop) ...[ + FutureBuilder( + future: () async { + final isMaximized = + isDesktop && await windowManager.isMaximized(); - return isMaximized; - }(), - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { - final isMaximized = snapshot.data ?? false; + return isMaximized; + }(), + builder: ( + BuildContext context, + AsyncSnapshot snapshot, + ) { + final isMaximized = snapshot.data ?? false; - return Row( - children: [ - Visibility( - visible: !isFullScreen, - child: IconButton( - tooltip: isAlwaysOnTop - ? '${t.always_on_top_on} ( F10 )' - : '${t.always_on_top_off} ( F10 )', - icon: Icon( - isAlwaysOnTop - ? Icons.push_pin_rounded - : Icons.push_pin_outlined, - size: 18, - color: color, - ), - onPressed: useUiStore().toggleIsAlwaysOnTop, - style: ButtonStyle(overlayColor: overlayColor), + return Row( + children: [ + Visibility( + visible: !isFullScreen, + child: IconButton( + tooltip: isAlwaysOnTop + ? '${t.always_on_top_on} ( F10 )' + : '${t.always_on_top_off} ( F10 )', + icon: Icon( + isAlwaysOnTop + ? Icons.push_pin_rounded + : Icons.push_pin_outlined, + size: 18, + color: color, ), + onPressed: usePlayerUiStore().toggleIsAlwaysOnTop, + style: ButtonStyle(overlayColor: overlayColor), ), - Visibility( - visible: isFullScreen, - child: IconButton( - tooltip: isFullScreen - ? '${t.exit_fullscreen} ( Escape, F11, Enter )' - : '${t.enter_fullscreen} ( F11, Enter )', - icon: Icon( - isFullScreen - ? Icons.close_fullscreen_rounded - : Icons.open_in_full_rounded, - size: 18, - color: color, - ), - onPressed: () async { - if (isFullScreen) { - await resizeWindow?.call(); - } - useUiStore().updateFullScreen(!isFullScreen); - }, - style: ButtonStyle(overlayColor: overlayColor), + ), + Visibility( + visible: isFullScreen, + child: IconButton( + tooltip: isFullScreen + ? '${t.exit_fullscreen} ( Escape, F11, Enter )' + : '${t.enter_fullscreen} ( F11, Enter )', + icon: Icon( + isFullScreen + ? Icons.close_fullscreen_rounded + : Icons.open_in_full_rounded, + size: 18, + color: color, ), + onPressed: () async { + usePlayerUiStore() + .updateFullScreen(!isFullScreen); + }, + style: ButtonStyle(overlayColor: overlayColor), ), - Visibility( - visible: !isFullScreen, - child: IconButton( - onPressed: () => windowManager.minimize(), - icon: Icon( - Icons.remove_rounded, - color: color, - ), - style: ButtonStyle(overlayColor: overlayColor), + ), + Visibility( + visible: !isFullScreen, + child: IconButton( + onPressed: () => windowManager.minimize(), + icon: Icon( + Icons.remove_rounded, + color: color, ), + style: ButtonStyle(overlayColor: overlayColor), ), - Visibility( - visible: !isFullScreen, - child: IconButton( - onPressed: () async { - if (isMaximized) { - await windowManager.unmaximize(); - await resizeWindow?.call(); - } else { - await windowManager.maximize(); - } - }, - icon: isMaximized - ? RotatedBox( - quarterTurns: 2, - child: Icon( - Icons.filter_none_rounded, - size: 18, - color: color, - ), - ) - : Icon( - Icons.crop_din_rounded, - size: 20, + ), + Visibility( + visible: !isFullScreen, + child: IconButton( + onPressed: () async { + if (isMaximized) { + await windowManager.unmaximize(); + } else { + await windowManager.maximize(); + } + }, + icon: isMaximized + ? RotatedBox( + quarterTurns: 2, + child: Icon( + Icons.filter_none_rounded, + size: 18, color: color, ), - style: ButtonStyle(overlayColor: overlayColor), - ), + ) + : Icon( + Icons.crop_din_rounded, + size: 20, + color: color, + ), + style: ButtonStyle(overlayColor: overlayColor), ), - ], - ); - }, + ), + ], + ); + }, + ), + IconButton( + onPressed: () async { + await saveProgress?.call(); + windowManager.close(); + }, + icon: Icon( + Icons.close_rounded, + color: color, ), - IconButton( - onPressed: () async { - await saveProgress?.call(); - windowManager.close(); - }, - icon: Icon( - Icons.close_rounded, - color: color, - ), - style: ButtonStyle( - overlayColor: WidgetStateProperty.resolveWith( - (Set states) { - if (states.contains(WidgetState.pressed)) { - return Colors.red.withValues(alpha: 0.4); - } else if (states.contains(WidgetState.hovered)) { - return Colors.red.withValues(alpha: 0.5); - } - return null; - }), - ), + style: ButtonStyle( + overlayColor: WidgetStateProperty.resolveWith( + (Set states) { + if (states.contains(WidgetState.pressed)) { + return Colors.red.withValues(alpha: 0.4); + } else if (states.contains(WidgetState.hovered)) { + return Colors.red.withValues(alpha: 0.5); + } + return null; + }), ), - ], + ), ], - ), - ], - ), + ], + ), + ], ), ), ); diff --git a/pubspec.lock b/pubspec.lock index 0d3b65a..cb6dbf7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f + sha256: f0bb5d1648339c8308cc0b9838d8456b3cfe5c91f9dc1a735b4d003269e5da9a url: "https://pub.dev" source: hosted - version: "82.0.0" + version: "88.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0" + sha256: "0b7b9c329d2879f8f05d6c05b32ee9ec025f39b077864bdb5ac9a7b63418a98f" url: "https://pub.dev" source: hosted - version: "7.4.5" + version: "8.1.1" android_x_storage: dependency: "direct main" description: @@ -29,10 +29,10 @@ packages: dependency: "direct main" description: name: app_links - sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba" + sha256: "5f88447519add627fe1cbcab4fd1da3d4fed15b9baf29f28b22535c95ecee3e8" url: "https://pub.dev" source: hosted - version: "6.4.0" + version: "6.4.1" app_links_linux: dependency: transitive description: @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: asn1lib - sha256: "0511d6be23b007e95105ae023db599aea731df604608978dada7f9faf2637623" + sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.6.5" async: dependency: transitive description: @@ -101,18 +101,18 @@ packages: dependency: transitive description: name: build - sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 + sha256: "5b887c55a0f734b433b3b2d89f9cd1f99eb636b17e268a5b4259258bc916504b" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "4.0.0" build_config: dependency: transitive description: name: build_config - sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" build_daemon: dependency: transitive description: @@ -121,30 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.4" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 - url: "https://pub.dev" - source: hosted - version: "2.4.4" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" + sha256: "804c47c936df75e1911c19a4fb8c46fa8ff2b3099b9f2b2aa4726af3774f734b" url: "https://pub.dev" source: hosted - version: "2.4.15" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" - url: "https://pub.dev" - source: hosted - version: "8.0.0" + version: "2.8.0" built_collection: dependency: transitive description: @@ -157,10 +141,10 @@ packages: dependency: transitive description: name: built_value - sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27" + sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d url: "https://pub.dev" source: hosted - version: "8.10.1" + version: "8.12.0" characters: dependency: transitive description: @@ -205,10 +189,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" url: "https://pub.dev" source: hosted - version: "4.10.1" + version: "4.11.0" collection: dependency: "direct main" description: @@ -274,21 +258,21 @@ packages: source: hosted version: "1.0.8" dart_pubspec_licenses: - dependency: transitive + dependency: "direct dev" description: name: dart_pubspec_licenses - sha256: "23ddb78ff9204d08e3109ced67cd3c6c6a066f581b0edf5ee092fc3e1127f4ea" + sha256: "391ca09a09430e485839118b89b55a5fb142845d6c305d33fdc92d75ec65c2e5" url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.9" dart_style: dependency: transitive description: name: dart_style - sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af" + sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" dbus: dependency: transitive description: @@ -301,34 +285,34 @@ packages: dependency: "direct main" description: name: desktop_drop - sha256: bd21017e0415632c85f6b813c846bc8c9811742507776dcf6abf91a14d946e98 + sha256: "927511f590ce01ee90d0d80f79bc71b9c919d8522d01e495e89a00c6f4a4fb5b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" device_info_plus: dependency: "direct main" description: name: device_info_plus - sha256: "0c6396126421b590089447154c5f98a5de423b70cfb15b1578fd018843ee6f53" + sha256: "49413c8ca514dea7633e8def233b25efdf83ec8522955cc2c0e3ad802927e7c6" url: "https://pub.dev" source: hosted - version: "11.4.0" + version: "12.1.0" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f url: "https://pub.dev" source: hosted - version: "7.0.2" + version: "7.0.3" dio: dependency: transitive description: name: dio - sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 url: "https://pub.dev" source: hosted - version: "5.8.0+1" + version: "5.9.0" dio_web_adapter: dependency: transitive description: @@ -358,10 +342,10 @@ packages: dependency: "direct main" description: name: dynamic_color - sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d + sha256: "43a5a6679649a7731ab860334a5812f2067c2d9ce6452cf069c5e0c25336c17c" url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.8.1" equatable: dependency: transitive description: @@ -398,10 +382,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "77f8e81d22d2a07d0dee2c62e1dda71dc1da73bf43bb2d45af09727406167964" + sha256: f2d9f173c2c14635cc0e9b14c143c49ef30b4934e8d1d274d6206fcb0086a06f url: "https://pub.dev" source: hosted - version: "10.1.9" + version: "10.3.3" fixnum: dependency: transitive description: @@ -427,10 +411,10 @@ packages: dependency: "direct main" description: name: flutter_hooks - sha256: b772e710d16d7a20c0740c4f855095026b31c7eb5ba3ab67d2bd52021cd9461d + sha256: "8ae1f090e5f4ef5cfa6670ce1ab5dddadd33f3533a7f9ba19d9f958aa2a89f42" url: "https://pub.dev" source: hosted - version: "0.21.2" + version: "0.21.3+1" flutter_lints: dependency: "direct dev" description: @@ -452,22 +436,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.7+1" - flutter_oss_licenses: - dependency: "direct dev" - description: - name: flutter_oss_licenses - sha256: e4bbaeb00bc768e8430ee0c95ad304d2f256fb15194d30b912cea269871c8885 - url: "https://pub.dev" - source: hosted - version: "3.0.4" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + sha256: b0694b7fb1689b0e6cc193b3f1fcac6423c4f93c74fb20b806c6b6f196db0c31 url: "https://pub.dev" source: hosted - version: "2.0.28" + version: "2.0.30" flutter_secure_storage: dependency: "direct main" description: @@ -546,18 +522,18 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c" + sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.2.3" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b + sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.1.0" frontend_server_client: dependency: transitive description: @@ -570,18 +546,18 @@ packages: dependency: "direct main" description: name: fvp - sha256: a2b6f305a5e559abc21b1be06ca0ffb5bb6b5b523d6d45eb8e78d53f3b89e9a2 + sha256: a2850b856e177cb48f3e49940613f2180f3375bcceab2f75e3d7b915009a2840 url: "https://pub.dev" source: hosted - version: "0.32.1" + version: "0.34.0" get_it: dependency: transitive description: name: get_it - sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 + sha256: a4292e7cf67193f8e7c1258203104eb2a51ec8b3a04baa14695f4064c144297b url: "https://pub.dev" source: hosted - version: "7.7.0" + version: "8.2.0" glob: dependency: transitive description: @@ -594,10 +570,10 @@ packages: dependency: "direct main" description: name: google_fonts - sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82 + sha256: ebc94ed30fd13cefd397cb1658b593f21571f014b7d1197eeb41fb95f05d899a url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.3.1" graphs: dependency: transitive description: @@ -626,10 +602,10 @@ packages: dependency: "direct main" description: name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" http_methods: dependency: transitive description: @@ -698,34 +674,34 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c + sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" url: "https://pub.dev" source: hosted - version: "6.9.5" + version: "6.11.1" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -835,7 +811,7 @@ packages: description: path: "." ref: main - resolved-ref: ffdd8015191aee63acc32c5657ecf050302861be + resolved-ref: "0abae44bddaa9befe16a90acd23ac544a9fe9325" url: "https://github.com/nini22P/media_stream" source: git version: "0.0.1" @@ -859,10 +835,10 @@ packages: dependency: "direct dev" description: name: msix - sha256: edde648a8133bf301883c869d19d127049683037c65ff64173ba526ac7a8af2f + sha256: f88033fcb9e0dd8de5b18897cbebbd28ea30596810f4a7c86b12b0c03ace87e5 url: "https://pub.dev" source: hosted - version: "3.16.9" + version: "3.16.12" mutex: dependency: transitive description: @@ -891,18 +867,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "9.0.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.1" path: dependency: "direct main" description: @@ -923,18 +899,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "993381400e94d18469750e5b9dcb8206f15bc09f9da86b9e44a9b0092a0066db" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.18" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: "16eef174aacb07e09c351502740fa6254c165757638eba1e9116b0a781201bbd" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" path_provider_linux: dependency: transitive description: @@ -971,10 +947,10 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "2d070d8684b68efb580a5997eb62f675e8a885ef0be6e754fb9ef489c177470f" + sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 url: "https://pub.dev" source: hosted - version: "12.0.0+1" + version: "12.0.1" permission_handler_android: dependency: transitive description: @@ -1019,10 +995,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "7.0.1" platform: dependency: transitive description: @@ -1067,18 +1043,18 @@ packages: dependency: transitive description: name: posix - sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62 + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "6.0.3" provider: dependency: "direct main" description: name: provider - sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.1.5+1" pub_semver: dependency: transitive description: @@ -1107,10 +1083,10 @@ packages: dependency: "direct main" description: name: saf_util - sha256: b18e57677fc704918b2a67d7b5aa1c9c61dc5f3f2e7e70480576db77d18bec4a + sha256: "219f983e5f17b28998335158cdc97add9d52af9884e38b5a43f10dcc070510ec" url: "https://pub.dev" source: hosted - version: "0.10.0" + version: "0.11.0" safe_local_storage: dependency: transitive description: @@ -1123,18 +1099,18 @@ packages: dependency: "direct main" description: name: screen_brightness - sha256: "20b43489fbb12316d64633d5abb731f8d3e2c49871f65c8e434c6225d0f58fcf" + sha256: "5f70754028f169f059fdc61112a19dcbee152f8b293c42c848317854d650cba3" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.7" screen_brightness_android: dependency: transitive description: name: screen_brightness_android - sha256: "6ba1b5812f66c64e9e4892be2d36ecd34210f4e0da8bdec6a2ea34f1aa42683e" + sha256: d34f5321abd03bc3474f4c381f53d189117eba0b039eac1916aa92cca5fd0a96 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" screen_brightness_ios: dependency: transitive description: @@ -1155,10 +1131,10 @@ packages: dependency: transitive description: name: screen_brightness_ohos - sha256: "61e313e46eaee3f83dd4e85a2a91f8a81be02c154bc9e60830a7c0fd76dac286" + sha256: a93a263dcd39b5c56e589eb495bcd001ce65cdd96ff12ab1350683559d5c5bb7 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" screen_brightness_platform_interface: dependency: transitive description: @@ -1264,18 +1240,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + sha256: ccf30b0c9fbcd79d8b6f5bfac23199fb354938436f62475e14aea0f29ee0f800 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "4.0.1" source_helper: dependency: transitive description: name: source_helper - sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" url: "https://pub.dev" source: hosted - version: "1.3.5" + version: "1.3.8" source_span: dependency: transitive description: @@ -1328,10 +1304,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "3.4.0" term_glyph: dependency: transitive description: @@ -1344,18 +1320,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" - timing: - dependency: transitive - description: - name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" - url: "https://pub.dev" - source: hosted - version: "1.0.2" + version: "0.7.6" typed_data: dependency: transitive description: @@ -1384,26 +1352,26 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.3.2" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + sha256: "07cffecb7d68cbc6437cd803d5f11a86fe06736735c3dfe46ff73bcb0f958eed" url: "https://pub.dev" source: hosted - version: "6.3.16" + version: "6.3.21" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7 url: "https://pub.dev" source: hosted - version: "6.3.3" + version: "6.3.4" url_launcher_linux: dependency: transitive description: @@ -1416,10 +1384,10 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.3" url_launcher_platform_interface: dependency: transitive description: @@ -1456,10 +1424,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" video_player: dependency: "direct main" description: @@ -1472,42 +1440,42 @@ packages: dependency: transitive description: name: video_player_android - sha256: "4a5135754a62dbc827a64a42ef1f8ed72c962e191c97e2d48744225c2b9ebb73" + sha256: "59e5a457ddcc1688f39e9aef0efb62aa845cf0cbbac47e44ac9730dc079a2385" url: "https://pub.dev" source: hosted - version: "2.8.7" + version: "2.8.13" video_player_avfoundation: dependency: transitive description: name: video_player_avfoundation - sha256: "9ee764e5cd2fc1e10911ae8ad588e1a19db3b6aa9a6eb53c127c42d3a3c3f22f" + sha256: f9a780aac57802b2892f93787e5ea53b5f43cc57dc107bee9436458365be71cd url: "https://pub.dev" source: hosted - version: "2.7.1" + version: "2.8.4" video_player_platform_interface: dependency: transitive description: name: video_player_platform_interface - sha256: df534476c341ab2c6a835078066fc681b8265048addd853a1e3c78740316a844 + sha256: cf2a1d29a284db648fd66cbd18aacc157f9862d77d2cc790f6f9678a46c1db5a url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.4.0" video_player_web: dependency: transitive description: name: video_player_web - sha256: e8bba2e5d1e159d5048c9a491bb2a7b29c535c612bb7d10c1e21107f5bd365ba + sha256: "9f3c00be2ef9b76a95d94ac5119fb843dca6f2c69e6c9968f6f2b6c9e7afbdeb" url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "2.4.0" vm_service: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "15.0.2" volume_controller: dependency: transitive description: @@ -1520,26 +1488,26 @@ packages: dependency: "direct main" description: name: wakelock_plus - sha256: a474e314c3e8fb5adef1f9ae2d247e57467ad557fa7483a2b895bc1b421c5678 + sha256: "9296d40c9adbedaba95d1e704f4e0b434be446e2792948d0e4aa977048104228" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" wakelock_plus_platform_interface: dependency: transitive description: name: wakelock_plus_platform_interface - sha256: e10444072e50dbc4999d7316fd303f7ea53d31c824aa5eb05d7ccbdd98985207 + sha256: "036deb14cd62f558ca3b73006d52ce049fabcdcb2eddfe0bf0fe4e8a943b5cf2" url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.3.0" watcher: dependency: transitive description: name: watcher - sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + sha256: "5bf046f41320ac97a469d506261797f35254fa61c641741ef32dacda98b7d39c" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.3" web: dependency: transitive description: @@ -1576,10 +1544,10 @@ packages: dependency: transitive description: name: win32 - sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "5.14.0" win32_registry: dependency: transitive description: @@ -1592,10 +1560,10 @@ packages: dependency: "direct main" description: name: window_manager - sha256: "51d50168ab267d344b975b15390426b1243600d436770d3f13de67e55b05ec16" + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.5.1" window_size: dependency: "direct main" description: @@ -1616,10 +1584,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.6.1" yaml: dependency: transitive description: @@ -1637,5 +1605,5 @@ packages: source: hosted version: "0.0.5" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.9.0 <4.0.0" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index b03e050..63e4064 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: iris description: "A lightweight video player" publish_to: 'none' -version: 1.4.2+3 +version: 1.5.0+3 environment: sdk: ^3.5.4 @@ -13,43 +13,43 @@ dependencies: flutter_localizations: sdk: flutter intl: any - file_picker: ^10.1.9 + file_picker: ^10.3.3 flutter_breadcrumb: ^1.0.1 - flutter_hooks: ^0.21.2 + flutter_hooks: ^0.21.3+1 flutter_secure_storage: ^8.1.0 flutter_zustand: ^0.0.5 - media_kit: ^1.1.11 - media_kit_video: ^1.2.5 - media_kit_libs_video: ^1.0.5 - path: ^1.9.0 + media_kit: ^1.2.0 + media_kit_video: ^1.3.0 + media_kit_libs_video: ^1.0.6 + path: ^1.9.1 path_provider: ^2.1.5 - package_info_plus: ^8.1.1 - provider: ^6.1.2 + package_info_plus: ^9.0.0 + provider: ^6.1.5 webdav_client: ^1.2.2 - window_manager: ^0.5.0 - url_launcher: ^6.3.1 + window_manager: ^0.5.1 + url_launcher: ^6.3.2 scrollable_positioned_list: ^0.3.8 - google_fonts: ^6.2.1 - dynamic_color: ^1.7.0 + google_fonts: ^6.3.1 + dynamic_color: ^1.8.1 window_size: ^0.1.0 uuid: ^4.5.1 - flutter_markdown: ^0.7.5 - http: ^1.2.2 - collection: ^1.19.0 + flutter_markdown: ^0.7.7+1 + http: ^1.5.0 + collection: ^1.19.1 json_annotation: ^4.9.0 - freezed_annotation: ^3.0.0 + freezed_annotation: ^3.1.0 disks_desktop: ^1.0.1 android_x_storage: ^1.0.2 - permission_handler: ^12.0.0+1 - desktop_drop: ^0.6.0 - app_links: ^6.3.3 - device_info_plus: ^11.2.1 - saf_util: ^0.10.0 - screen_brightness: ^2.1.4 + permission_handler: ^12.0.1 + desktop_drop: ^0.6.1 + app_links: ^6.4.1 + device_info_plus: ^12.1.0 + saf_util: ^0.11.0 + screen_brightness: ^2.1.7 flutter_volume_controller: ^1.3.3 - fvp: ^0.32.1 - video_player: ^2.9.2 - wakelock_plus: ^1.2.10 + fvp: ^0.34.0 + video_player: ^2.10.0 + wakelock_plus: ^1.4.0 popover: ^0.3.1 pure_ftp: ^0.7.5 drives_windows: @@ -65,11 +65,13 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^6.0.0 - flutter_oss_licenses: ^3.0.4 - build_runner: ^2.4.14 - freezed: ^3.0.6 - json_serializable: ^6.9.3 - msix: ^3.16.9 + dart_pubspec_licenses: ^3.0.9 + build_runner: ^2.8.0 + freezed: ^3.2.3 + json_serializable: ^6.11.1 + msix: ^3.16.12 + # flutter_hooks_lint: ^1.4.0 + # custom_lint: ^0.8.1 flutter: generate: true @@ -82,9 +84,9 @@ msix_config: identity_name: 22P.IRISplayer publisher_display_name: 22P publisher: CN=9740B6B2-E777-4F52-8ECD-C4A577A73010 - msix_version: 1.4.1.0 - logo_path: assets/images/icon.png - trimLogo: false + msix_version: 1.5.0.0 + logo_path: assets/images/logo.png + trim_logo: false languages: en-us, zh-cn execution_alias: iris file_extension: .3gp, .avi, .dpx, .dv, .f4v, .flv, .he264, .hevc, .h265, .mkv, .mp4, .mpeg, .mpg, .mov, .nsv, .rm, .rmvb, .ts, .vob, .webm, .wmv, .aac, .aiff, .alac, .cda, .dsd, .flac, .m4a, .midi, .mp3, .ogg, .opus, .raw, .wav, .wma \ No newline at end of file diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index af1407c..5ec13f9 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ