mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-20 11:08:03 +08:00
Compare commits
56 Commits
662ccfcf0a
...
2.0.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbc4f58323 | ||
|
|
b553e7554d | ||
|
|
68724c8a9e | ||
|
|
85baf8e0e6 | ||
|
|
222c9d01a0 | ||
|
|
db30aa8041 | ||
|
|
6f95456d20 | ||
|
|
de6e402d97 | ||
|
|
6341660788 | ||
|
|
a1dbcae93e | ||
|
|
1526137a64 | ||
|
|
3097b56816 | ||
|
|
db74eccf77 | ||
|
|
14890d342a | ||
|
|
51163dd985 | ||
|
|
f0d9b3a9a7 | ||
|
|
8f3707fbf1 | ||
|
|
f52bbe9804 | ||
|
|
3ec54868d0 | ||
|
|
c0b55f9af3 | ||
|
|
279f21857d | ||
|
|
b897103af0 | ||
|
|
353664fbd4 | ||
|
|
de3505ce07 | ||
|
|
cdc1720358 | ||
|
|
904d210ba2 | ||
|
|
db8dd85b63 | ||
|
|
8ad130567e | ||
|
|
7eb21bc5a2 | ||
|
|
ea4316a847 | ||
|
|
2bbc97a950 | ||
|
|
0178d105ba | ||
|
|
771fa75f48 | ||
|
|
82483b33fc | ||
|
|
886c53c7d8 | ||
|
|
f0050dd6e6 | ||
|
|
e6a2f65b4e | ||
|
|
2fc3f9864f | ||
|
|
64c05a1b06 | ||
|
|
7c4e20f96c | ||
|
|
ace286753c | ||
|
|
f0430eba9f | ||
|
|
bbcceb72a7 | ||
|
|
be4fa6ad2c | ||
|
|
50e1f77e10 | ||
|
|
ba56b45038 | ||
|
|
b4b3764e5f | ||
|
|
2220372e4f | ||
|
|
0957dfc66e | ||
|
|
9578f948b4 | ||
|
|
1724f0d202 | ||
|
|
2bebf200df | ||
|
|
fc7fc18b14 | ||
|
|
8f00ca5680 | ||
|
|
236b524445 | ||
|
|
ae59d257c3 |
2
.github/workflows/ios.yml
vendored
2
.github/workflows/ios.yml
vendored
@@ -13,7 +13,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-macos-app:
|
build-macos-app:
|
||||||
name: Release IOS
|
name: Release IOS
|
||||||
runs-on: macos-latest
|
runs-on: macos-26
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|||||||
2
.github/workflows/mac.yml
vendored
2
.github/workflows/mac.yml
vendored
@@ -13,7 +13,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-mac-app:
|
build-mac-app:
|
||||||
name: Release Mac
|
name: Release Mac
|
||||||
runs-on: macos-latest
|
runs-on: macos-26
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|||||||
7
.github/workflows/win_x64.yml
vendored
7
.github/workflows/win_x64.yml
vendored
@@ -57,7 +57,6 @@ jobs:
|
|||||||
mv dist/**/*.exe PiliPlus-Win-Setup/PiliPlus_windows_${{env.version}}_x64_setup.exe
|
mv dist/**/*.exe PiliPlus-Win-Setup/PiliPlus_windows_${{env.version}}_x64_setup.exe
|
||||||
|
|
||||||
- name: Compress
|
- name: Compress
|
||||||
if: ${{ github.event.inputs.tag != '' }}
|
|
||||||
run: |
|
run: |
|
||||||
Compress-Archive -Path "Release/PiliPlus-Win" -DestinationPath "PiliPlus_windows_${{env.version}}_x64_portable.zip"
|
Compress-Archive -Path "Release/PiliPlus-Win" -DestinationPath "PiliPlus_windows_${{env.version}}_x64_portable.zip"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
@@ -75,9 +74,9 @@ jobs:
|
|||||||
- name: Upload windows file release
|
- name: Upload windows file release
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
archive: true
|
archive: false
|
||||||
name: PiliPlus_windows_${{env.version}}_x64_portable
|
name: Windows-file-x64-release
|
||||||
path: Release
|
path: PiliPlus_windows_*.zip
|
||||||
|
|
||||||
- name: Upload windows setup release
|
- name: Upload windows setup release
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ android {
|
|||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlin {
|
||||||
jvmTarget = JavaVersion.VERSION_17.toString()
|
compilerOptions {
|
||||||
|
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
@@ -62,10 +64,10 @@ android {
|
|||||||
value = "PiliPlus dev",
|
value = "PiliPlus dev",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
proguardFiles(
|
// proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
// getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
"proguard-rules.pro"
|
// "proguard-rules.pro"
|
||||||
)
|
// )
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
applicationIdSuffix = ".debug"
|
applicationIdSuffix = ".debug"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.example.piliplus">
|
package="com.example.piliplus">
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -16,8 +17,7 @@
|
|||||||
</queries>
|
</queries>
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name=
|
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
||||||
"android.support.customtabs.action.CustomTabsService" />
|
|
||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
|
|
||||||
@@ -35,56 +35,62 @@
|
|||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
|
|
||||||
<application
|
<application xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:enableOnBackInvokedCallback="false"
|
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
android:enableOnBackInvokedCallback="false"
|
||||||
android:fullBackupContent="false"
|
android:fullBackupContent="false"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
tools:replace="android:allowBackup">
|
tools:replace="android:allowBackup">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||||
android:value="false" />
|
android:value="false" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:theme="@style/LaunchTheme"
|
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:exported="true"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:launchMode="singleTask"
|
||||||
android:supportsPictureInPicture="true"
|
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
>
|
android:supportsPictureInPicture="true"
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
|
||||||
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
|
<meta-data
|
||||||
|
android:name="android.app.shortcuts"
|
||||||
|
android:resource="@xml/shortcuts" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="flutter_deeplinking_enabled"
|
||||||
|
android:value="false" />
|
||||||
|
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
to determine the Window background behind the Flutter UI. -->
|
to determine the Window background behind the Flutter UI. -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
android:resource="@style/NormalTheme"
|
android:resource="@style/NormalTheme" />
|
||||||
/>
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter android:label="PiliPlus">
|
<intent-filter android:label="PiliPlus">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data android:scheme="http"/>
|
|
||||||
<data android:scheme="https"/>
|
<data android:scheme="http" />
|
||||||
<data android:host="*.bilibili.com"/>
|
<data android:scheme="https" />
|
||||||
<data android:host="*.bilibili.cn"/>
|
<data android:host="*.bilibili.com" />
|
||||||
<data android:host="*.bilibili.tv"/>
|
<data android:host="*.bilibili.cn" />
|
||||||
<data android:host="bilibili.com"/>
|
<data android:host="*.bilibili.tv" />
|
||||||
<data android:host="bilibili.cn"/>
|
<data android:host="bilibili.com" />
|
||||||
<data android:host="bilibili.tv"/>
|
<data android:host="bilibili.cn" />
|
||||||
|
<data android:host="bilibili.tv" />
|
||||||
<data android:host="b23.tv" />
|
<data android:host="b23.tv" />
|
||||||
<!--<data android:host="live.bilibili.com"/>-->
|
<!--<data android:host="live.bilibili.com"/>-->
|
||||||
<!--<data android:host="www.bilibili.com"/>-->
|
<!--<data android:host="www.bilibili.com"/>-->
|
||||||
@@ -100,36 +106,56 @@
|
|||||||
<intent-filter android:label="PiliPlus">
|
<intent-filter android:label="PiliPlus">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
<action android:name="android.intent.action.SEARCH" />
|
||||||
|
<action android:name="com.example.piliplus.SHORTCUT" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data android:scheme="bilibili"/>
|
|
||||||
|
<data android:scheme="bilibili" />
|
||||||
|
<data android:host="download" />
|
||||||
<data android:host="forward" />
|
<data android:host="forward" />
|
||||||
<data android:host="comment"
|
<data
|
||||||
|
android:host="comment"
|
||||||
android:pathPattern="/detail/.*/.*/.*" />
|
android:pathPattern="/detail/.*/.*/.*" />
|
||||||
<data android:host="uper" />
|
<data android:host="uper" />
|
||||||
<data android:host="article"
|
<data
|
||||||
|
android:host="article"
|
||||||
android:pathPattern="/readlist" />
|
android:pathPattern="/readlist" />
|
||||||
<data android:host="opus" />
|
<data android:host="opus" />
|
||||||
<data android:host="advertise" android:path="/home" />
|
<data
|
||||||
|
android:host="advertise"
|
||||||
|
android:path="/home" />
|
||||||
<data android:host="clip" />
|
<data android:host="clip" />
|
||||||
<data android:host="search" android:pathPattern=".*" />
|
<data
|
||||||
|
android:host="search"
|
||||||
|
android:pathPattern=".*" />
|
||||||
<data android:host="stardust-search" />
|
<data android:host="stardust-search" />
|
||||||
<data android:host="music" />
|
<data android:host="music" />
|
||||||
<data android:host="cheese" />
|
<data android:host="cheese" />
|
||||||
<data android:host="bangumi"
|
<data
|
||||||
|
android:host="bangumi"
|
||||||
android:pathPattern="/season.*" />
|
android:pathPattern="/season.*" />
|
||||||
<data android:host="bangumi" android:pathPattern="/.*" />
|
<data
|
||||||
<data android:host="pictureshow"
|
android:host="bangumi"
|
||||||
|
android:pathPattern="/.*" />
|
||||||
|
<data
|
||||||
|
android:host="pictureshow"
|
||||||
android:pathPrefix="/creative_center" />
|
android:pathPrefix="/creative_center" />
|
||||||
<data android:host="cliparea" />
|
<data android:host="cliparea" />
|
||||||
<data android:host="im" />
|
<data android:host="im" />
|
||||||
<data android:host="im" android:path="/notifications" />
|
<data
|
||||||
|
android:host="im"
|
||||||
|
android:path="/notifications" />
|
||||||
<data android:host="following" />
|
<data android:host="following" />
|
||||||
<data android:host="following"
|
<data
|
||||||
|
android:host="following"
|
||||||
android:pathPattern="/detail/.*" />
|
android:pathPattern="/detail/.*" />
|
||||||
<data android:host="following"
|
<data
|
||||||
|
android:host="following"
|
||||||
android:path="/publishInfo/" />
|
android:path="/publishInfo/" />
|
||||||
<data android:host="laser" android:pathPattern="/.*" />
|
<data
|
||||||
|
android:host="laser"
|
||||||
|
android:pathPattern="/.*" />
|
||||||
<data android:host="livearea" />
|
<data android:host="livearea" />
|
||||||
<data android:host="live" />
|
<data android:host="live" />
|
||||||
<data android:host="catalog" />
|
<data android:host="catalog" />
|
||||||
@@ -147,28 +173,44 @@
|
|||||||
<data android:host="video" />
|
<data android:host="video" />
|
||||||
<data android:host="story" />
|
<data android:host="story" />
|
||||||
<data android:host="podcast" />
|
<data android:host="podcast" />
|
||||||
<data android:host="main" android:path="/favorite" />
|
<data
|
||||||
<data android:host="pgc" android:path="/theater/match" />
|
android:host="main"
|
||||||
<data android:host="pgc" android:path="/theater/square" />
|
android:path="/favorite" />
|
||||||
<data android:host="m.bilibili.com"
|
<data
|
||||||
|
android:host="pgc"
|
||||||
|
android:path="/theater/match" />
|
||||||
|
<data
|
||||||
|
android:host="pgc"
|
||||||
|
android:path="/theater/square" />
|
||||||
|
<data
|
||||||
|
android:host="m.bilibili.com"
|
||||||
android:path="/topic-detail" />
|
android:path="/topic-detail" />
|
||||||
<data android:host="article" />
|
<data android:host="article" />
|
||||||
<data android:host="pegasus"
|
<data
|
||||||
|
android:host="pegasus"
|
||||||
android:pathPattern="/channel/v2/.*" />
|
android:pathPattern="/channel/v2/.*" />
|
||||||
<data android:host="feed" android:pathPattern="/channel" />
|
<data
|
||||||
|
android:host="feed"
|
||||||
|
android:pathPattern="/channel" />
|
||||||
<data android:host="vip" />
|
<data android:host="vip" />
|
||||||
<data android:host="user_center" android:path="/vip" />
|
<data
|
||||||
|
android:host="user_center"
|
||||||
|
android:path="/vip" />
|
||||||
<data android:host="history" />
|
<data android:host="history" />
|
||||||
<data android:host="charge" android:path="/rank" />
|
<data
|
||||||
|
android:host="charge"
|
||||||
|
android:path="/rank" />
|
||||||
<data android:host="assistant" />
|
<data android:host="assistant" />
|
||||||
<data android:host="feedback" />
|
<data android:host="feedback" />
|
||||||
<data android:host="auth" android:path="/launch" />
|
<data
|
||||||
|
android:host="auth"
|
||||||
|
android:path="/launch" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<service
|
<service
|
||||||
android:name="com.ryanheise.audioservice.AudioService"
|
android:name="com.ryanheise.audioservice.AudioService"
|
||||||
|
android:exported="true"
|
||||||
android:foregroundServiceType="mediaPlayback"
|
android:foregroundServiceType="mediaPlayback"
|
||||||
android:exported="true"
|
|
||||||
tools:ignore="Instantiatable">
|
tools:ignore="Instantiatable">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.media.browse.MediaBrowserService" />
|
<action android:name="android.media.browse.MediaBrowserService" />
|
||||||
@@ -177,32 +219,37 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="com.yalantis.ucrop.UCropActivity"
|
android:name="com.yalantis.ucrop.UCropActivity"
|
||||||
android:theme="@style/Ucrop.CropTheme"/>
|
android:theme="@style/Ucrop.CropTheme" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
|
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
tools:ignore="Instantiatable">
|
tools:ignore="Instantiatable">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
|
<uses-permission
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
android:maxSdkVersion="32" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
android:maxSdkVersion="28" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<!--
|
<!--
|
||||||
Media access permissions.
|
Media access permissions.
|
||||||
Android 13 or higher.
|
Android 13 or higher.
|
||||||
@@ -210,5 +257,5 @@
|
|||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||||
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
|
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
package com.example.piliplus
|
package com.example.piliplus
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.app.PictureInPictureParams
|
import android.app.PictureInPictureParams
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.pm.ShortcutInfo
|
||||||
|
import android.content.pm.ShortcutManager
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.drawable.Icon
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
@@ -16,6 +21,7 @@ import com.ryanheise.audioservice.AudioServiceActivity
|
|||||||
import io.flutter.embedding.engine.FlutterEngine
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class MainActivity : AudioServiceActivity() {
|
class MainActivity : AudioServiceActivity() {
|
||||||
private lateinit var methodChannel: MethodChannel
|
private lateinit var methodChannel: MethodChannel
|
||||||
@@ -133,6 +139,38 @@ class MainActivity : AudioServiceActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"createShortcut" -> {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
try {
|
||||||
|
val shortcutManager =
|
||||||
|
context.getSystemService(ShortcutManager::class.java)
|
||||||
|
if (shortcutManager.isRequestPinShortcutSupported) {
|
||||||
|
val id = call.argument<String>("id")!!
|
||||||
|
val uri = call.argument<String>("uri")!!
|
||||||
|
val label = call.argument<String>("label")!!
|
||||||
|
val icon = call.argument<String>("icon")!!
|
||||||
|
val bitmap = BitmapFactory.decodeFile(icon)
|
||||||
|
val shortcut =
|
||||||
|
ShortcutInfo.Builder(context, id)
|
||||||
|
.setShortLabel(label)
|
||||||
|
.setIcon(Icon.createWithAdaptiveBitmap(bitmap))
|
||||||
|
.setIntent(Intent(Intent.ACTION_VIEW, uri.toUri()))
|
||||||
|
.build()
|
||||||
|
val pinIntent =
|
||||||
|
shortcutManager.createShortcutResultIntent(shortcut)
|
||||||
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
|
context, 0, pinIntent, PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
shortcutManager.requestPinShortcut(
|
||||||
|
shortcut,
|
||||||
|
pendingIntent.intentSender
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
android/app/src/main/res/drawable/download.xml
Normal file
10
android/app/src/main/res/drawable/download.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#515151"
|
||||||
|
android:pathData="M16.59 9H15V4c0-.55-.45-1-1-1h-4c-.55 0-1 .45-1 1v5H7.41c-.89 0-1.34 1.08-.71 1.71l4.59 4.59c.39.39 1.02.39 1.41 0l4.59-4.59c.63-.63.19-1.71-.7-1.71zM5 19c0 .55.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1H6c-.55 0-1 .45-1 1z" />
|
||||||
|
</vector>
|
||||||
10
android/app/src/main/res/drawable/search.xml
Normal file
10
android/app/src/main/res/drawable/search.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#515151"
|
||||||
|
android:pathData="M15.5 14h-.79l-.28-.27c1.2-1.4 1.82-3.31 1.48-5.34-.47-2.78-2.79-5-5.59-5.34-4.23-.52-7.79 3.04-7.27 7.27.34 2.8 2.56 5.12 5.34 5.59 2.03.34 3.94-.28 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" />
|
||||||
|
</vector>
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">PiliPlus</string>
|
<string name="app_name">PiliPlus</string>
|
||||||
|
<string name="search">搜索</string>
|
||||||
|
<string name="offline_video">离线视频</string>
|
||||||
</resources>
|
</resources>
|
||||||
20
android/app/src/main/res/xml-v25/shortcuts.xml
Normal file
20
android/app/src/main/res/xml-v25/shortcuts.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<shortcut
|
||||||
|
android:icon="@drawable/search"
|
||||||
|
android:shortcutId="search"
|
||||||
|
android:shortcutLongLabel="@string/search"
|
||||||
|
android:shortcutShortLabel="@string/search">
|
||||||
|
<intent
|
||||||
|
android:action="com.example.piliplus.SHORTCUT"
|
||||||
|
android:data="bilibili://search" />
|
||||||
|
</shortcut>
|
||||||
|
<shortcut
|
||||||
|
android:icon="@drawable/download"
|
||||||
|
android:shortcutId="offline_video"
|
||||||
|
android:shortcutLongLabel="@string/offline_video"
|
||||||
|
android:shortcutShortLabel="@string/offline_video">
|
||||||
|
<intent
|
||||||
|
android:action="com.example.piliplus.SHORTCUT"
|
||||||
|
android:data="bilibili://download" />
|
||||||
|
</shortcut>
|
||||||
|
</shortcuts>
|
||||||
@@ -20,7 +20,5 @@
|
|||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>MinimumOSVersion</key>
|
|
||||||
<string>13.0</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- audio_session (0.0.1):
|
- audio_session (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- auto_orientation (0.0.1):
|
- battery_plus (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- chat_bottom_container (0.0.1):
|
- chat_bottom_container (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
@@ -68,9 +68,9 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- GT3Captcha-iOS
|
- GT3Captcha-iOS
|
||||||
- GT3Captcha-iOS (0.15.8.3)
|
- GT3Captcha-iOS (0.15.8.3)
|
||||||
- image_cropper (0.0.4):
|
- image_cropper (0.0.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- TOCropViewController (~> 2.8.0)
|
- TOCropViewController (~> 3.1.1)
|
||||||
- image_picker_ios (0.0.1):
|
- image_picker_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- live_photo_maker (0.0.3):
|
- live_photo_maker (0.0.3):
|
||||||
@@ -81,12 +81,11 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- media_kit_video (0.0.1):
|
- media_kit_video (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- native_device_orientation (0.0.1):
|
||||||
|
- Flutter
|
||||||
- OrderedSet (6.0.3)
|
- OrderedSet (6.0.3)
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- path_provider_foundation (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- permission_handler_apple (9.3.0):
|
- permission_handler_apple (9.3.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- saver_gallery (0.0.1):
|
- saver_gallery (0.0.1):
|
||||||
@@ -105,7 +104,7 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- SwiftyGif (5.4.5)
|
- SwiftyGif (5.4.5)
|
||||||
- TOCropViewController (2.8.0)
|
- TOCropViewController (3.1.1)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- wakelock_plus (0.0.1):
|
- wakelock_plus (0.0.1):
|
||||||
@@ -115,7 +114,7 @@ DEPENDENCIES:
|
|||||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||||
- audio_service (from `.symlinks/plugins/audio_service/darwin`)
|
- audio_service (from `.symlinks/plugins/audio_service/darwin`)
|
||||||
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
||||||
- auto_orientation (from `.symlinks/plugins/auto_orientation/ios`)
|
- battery_plus (from `.symlinks/plugins/battery_plus/ios`)
|
||||||
- chat_bottom_container (from `.symlinks/plugins/chat_bottom_container/ios`)
|
- chat_bottom_container (from `.symlinks/plugins/chat_bottom_container/ios`)
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
@@ -133,8 +132,8 @@ DEPENDENCIES:
|
|||||||
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
||||||
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
|
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
|
||||||
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
|
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
|
||||||
|
- native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- saver_gallery (from `.symlinks/plugins/saver_gallery/ios`)
|
- saver_gallery (from `.symlinks/plugins/saver_gallery/ios`)
|
||||||
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
||||||
@@ -161,8 +160,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/audio_service/darwin"
|
:path: ".symlinks/plugins/audio_service/darwin"
|
||||||
audio_session:
|
audio_session:
|
||||||
:path: ".symlinks/plugins/audio_session/ios"
|
:path: ".symlinks/plugins/audio_session/ios"
|
||||||
auto_orientation:
|
battery_plus:
|
||||||
:path: ".symlinks/plugins/auto_orientation/ios"
|
:path: ".symlinks/plugins/battery_plus/ios"
|
||||||
chat_bottom_container:
|
chat_bottom_container:
|
||||||
:path: ".symlinks/plugins/chat_bottom_container/ios"
|
:path: ".symlinks/plugins/chat_bottom_container/ios"
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
@@ -197,10 +196,10 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
|
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
|
||||||
media_kit_video:
|
media_kit_video:
|
||||||
:path: ".symlinks/plugins/media_kit_video/ios"
|
:path: ".symlinks/plugins/media_kit_video/ios"
|
||||||
|
native_device_orientation:
|
||||||
|
:path: ".symlinks/plugins/native_device_orientation/ios"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
saver_gallery:
|
saver_gallery:
|
||||||
@@ -219,44 +218,44 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/wakelock_plus/ios"
|
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
app_links: 6d01271b3907b0ee7325c5297c75d697c4226c4d
|
app_links: a754cbec3c255bd4bbb4d236ecc06f28cd9a7ce8
|
||||||
audio_service: cab6c1a0eaf01b5a35b567e11fa67d3cc1956910
|
audio_service: aa99a6ba2ae7565996015322b0bb024e1d25c6fd
|
||||||
audio_session: 19e9480dbdd4e5f6c4543826b2e8b0e4ab6145fe
|
audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0
|
||||||
auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d
|
battery_plus: b42253f6d2dde71712f8c36fef456d99121c5977
|
||||||
chat_bottom_container: d8b077152c91b0ab90001e900748ea50353a5520
|
chat_bottom_container: f1eb8323db77a87db50f361142c679f11e892d1b
|
||||||
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
|
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||||
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
|
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
||||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
|
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
|
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||||
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
|
flutter_mailer: 3a8cd4f36c960fb04528d5471097270c19fec1c4
|
||||||
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
|
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||||
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
|
flutter_volume_controller: c2be490cb0487e8b88d0d9fc2b7e1c139a4ebccb
|
||||||
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
|
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||||
gt3_flutter_plugin: 5bd2c08d3c19cbb6ee3b08f4358439e54c8ab2ee
|
gt3_flutter_plugin: 37090e5fa66ff2a52939eb9d208fc36fa49d36e5
|
||||||
GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6
|
GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6
|
||||||
image_cropper: b8ef14d3fcff4040b0f9da2ca28d98219a5cba0e
|
image_cropper: e405d3e44183f8e8edbec2e49b01ff9c819c7ac8
|
||||||
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b
|
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
||||||
live_photo_maker: 7d57bfc70a120b4673c10871f354f4b1b6fde5fd
|
live_photo_maker: 29280ca88323bd5a33aafd00d98624d5cf522176
|
||||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
|
||||||
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
|
media_kit_native_event_loop: 5fba1a849a6c87a34985f1e178a0de5bd444a0cf
|
||||||
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
|
||||||
|
native_device_orientation: e3580675687d5034770da198f6839ebf2122ef94
|
||||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
|
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
saver_gallery: af2d0c762dafda254e0ad025ef0dabd6506cd490
|
||||||
saver_gallery: 76172dc4bf6b40e66d694948ada9ff402304dd87
|
screen_brightness_ios: 9953fd7da5bd480f1a93990daeec2eb42d4f3b52
|
||||||
screen_brightness_ios: 6a6f7794b67f07c4f1e24f6374b2d8ad367ffb39
|
|
||||||
SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a
|
SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a
|
||||||
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||||
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
TOCropViewController: 797deaf39c90e6e9ddd848d88817f6b9a8a09888
|
TOCropViewController: 9002a9b12d8104d7478cdc306d80f0efea7fe2c5
|
||||||
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa
|
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||||
wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e
|
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
|
||||||
|
|
||||||
PODFILE CHECKSUM: f62db4fb414ebdecb264109948f76dfef35fdc3d
|
PODFILE CHECKSUM: f62db4fb414ebdecb264109948f76dfef35fdc3d
|
||||||
|
|
||||||
|
|||||||
@@ -131,5 +131,13 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>UIStatusBarHidden</key>
|
<key>UIStatusBarHidden</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
|
<string>需要访问本地网络以发现和连接 DLNA 投屏设备</string>
|
||||||
|
<key>NSBonjourServices</key>
|
||||||
|
<array>
|
||||||
|
<string>_ssdp._udp</string>
|
||||||
|
<string>_upnp._tcp</string>
|
||||||
|
<string>_http._tcp</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
56
lib/common/assets.dart
Normal file
56
lib/common/assets.dart
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
abstract final class Assets {
|
||||||
|
static const digitalNum = 'digital_id_num';
|
||||||
|
|
||||||
|
static const logo = 'assets/images/logo/logo.png';
|
||||||
|
static const logo2 = 'assets/images/logo/logo_2.png';
|
||||||
|
static const logoIco = 'assets/images/logo/ico/app_icon.ico';
|
||||||
|
static const logoLarge = 'assets/images/logo/desktop/logo_large.png';
|
||||||
|
|
||||||
|
static const vipIcon = 'assets/images/big-vip.png';
|
||||||
|
static const avatarPlaceHolder = 'assets/images/noface.jpeg';
|
||||||
|
static const loading = 'assets/images/loading.png';
|
||||||
|
static const buffering = 'assets/images/loading.webp';
|
||||||
|
static const play = 'assets/images/play.png';
|
||||||
|
static const topicHeader = 'assets/images/topic-header-bg.png';
|
||||||
|
static const trendingBanner = 'assets/images/trending_banner.png';
|
||||||
|
static const ai = 'assets/images/ai.png';
|
||||||
|
|
||||||
|
static const livingChart = 'assets/images/live.gif';
|
||||||
|
static const livingStatic = 'assets/images/live.png';
|
||||||
|
static const livingRect = 'assets/images/live/live.gif';
|
||||||
|
static const livingBackground = 'assets/images/live/default_bg.webp';
|
||||||
|
|
||||||
|
static const thunder1 = 'assets/images/paycoins/ic_thunder_1.png';
|
||||||
|
static const thunder2 = 'assets/images/paycoins/ic_thunder_2.png';
|
||||||
|
static const thunder3 = 'assets/images/paycoins/ic_thunder_3.png';
|
||||||
|
static const notEnough = 'assets/images/paycoins/ic_22_not_enough_pay.png';
|
||||||
|
static const mario = 'assets/images/paycoins/ic_22_mario.png';
|
||||||
|
static const gunSister = 'assets/images/paycoins/ic_22_gun_sister.png';
|
||||||
|
static const payBox = 'assets/images/paycoins/ic_pay_coins_box.png';
|
||||||
|
static const coinsOne = 'assets/images/paycoins/ic_coins_one.png';
|
||||||
|
static const coinsTwo = 'assets/images/paycoins/ic_coins_two.png';
|
||||||
|
static const left = 'assets/images/paycoins/ic_left.png';
|
||||||
|
static const leftDisable = 'assets/images/paycoins/ic_left_disable.png';
|
||||||
|
static const right = 'assets/images/paycoins/ic_right.png';
|
||||||
|
static const rightDisable = 'assets/images/paycoins/ic_right_disable.png';
|
||||||
|
static const panelClose = 'assets/images/paycoins/ic_panel_close.png';
|
||||||
|
|
||||||
|
static const List<String> mpvAnime4KShaders = [
|
||||||
|
'Anime4K_Clamp_Highlights.glsl',
|
||||||
|
'Anime4K_Restore_CNN_VL.glsl',
|
||||||
|
'Anime4K_Upscale_CNN_x2_VL.glsl',
|
||||||
|
'Anime4K_AutoDownscalePre_x2.glsl',
|
||||||
|
'Anime4K_AutoDownscalePre_x4.glsl',
|
||||||
|
'Anime4K_Upscale_CNN_x2_M.glsl',
|
||||||
|
];
|
||||||
|
|
||||||
|
static const mpvAnime4KShadersLite = [
|
||||||
|
'Anime4K_Clamp_Highlights.glsl',
|
||||||
|
'Anime4K_Restore_CNN_M.glsl',
|
||||||
|
'Anime4K_Restore_CNN_S.glsl',
|
||||||
|
'Anime4K_Upscale_CNN_x2_M.glsl',
|
||||||
|
'Anime4K_AutoDownscalePre_x2.glsl',
|
||||||
|
'Anime4K_AutoDownscalePre_x4.glsl',
|
||||||
|
'Anime4K_Upscale_CNN_x2_S.glsl',
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,30 +1,3 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
abstract final class StyleString {
|
|
||||||
static const double cardSpace = 8;
|
|
||||||
static const double safeSpace = 12;
|
|
||||||
static const BorderRadius mdRadius = BorderRadius.all(imgRadius);
|
|
||||||
static const Radius imgRadius = Radius.circular(10);
|
|
||||||
static const double aspectRatio = 16 / 10;
|
|
||||||
static const double aspectRatio16x9 = 16 / 9;
|
|
||||||
static const double imgMaxRatio = 2.6;
|
|
||||||
static const bottomSheetRadius = BorderRadius.vertical(
|
|
||||||
top: Radius.circular(18),
|
|
||||||
);
|
|
||||||
static const dialogFixedConstraints = BoxConstraints(
|
|
||||||
minWidth: 420,
|
|
||||||
maxWidth: 420,
|
|
||||||
);
|
|
||||||
static const topBarHeight = 52.0;
|
|
||||||
static const buttonStyle = ButtonStyle(
|
|
||||||
visualDensity: VisualDensity(
|
|
||||||
horizontal: -2,
|
|
||||||
vertical: -1.25,
|
|
||||||
),
|
|
||||||
tapTargetSize: .shrinkWrap,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract final class Constants {
|
abstract final class Constants {
|
||||||
static const appName = 'PiliPlus';
|
static const appName = 'PiliPlus';
|
||||||
static const sourceCodeUrl = 'https://github.com/bggRGjQaUbCoE/PiliPlus';
|
static const sourceCodeUrl = 'https://github.com/bggRGjQaUbCoE/PiliPlus';
|
||||||
@@ -68,244 +41,4 @@ abstract final class Constants {
|
|||||||
|
|
||||||
// 'itemOpusStyle,opusBigCover,onlyfansVote,endFooterHidden,decorationCard,onlyfansAssetsV2,ugcDelete,onlyfansQaCard,editable,opusPrivateVisible,avatarAutoTheme,sunflowerStyle,cardsEnhance,eva3CardOpus,eva3CardVideo,eva3CardComment,eva3CardVote,eva3CardUser'
|
// 'itemOpusStyle,opusBigCover,onlyfansVote,endFooterHidden,decorationCard,onlyfansAssetsV2,ugcDelete,onlyfansQaCard,editable,opusPrivateVisible,avatarAutoTheme,sunflowerStyle,cardsEnhance,eva3CardOpus,eva3CardVideo,eva3CardComment,eva3CardVote,eva3CardUser'
|
||||||
static const dynFeatures = 'itemOpusStyle,listOnlyfans,onlyfansQaCard';
|
static const dynFeatures = 'itemOpusStyle,listOnlyfans,onlyfansQaCard';
|
||||||
|
|
||||||
// 超分辨率滤镜
|
|
||||||
static const List<String> mpvAnime4KShaders = [
|
|
||||||
'Anime4K_Clamp_Highlights.glsl',
|
|
||||||
'Anime4K_Restore_CNN_VL.glsl',
|
|
||||||
'Anime4K_Upscale_CNN_x2_VL.glsl',
|
|
||||||
'Anime4K_AutoDownscalePre_x2.glsl',
|
|
||||||
'Anime4K_AutoDownscalePre_x4.glsl',
|
|
||||||
'Anime4K_Upscale_CNN_x2_M.glsl',
|
|
||||||
];
|
|
||||||
|
|
||||||
// 超分辨率滤镜 (轻量)
|
|
||||||
static const mpvAnime4KShadersLite = [
|
|
||||||
'Anime4K_Clamp_Highlights.glsl',
|
|
||||||
'Anime4K_Restore_CNN_M.glsl',
|
|
||||||
'Anime4K_Restore_CNN_S.glsl',
|
|
||||||
'Anime4K_Upscale_CNN_x2_M.glsl',
|
|
||||||
'Anime4K_AutoDownscalePre_x2.glsl',
|
|
||||||
'Anime4K_AutoDownscalePre_x4.glsl',
|
|
||||||
'Anime4K_Upscale_CNN_x2_S.glsl',
|
|
||||||
];
|
|
||||||
|
|
||||||
//内容来自 https://passport.bilibili.com/web/generic/country/list
|
|
||||||
static const internationalDialingPrefix = [
|
|
||||||
(id: 1, cname: "中国大陆", countryId: 86),
|
|
||||||
(id: 5, cname: "中国香港特别行政区", countryId: 852),
|
|
||||||
(id: 2, cname: "中国澳门特别行政区", countryId: 853),
|
|
||||||
(id: 3, cname: "中国台湾", countryId: 886),
|
|
||||||
(id: 4, cname: "美国", countryId: 1),
|
|
||||||
(id: 6, cname: "比利时", countryId: 32),
|
|
||||||
(id: 7, cname: "澳大利亚", countryId: 61),
|
|
||||||
(id: 8, cname: "法国", countryId: 33),
|
|
||||||
(id: 9, cname: "加拿大", countryId: 1),
|
|
||||||
(id: 10, cname: "日本", countryId: 81),
|
|
||||||
(id: 11, cname: "新加坡", countryId: 65),
|
|
||||||
(id: 12, cname: "韩国", countryId: 82),
|
|
||||||
(id: 13, cname: "马来西亚", countryId: 60),
|
|
||||||
(id: 14, cname: "英国", countryId: 44),
|
|
||||||
(id: 15, cname: "意大利", countryId: 39),
|
|
||||||
(id: 16, cname: "德国", countryId: 49),
|
|
||||||
(id: 18, cname: "俄罗斯", countryId: 7),
|
|
||||||
(id: 19, cname: "新西兰", countryId: 64),
|
|
||||||
(id: 153, cname: "瓦利斯群岛和富图纳群岛", countryId: 1681),
|
|
||||||
(id: 152, cname: "葡萄牙", countryId: 351),
|
|
||||||
(id: 151, cname: "帕劳", countryId: 680),
|
|
||||||
(id: 150, cname: "诺福克岛", countryId: 672),
|
|
||||||
(id: 149, cname: "挪威", countryId: 47),
|
|
||||||
(id: 148, cname: "纽埃岛", countryId: 683),
|
|
||||||
(id: 147, cname: "尼日利亚", countryId: 234),
|
|
||||||
(id: 146, cname: "尼日尔", countryId: 227),
|
|
||||||
(id: 145, cname: "尼加拉瓜", countryId: 505),
|
|
||||||
(id: 144, cname: "尼泊尔", countryId: 977),
|
|
||||||
(id: 143, cname: "瑙鲁", countryId: 674),
|
|
||||||
(id: 154, cname: "格鲁吉亚", countryId: 995),
|
|
||||||
(id: 155, cname: "瑞典", countryId: 46),
|
|
||||||
(id: 165, cname: "沙特阿拉伯", countryId: 966),
|
|
||||||
(id: 164, cname: "桑给巴尔岛", countryId: 259),
|
|
||||||
(id: 163, cname: "塞舌尔共和国", countryId: 248),
|
|
||||||
(id: 162, cname: "塞浦路斯", countryId: 357),
|
|
||||||
(id: 161, cname: "塞内加尔", countryId: 221),
|
|
||||||
(id: 160, cname: "塞拉利昂", countryId: 232),
|
|
||||||
(id: 159, cname: "萨摩亚,东部", countryId: 684),
|
|
||||||
(id: 158, cname: "萨摩亚,西部", countryId: 685),
|
|
||||||
(id: 157, cname: "萨尔瓦多", countryId: 503),
|
|
||||||
(id: 156, cname: "瑞士", countryId: 41),
|
|
||||||
(id: 166, cname: "圣多美和普林西比", countryId: 239),
|
|
||||||
(id: 142, cname: "塞尔维亚", countryId: 381),
|
|
||||||
(id: 141, cname: "南非", countryId: 27),
|
|
||||||
(id: 128, cname: "毛里塔尼亚", countryId: 222),
|
|
||||||
(id: 127, cname: "毛里求斯", countryId: 230),
|
|
||||||
(id: 126, cname: "马歇尔岛", countryId: 692),
|
|
||||||
(id: 125, cname: "马提尼克岛", countryId: 596),
|
|
||||||
(id: 124, cname: "马其顿", countryId: 389),
|
|
||||||
(id: 123, cname: "马里亚纳岛", countryId: 1670),
|
|
||||||
(id: 122, cname: "马里", countryId: 223),
|
|
||||||
(id: 121, cname: "马拉维", countryId: 265),
|
|
||||||
(id: 120, cname: "马耳他", countryId: 356),
|
|
||||||
(id: 119, cname: "马尔代夫", countryId: 960),
|
|
||||||
(id: 129, cname: "蒙古", countryId: 976),
|
|
||||||
(id: 130, cname: "蒙特塞拉特岛", countryId: 1664),
|
|
||||||
(id: 140, cname: "纳米比亚", countryId: 264),
|
|
||||||
(id: 139, cname: "墨西哥", countryId: 52),
|
|
||||||
(id: 138, cname: "莫桑比克", countryId: 258),
|
|
||||||
(id: 137, cname: "摩纳哥", countryId: 377),
|
|
||||||
(id: 136, cname: "摩洛哥", countryId: 212),
|
|
||||||
(id: 135, cname: "摩尔多瓦", countryId: 373),
|
|
||||||
(id: 134, cname: "缅甸", countryId: 95),
|
|
||||||
(id: 133, cname: "密克罗尼西亚", countryId: 691),
|
|
||||||
(id: 132, cname: "秘鲁", countryId: 51),
|
|
||||||
(id: 131, cname: "孟加拉国", countryId: 880),
|
|
||||||
(id: 118, cname: "马达加斯加", countryId: 261),
|
|
||||||
(id: 167, cname: "圣卢西亚", countryId: 1784),
|
|
||||||
(id: 216, cname: "智利", countryId: 56),
|
|
||||||
(id: 203, cname: "牙买加", countryId: 1876),
|
|
||||||
(id: 202, cname: "叙利亚", countryId: 963),
|
|
||||||
(id: 201, cname: "匈牙利", countryId: 36),
|
|
||||||
(id: 200, cname: "科特迪瓦", countryId: 225),
|
|
||||||
(id: 199, cname: "希腊", countryId: 30),
|
|
||||||
(id: 198, cname: "西班牙", countryId: 34),
|
|
||||||
(id: 197, cname: "乌兹别克斯坦", countryId: 998),
|
|
||||||
(id: 196, cname: "乌拉圭", countryId: 598),
|
|
||||||
(id: 195, cname: "乌克兰", countryId: 380),
|
|
||||||
(id: 194, cname: "乌干达", countryId: 256),
|
|
||||||
(id: 204, cname: "亚美尼亚", countryId: 374),
|
|
||||||
(id: 205, cname: "也门", countryId: 967),
|
|
||||||
(id: 215, cname: "直布罗陀", countryId: 350),
|
|
||||||
(id: 214, cname: "乍得", countryId: 235),
|
|
||||||
(id: 213, cname: "赞比亚", countryId: 260),
|
|
||||||
(id: 212, cname: "越南", countryId: 84),
|
|
||||||
(id: 211, cname: "约旦", countryId: 962),
|
|
||||||
(id: 210, cname: "印尼", countryId: 62),
|
|
||||||
(id: 209, cname: "印度", countryId: 91),
|
|
||||||
(id: 208, cname: "以色列", countryId: 972),
|
|
||||||
(id: 207, cname: "伊朗", countryId: 98),
|
|
||||||
(id: 206, cname: "伊拉克", countryId: 964),
|
|
||||||
(id: 193, cname: "文莱", countryId: 673),
|
|
||||||
(id: 192, cname: "委内瑞拉", countryId: 58),
|
|
||||||
(id: 191, cname: "维珍群岛(英属)", countryId: 1284),
|
|
||||||
(id: 178, cname: "泰国", countryId: 66),
|
|
||||||
(id: 177, cname: "索马里", countryId: 252),
|
|
||||||
(id: 176, cname: "所罗门群岛", countryId: 677),
|
|
||||||
(id: 175, cname: "苏里南", countryId: 597),
|
|
||||||
(id: 174, cname: "苏丹", countryId: 249),
|
|
||||||
(id: 173, cname: "斯威士兰", countryId: 268),
|
|
||||||
(id: 172, cname: "斯洛文尼亚", countryId: 386),
|
|
||||||
(id: 171, cname: "斯洛伐克", countryId: 421),
|
|
||||||
(id: 170, cname: "斯里兰卡", countryId: 94),
|
|
||||||
(id: 169, cname: "圣皮埃尔和密克隆群岛", countryId: 508),
|
|
||||||
(id: 179, cname: "坦桑尼亚", countryId: 255),
|
|
||||||
(id: 180, cname: "汤加", countryId: 676),
|
|
||||||
(id: 190, cname: "维珍群岛(美属)", countryId: 1340),
|
|
||||||
(id: 189, cname: "瓦努阿图", countryId: 678),
|
|
||||||
(id: 188, cname: "托克劳岛", countryId: 690),
|
|
||||||
(id: 187, cname: "土库曼斯坦", countryId: 993),
|
|
||||||
(id: 186, cname: "土耳其", countryId: 90),
|
|
||||||
(id: 185, cname: "图瓦卢", countryId: 688),
|
|
||||||
(id: 184, cname: "突尼斯", countryId: 216),
|
|
||||||
(id: 183, cname: "阿森松岛", countryId: 247),
|
|
||||||
(id: 182, cname: "特立尼达和多巴哥", countryId: 1868),
|
|
||||||
(id: 181, cname: "特克斯和凯科斯", countryId: 1649),
|
|
||||||
(id: 168, cname: "圣马力诺", countryId: 378),
|
|
||||||
(id: 67, cname: "法属圭亚那", countryId: 594),
|
|
||||||
(id: 54, cname: "不丹", countryId: 975),
|
|
||||||
(id: 53, cname: "博茨瓦纳", countryId: 267),
|
|
||||||
(id: 52, cname: "伯利兹", countryId: 501),
|
|
||||||
(id: 51, cname: "玻利维亚", countryId: 591),
|
|
||||||
(id: 50, cname: "波兰", countryId: 48),
|
|
||||||
(id: 49, cname: "波黑", countryId: 387),
|
|
||||||
(id: 48, cname: "波多黎各", countryId: 1787),
|
|
||||||
(id: 47, cname: "冰岛", countryId: 354),
|
|
||||||
(id: 46, cname: "贝宁", countryId: 229),
|
|
||||||
(id: 45, cname: "保加利亚", countryId: 359),
|
|
||||||
(id: 55, cname: "布基纳法索", countryId: 226),
|
|
||||||
(id: 56, cname: "布隆迪", countryId: 257),
|
|
||||||
(id: 66, cname: "法属波利尼西亚", countryId: 689),
|
|
||||||
(id: 65, cname: "法罗岛", countryId: 298),
|
|
||||||
(id: 64, cname: "厄立特里亚", countryId: 291),
|
|
||||||
(id: 63, cname: "厄瓜多尔", countryId: 593),
|
|
||||||
(id: 62, cname: "多米尼加代表", countryId: 1809),
|
|
||||||
(id: 61, cname: "多米尼加", countryId: 1767),
|
|
||||||
(id: 60, cname: "多哥", countryId: 228),
|
|
||||||
(id: 59, cname: "迪戈加西亚岛", countryId: 246),
|
|
||||||
(id: 58, cname: "丹麦", countryId: 45),
|
|
||||||
(id: 57, cname: "赤道几内亚", countryId: 240),
|
|
||||||
(id: 44, cname: "百慕大群岛", countryId: 1441),
|
|
||||||
(id: 43, cname: "白俄罗斯", countryId: 375),
|
|
||||||
(id: 42, cname: "巴西", countryId: 55),
|
|
||||||
(id: 29, cname: "爱尔兰", countryId: 353),
|
|
||||||
(id: 28, cname: "埃塞俄比亚", countryId: 251),
|
|
||||||
(id: 27, cname: "埃及", countryId: 20),
|
|
||||||
(id: 26, cname: "阿塞拜疆", countryId: 994),
|
|
||||||
(id: 25, cname: "阿曼", countryId: 968),
|
|
||||||
(id: 24, cname: "阿联酋", countryId: 971),
|
|
||||||
(id: 23, cname: "阿根廷", countryId: 54),
|
|
||||||
(id: 22, cname: "阿富汗", countryId: 93),
|
|
||||||
(id: 21, cname: "阿尔及利亚", countryId: 213),
|
|
||||||
(id: 20, cname: "阿尔巴尼亚", countryId: 355),
|
|
||||||
(id: 30, cname: "爱沙尼亚", countryId: 372),
|
|
||||||
(id: 31, cname: "安道尔", countryId: 376),
|
|
||||||
(id: 41, cname: "巴拿马", countryId: 507),
|
|
||||||
(id: 40, cname: "巴林", countryId: 973),
|
|
||||||
(id: 39, cname: "巴拉圭", countryId: 595),
|
|
||||||
(id: 38, cname: "巴基斯坦", countryId: 92),
|
|
||||||
(id: 37, cname: "巴哈马群岛", countryId: 1242),
|
|
||||||
(id: 36, cname: "巴布亚新几内亚", countryId: 675),
|
|
||||||
(id: 35, cname: "巴巴多斯", countryId: 1246),
|
|
||||||
(id: 34, cname: "奥地利", countryId: 43),
|
|
||||||
(id: 33, cname: "安提瓜岛和巴布达", countryId: 1268),
|
|
||||||
(id: 32, cname: "安哥拉", countryId: 244),
|
|
||||||
(id: 68, cname: "非洲中部", countryId: 236),
|
|
||||||
(id: 117, cname: "罗马尼亚", countryId: 40),
|
|
||||||
(id: 104, cname: "科威特", countryId: 965),
|
|
||||||
(id: 103, cname: "科摩罗", countryId: 269),
|
|
||||||
(id: 102, cname: "开曼群岛", countryId: 1345),
|
|
||||||
(id: 101, cname: "卡塔尔", countryId: 974),
|
|
||||||
(id: 100, cname: "喀麦隆", countryId: 237),
|
|
||||||
(id: 99, cname: "聚会岛", countryId: 262),
|
|
||||||
(id: 98, cname: "津巴布韦", countryId: 263),
|
|
||||||
(id: 97, cname: "捷克", countryId: 420),
|
|
||||||
(id: 96, cname: "柬埔寨", countryId: 855),
|
|
||||||
(id: 95, cname: "加蓬", countryId: 241),
|
|
||||||
(id: 105, cname: "克罗地亚", countryId: 385),
|
|
||||||
(id: 106, cname: "肯尼亚", countryId: 254),
|
|
||||||
(id: 116, cname: "卢旺达", countryId: 250),
|
|
||||||
(id: 115, cname: "卢森堡", countryId: 352),
|
|
||||||
(id: 114, cname: "利比亚", countryId: 218),
|
|
||||||
(id: 113, cname: "利比里亚", countryId: 231),
|
|
||||||
(id: 112, cname: "立陶宛", countryId: 370),
|
|
||||||
(id: 111, cname: "黎巴嫩", countryId: 961),
|
|
||||||
(id: 110, cname: "老挝", countryId: 856),
|
|
||||||
(id: 109, cname: "莱索托", countryId: 266),
|
|
||||||
(id: 108, cname: "拉脱维亚", countryId: 371),
|
|
||||||
(id: 107, cname: "库克岛", countryId: 682),
|
|
||||||
(id: 94, cname: "加纳", countryId: 233),
|
|
||||||
(id: 93, cname: "几内亚比绍", countryId: 245),
|
|
||||||
(id: 92, cname: "几内亚", countryId: 224),
|
|
||||||
(id: 79, cname: "格林纳达", countryId: 1473),
|
|
||||||
(id: 78, cname: "哥斯达黎加", countryId: 506),
|
|
||||||
(id: 77, cname: "哥伦比亚", countryId: 57),
|
|
||||||
(id: 76, cname: "刚果(金)", countryId: 243),
|
|
||||||
(id: 75, cname: "刚果", countryId: 242),
|
|
||||||
(id: 74, cname: "冈比亚", countryId: 220),
|
|
||||||
(id: 73, cname: "福克兰岛", countryId: 500),
|
|
||||||
(id: 72, cname: "佛得角", countryId: 238),
|
|
||||||
(id: 71, cname: "芬兰", countryId: 358),
|
|
||||||
(id: 70, cname: "斐济", countryId: 679),
|
|
||||||
(id: 80, cname: "格陵兰岛", countryId: 299),
|
|
||||||
(id: 81, cname: "古巴", countryId: 53),
|
|
||||||
(id: 91, cname: "吉尔吉斯斯坦", countryId: 996),
|
|
||||||
(id: 90, cname: "吉布提", countryId: 253),
|
|
||||||
(id: 89, cname: "基里巴斯", countryId: 686),
|
|
||||||
(id: 88, cname: "维克岛", countryId: 1808),
|
|
||||||
(id: 87, cname: "洪都拉斯", countryId: 504),
|
|
||||||
(id: 86, cname: "荷兰", countryId: 31),
|
|
||||||
(id: 85, cname: "朝鲜", countryId: 850),
|
|
||||||
(id: 84, cname: "海地", countryId: 509),
|
|
||||||
(id: 83, cname: "关岛", countryId: 1671),
|
|
||||||
(id: 82, cname: "瓜德罗普岛", countryId: 590),
|
|
||||||
(id: 69, cname: "菲律宾", countryId: 63),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|||||||
220
lib/common/dial_prefix.dart
Normal file
220
lib/common/dial_prefix.dart
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
abstract final class Login {
|
||||||
|
//内容来自 https://passport.bilibili.com/web/generic/country/list
|
||||||
|
static const dialPrefix = [
|
||||||
|
(id: 1, cname: "中国大陆", countryId: 86),
|
||||||
|
(id: 5, cname: "中国香港特别行政区", countryId: 852),
|
||||||
|
(id: 2, cname: "中国澳门特别行政区", countryId: 853),
|
||||||
|
(id: 3, cname: "中国台湾", countryId: 886),
|
||||||
|
(id: 4, cname: "美国", countryId: 1),
|
||||||
|
(id: 6, cname: "比利时", countryId: 32),
|
||||||
|
(id: 7, cname: "澳大利亚", countryId: 61),
|
||||||
|
(id: 8, cname: "法国", countryId: 33),
|
||||||
|
(id: 9, cname: "加拿大", countryId: 1),
|
||||||
|
(id: 10, cname: "日本", countryId: 81),
|
||||||
|
(id: 11, cname: "新加坡", countryId: 65),
|
||||||
|
(id: 12, cname: "韩国", countryId: 82),
|
||||||
|
(id: 13, cname: "马来西亚", countryId: 60),
|
||||||
|
(id: 14, cname: "英国", countryId: 44),
|
||||||
|
(id: 15, cname: "意大利", countryId: 39),
|
||||||
|
(id: 16, cname: "德国", countryId: 49),
|
||||||
|
(id: 18, cname: "俄罗斯", countryId: 7),
|
||||||
|
(id: 19, cname: "新西兰", countryId: 64),
|
||||||
|
(id: 153, cname: "瓦利斯群岛和富图纳群岛", countryId: 1681),
|
||||||
|
(id: 152, cname: "葡萄牙", countryId: 351),
|
||||||
|
(id: 151, cname: "帕劳", countryId: 680),
|
||||||
|
(id: 150, cname: "诺福克岛", countryId: 672),
|
||||||
|
(id: 149, cname: "挪威", countryId: 47),
|
||||||
|
(id: 148, cname: "纽埃岛", countryId: 683),
|
||||||
|
(id: 147, cname: "尼日利亚", countryId: 234),
|
||||||
|
(id: 146, cname: "尼日尔", countryId: 227),
|
||||||
|
(id: 145, cname: "尼加拉瓜", countryId: 505),
|
||||||
|
(id: 144, cname: "尼泊尔", countryId: 977),
|
||||||
|
(id: 143, cname: "瑙鲁", countryId: 674),
|
||||||
|
(id: 154, cname: "格鲁吉亚", countryId: 995),
|
||||||
|
(id: 155, cname: "瑞典", countryId: 46),
|
||||||
|
(id: 165, cname: "沙特阿拉伯", countryId: 966),
|
||||||
|
(id: 164, cname: "桑给巴尔岛", countryId: 259),
|
||||||
|
(id: 163, cname: "塞舌尔共和国", countryId: 248),
|
||||||
|
(id: 162, cname: "塞浦路斯", countryId: 357),
|
||||||
|
(id: 161, cname: "塞内加尔", countryId: 221),
|
||||||
|
(id: 160, cname: "塞拉利昂", countryId: 232),
|
||||||
|
(id: 159, cname: "萨摩亚,东部", countryId: 684),
|
||||||
|
(id: 158, cname: "萨摩亚,西部", countryId: 685),
|
||||||
|
(id: 157, cname: "萨尔瓦多", countryId: 503),
|
||||||
|
(id: 156, cname: "瑞士", countryId: 41),
|
||||||
|
(id: 166, cname: "圣多美和普林西比", countryId: 239),
|
||||||
|
(id: 142, cname: "塞尔维亚", countryId: 381),
|
||||||
|
(id: 141, cname: "南非", countryId: 27),
|
||||||
|
(id: 128, cname: "毛里塔尼亚", countryId: 222),
|
||||||
|
(id: 127, cname: "毛里求斯", countryId: 230),
|
||||||
|
(id: 126, cname: "马歇尔岛", countryId: 692),
|
||||||
|
(id: 125, cname: "马提尼克岛", countryId: 596),
|
||||||
|
(id: 124, cname: "马其顿", countryId: 389),
|
||||||
|
(id: 123, cname: "马里亚纳岛", countryId: 1670),
|
||||||
|
(id: 122, cname: "马里", countryId: 223),
|
||||||
|
(id: 121, cname: "马拉维", countryId: 265),
|
||||||
|
(id: 120, cname: "马耳他", countryId: 356),
|
||||||
|
(id: 119, cname: "马尔代夫", countryId: 960),
|
||||||
|
(id: 129, cname: "蒙古", countryId: 976),
|
||||||
|
(id: 130, cname: "蒙特塞拉特岛", countryId: 1664),
|
||||||
|
(id: 140, cname: "纳米比亚", countryId: 264),
|
||||||
|
(id: 139, cname: "墨西哥", countryId: 52),
|
||||||
|
(id: 138, cname: "莫桑比克", countryId: 258),
|
||||||
|
(id: 137, cname: "摩纳哥", countryId: 377),
|
||||||
|
(id: 136, cname: "摩洛哥", countryId: 212),
|
||||||
|
(id: 135, cname: "摩尔多瓦", countryId: 373),
|
||||||
|
(id: 134, cname: "缅甸", countryId: 95),
|
||||||
|
(id: 133, cname: "密克罗尼西亚", countryId: 691),
|
||||||
|
(id: 132, cname: "秘鲁", countryId: 51),
|
||||||
|
(id: 131, cname: "孟加拉国", countryId: 880),
|
||||||
|
(id: 118, cname: "马达加斯加", countryId: 261),
|
||||||
|
(id: 167, cname: "圣卢西亚", countryId: 1784),
|
||||||
|
(id: 216, cname: "智利", countryId: 56),
|
||||||
|
(id: 203, cname: "牙买加", countryId: 1876),
|
||||||
|
(id: 202, cname: "叙利亚", countryId: 963),
|
||||||
|
(id: 201, cname: "匈牙利", countryId: 36),
|
||||||
|
(id: 200, cname: "科特迪瓦", countryId: 225),
|
||||||
|
(id: 199, cname: "希腊", countryId: 30),
|
||||||
|
(id: 198, cname: "西班牙", countryId: 34),
|
||||||
|
(id: 197, cname: "乌兹别克斯坦", countryId: 998),
|
||||||
|
(id: 196, cname: "乌拉圭", countryId: 598),
|
||||||
|
(id: 195, cname: "乌克兰", countryId: 380),
|
||||||
|
(id: 194, cname: "乌干达", countryId: 256),
|
||||||
|
(id: 204, cname: "亚美尼亚", countryId: 374),
|
||||||
|
(id: 205, cname: "也门", countryId: 967),
|
||||||
|
(id: 215, cname: "直布罗陀", countryId: 350),
|
||||||
|
(id: 214, cname: "乍得", countryId: 235),
|
||||||
|
(id: 213, cname: "赞比亚", countryId: 260),
|
||||||
|
(id: 212, cname: "越南", countryId: 84),
|
||||||
|
(id: 211, cname: "约旦", countryId: 962),
|
||||||
|
(id: 210, cname: "印尼", countryId: 62),
|
||||||
|
(id: 209, cname: "印度", countryId: 91),
|
||||||
|
(id: 208, cname: "以色列", countryId: 972),
|
||||||
|
(id: 207, cname: "伊朗", countryId: 98),
|
||||||
|
(id: 206, cname: "伊拉克", countryId: 964),
|
||||||
|
(id: 193, cname: "文莱", countryId: 673),
|
||||||
|
(id: 192, cname: "委内瑞拉", countryId: 58),
|
||||||
|
(id: 191, cname: "维珍群岛(英属)", countryId: 1284),
|
||||||
|
(id: 178, cname: "泰国", countryId: 66),
|
||||||
|
(id: 177, cname: "索马里", countryId: 252),
|
||||||
|
(id: 176, cname: "所罗门群岛", countryId: 677),
|
||||||
|
(id: 175, cname: "苏里南", countryId: 597),
|
||||||
|
(id: 174, cname: "苏丹", countryId: 249),
|
||||||
|
(id: 173, cname: "斯威士兰", countryId: 268),
|
||||||
|
(id: 172, cname: "斯洛文尼亚", countryId: 386),
|
||||||
|
(id: 171, cname: "斯洛伐克", countryId: 421),
|
||||||
|
(id: 170, cname: "斯里兰卡", countryId: 94),
|
||||||
|
(id: 169, cname: "圣皮埃尔和密克隆群岛", countryId: 508),
|
||||||
|
(id: 179, cname: "坦桑尼亚", countryId: 255),
|
||||||
|
(id: 180, cname: "汤加", countryId: 676),
|
||||||
|
(id: 190, cname: "维珍群岛(美属)", countryId: 1340),
|
||||||
|
(id: 189, cname: "瓦努阿图", countryId: 678),
|
||||||
|
(id: 188, cname: "托克劳岛", countryId: 690),
|
||||||
|
(id: 187, cname: "土库曼斯坦", countryId: 993),
|
||||||
|
(id: 186, cname: "土耳其", countryId: 90),
|
||||||
|
(id: 185, cname: "图瓦卢", countryId: 688),
|
||||||
|
(id: 184, cname: "突尼斯", countryId: 216),
|
||||||
|
(id: 183, cname: "阿森松岛", countryId: 247),
|
||||||
|
(id: 182, cname: "特立尼达和多巴哥", countryId: 1868),
|
||||||
|
(id: 181, cname: "特克斯和凯科斯", countryId: 1649),
|
||||||
|
(id: 168, cname: "圣马力诺", countryId: 378),
|
||||||
|
(id: 67, cname: "法属圭亚那", countryId: 594),
|
||||||
|
(id: 54, cname: "不丹", countryId: 975),
|
||||||
|
(id: 53, cname: "博茨瓦纳", countryId: 267),
|
||||||
|
(id: 52, cname: "伯利兹", countryId: 501),
|
||||||
|
(id: 51, cname: "玻利维亚", countryId: 591),
|
||||||
|
(id: 50, cname: "波兰", countryId: 48),
|
||||||
|
(id: 49, cname: "波黑", countryId: 387),
|
||||||
|
(id: 48, cname: "波多黎各", countryId: 1787),
|
||||||
|
(id: 47, cname: "冰岛", countryId: 354),
|
||||||
|
(id: 46, cname: "贝宁", countryId: 229),
|
||||||
|
(id: 45, cname: "保加利亚", countryId: 359),
|
||||||
|
(id: 55, cname: "布基纳法索", countryId: 226),
|
||||||
|
(id: 56, cname: "布隆迪", countryId: 257),
|
||||||
|
(id: 66, cname: "法属波利尼西亚", countryId: 689),
|
||||||
|
(id: 65, cname: "法罗岛", countryId: 298),
|
||||||
|
(id: 64, cname: "厄立特里亚", countryId: 291),
|
||||||
|
(id: 63, cname: "厄瓜多尔", countryId: 593),
|
||||||
|
(id: 62, cname: "多米尼加代表", countryId: 1809),
|
||||||
|
(id: 61, cname: "多米尼加", countryId: 1767),
|
||||||
|
(id: 60, cname: "多哥", countryId: 228),
|
||||||
|
(id: 59, cname: "迪戈加西亚岛", countryId: 246),
|
||||||
|
(id: 58, cname: "丹麦", countryId: 45),
|
||||||
|
(id: 57, cname: "赤道几内亚", countryId: 240),
|
||||||
|
(id: 44, cname: "百慕大群岛", countryId: 1441),
|
||||||
|
(id: 43, cname: "白俄罗斯", countryId: 375),
|
||||||
|
(id: 42, cname: "巴西", countryId: 55),
|
||||||
|
(id: 29, cname: "爱尔兰", countryId: 353),
|
||||||
|
(id: 28, cname: "埃塞俄比亚", countryId: 251),
|
||||||
|
(id: 27, cname: "埃及", countryId: 20),
|
||||||
|
(id: 26, cname: "阿塞拜疆", countryId: 994),
|
||||||
|
(id: 25, cname: "阿曼", countryId: 968),
|
||||||
|
(id: 24, cname: "阿联酋", countryId: 971),
|
||||||
|
(id: 23, cname: "阿根廷", countryId: 54),
|
||||||
|
(id: 22, cname: "阿富汗", countryId: 93),
|
||||||
|
(id: 21, cname: "阿尔及利亚", countryId: 213),
|
||||||
|
(id: 20, cname: "阿尔巴尼亚", countryId: 355),
|
||||||
|
(id: 30, cname: "爱沙尼亚", countryId: 372),
|
||||||
|
(id: 31, cname: "安道尔", countryId: 376),
|
||||||
|
(id: 41, cname: "巴拿马", countryId: 507),
|
||||||
|
(id: 40, cname: "巴林", countryId: 973),
|
||||||
|
(id: 39, cname: "巴拉圭", countryId: 595),
|
||||||
|
(id: 38, cname: "巴基斯坦", countryId: 92),
|
||||||
|
(id: 37, cname: "巴哈马群岛", countryId: 1242),
|
||||||
|
(id: 36, cname: "巴布亚新几内亚", countryId: 675),
|
||||||
|
(id: 35, cname: "巴巴多斯", countryId: 1246),
|
||||||
|
(id: 34, cname: "奥地利", countryId: 43),
|
||||||
|
(id: 33, cname: "安提瓜岛和巴布达", countryId: 1268),
|
||||||
|
(id: 32, cname: "安哥拉", countryId: 244),
|
||||||
|
(id: 68, cname: "非洲中部", countryId: 236),
|
||||||
|
(id: 117, cname: "罗马尼亚", countryId: 40),
|
||||||
|
(id: 104, cname: "科威特", countryId: 965),
|
||||||
|
(id: 103, cname: "科摩罗", countryId: 269),
|
||||||
|
(id: 102, cname: "开曼群岛", countryId: 1345),
|
||||||
|
(id: 101, cname: "卡塔尔", countryId: 974),
|
||||||
|
(id: 100, cname: "喀麦隆", countryId: 237),
|
||||||
|
(id: 99, cname: "聚会岛", countryId: 262),
|
||||||
|
(id: 98, cname: "津巴布韦", countryId: 263),
|
||||||
|
(id: 97, cname: "捷克", countryId: 420),
|
||||||
|
(id: 96, cname: "柬埔寨", countryId: 855),
|
||||||
|
(id: 95, cname: "加蓬", countryId: 241),
|
||||||
|
(id: 105, cname: "克罗地亚", countryId: 385),
|
||||||
|
(id: 106, cname: "肯尼亚", countryId: 254),
|
||||||
|
(id: 116, cname: "卢旺达", countryId: 250),
|
||||||
|
(id: 115, cname: "卢森堡", countryId: 352),
|
||||||
|
(id: 114, cname: "利比亚", countryId: 218),
|
||||||
|
(id: 113, cname: "利比里亚", countryId: 231),
|
||||||
|
(id: 112, cname: "立陶宛", countryId: 370),
|
||||||
|
(id: 111, cname: "黎巴嫩", countryId: 961),
|
||||||
|
(id: 110, cname: "老挝", countryId: 856),
|
||||||
|
(id: 109, cname: "莱索托", countryId: 266),
|
||||||
|
(id: 108, cname: "拉脱维亚", countryId: 371),
|
||||||
|
(id: 107, cname: "库克岛", countryId: 682),
|
||||||
|
(id: 94, cname: "加纳", countryId: 233),
|
||||||
|
(id: 93, cname: "几内亚比绍", countryId: 245),
|
||||||
|
(id: 92, cname: "几内亚", countryId: 224),
|
||||||
|
(id: 79, cname: "格林纳达", countryId: 1473),
|
||||||
|
(id: 78, cname: "哥斯达黎加", countryId: 506),
|
||||||
|
(id: 77, cname: "哥伦比亚", countryId: 57),
|
||||||
|
(id: 76, cname: "刚果(金)", countryId: 243),
|
||||||
|
(id: 75, cname: "刚果", countryId: 242),
|
||||||
|
(id: 74, cname: "冈比亚", countryId: 220),
|
||||||
|
(id: 73, cname: "福克兰岛", countryId: 500),
|
||||||
|
(id: 72, cname: "佛得角", countryId: 238),
|
||||||
|
(id: 71, cname: "芬兰", countryId: 358),
|
||||||
|
(id: 70, cname: "斐济", countryId: 679),
|
||||||
|
(id: 80, cname: "格陵兰岛", countryId: 299),
|
||||||
|
(id: 81, cname: "古巴", countryId: 53),
|
||||||
|
(id: 91, cname: "吉尔吉斯斯坦", countryId: 996),
|
||||||
|
(id: 90, cname: "吉布提", countryId: 253),
|
||||||
|
(id: 89, cname: "基里巴斯", countryId: 686),
|
||||||
|
(id: 88, cname: "维克岛", countryId: 1808),
|
||||||
|
(id: 87, cname: "洪都拉斯", countryId: 504),
|
||||||
|
(id: 86, cname: "荷兰", countryId: 31),
|
||||||
|
(id: 85, cname: "朝鲜", countryId: 850),
|
||||||
|
(id: 84, cname: "海地", countryId: 509),
|
||||||
|
(id: 83, cname: "关岛", countryId: 1671),
|
||||||
|
(id: 82, cname: "瓜德罗普岛", countryId: 590),
|
||||||
|
(id: 69, cname: "菲律宾", countryId: 63),
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
||||||
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ class FavPgcItemSkeleton extends StatelessWidget {
|
|||||||
return Skeleton(
|
return Skeleton(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: StyleString.safeSpace,
|
horizontal: Style.safeSpace,
|
||||||
vertical: 5,
|
vertical: 5,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
||||||
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class MediaPgcSkeleton extends StatefulWidget {
|
class MediaPgcSkeleton extends StatefulWidget {
|
||||||
@@ -15,11 +15,9 @@ class _MediaPgcSkeletonState extends State<MediaPgcSkeleton> {
|
|||||||
Color bgColor = Theme.of(context).colorScheme.onInverseSurface;
|
Color bgColor = Theme.of(context).colorScheme.onInverseSurface;
|
||||||
return Skeleton(
|
return Skeleton(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const .symmetric(
|
||||||
StyleString.safeSpace,
|
horizontal: Style.safeSpace,
|
||||||
7,
|
vertical: 7,
|
||||||
StyleString.safeSpace,
|
|
||||||
7,
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
||||||
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class VideoCardHSkeleton extends StatelessWidget {
|
class VideoCardHSkeleton extends StatelessWidget {
|
||||||
@@ -10,25 +10,25 @@ class VideoCardHSkeleton extends StatelessWidget {
|
|||||||
final color = Theme.of(context).colorScheme.onInverseSurface;
|
final color = Theme.of(context).colorScheme.onInverseSurface;
|
||||||
return Skeleton(
|
return Skeleton(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const .symmetric(
|
||||||
horizontal: StyleString.safeSpace,
|
horizontal: Style.safeSpace,
|
||||||
vertical: 5,
|
vertical: 5,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: Style.aspectRatio,
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: color,
|
color: color,
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: Style.mdRadius,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4),
|
padding: const .fromLTRB(10, 4, 6, 4),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
|
||||||
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
||||||
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class VideoCardVSkeleton extends StatelessWidget {
|
class VideoCardVSkeleton extends StatelessWidget {
|
||||||
@@ -13,11 +13,11 @@ class VideoCardVSkeleton extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: Style.aspectRatio,
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: color,
|
color: color,
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: Style.mdRadius,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
24
lib/common/style.dart
Normal file
24
lib/common/style.dart
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import 'package:flutter/material.dart'
|
||||||
|
show BorderRadius, Radius, BoxConstraints, ButtonStyle, VisualDensity;
|
||||||
|
|
||||||
|
abstract final class Style {
|
||||||
|
static const cardSpace = 8.0;
|
||||||
|
static const safeSpace = 12.0;
|
||||||
|
static const mdRadius = BorderRadius.all(imgRadius);
|
||||||
|
static const imgRadius = Radius.circular(10);
|
||||||
|
static const aspectRatio = 16 / 10;
|
||||||
|
static const aspectRatio16x9 = 16 / 9;
|
||||||
|
static const imgMaxRatio = 2.6;
|
||||||
|
static const bottomSheetRadius = BorderRadius.vertical(
|
||||||
|
top: Radius.circular(18),
|
||||||
|
);
|
||||||
|
static const dialogFixedConstraints = BoxConstraints(
|
||||||
|
minWidth: 420,
|
||||||
|
maxWidth: 420,
|
||||||
|
);
|
||||||
|
static const topBarHeight = 52.0;
|
||||||
|
static const buttonStyle = ButtonStyle(
|
||||||
|
visualDensity: VisualDensity(horizontal: -2, vertical: -1.25),
|
||||||
|
tapTargetSize: .shrinkWrap,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,11 +5,11 @@ import 'package:flutter/material.dart';
|
|||||||
Widget avatars({
|
Widget avatars({
|
||||||
required ColorScheme colorScheme,
|
required ColorScheme colorScheme,
|
||||||
required Iterable<Owner> users,
|
required Iterable<Owner> users,
|
||||||
|
double gap = 6.0,
|
||||||
}) {
|
}) {
|
||||||
const gap = 6.0;
|
|
||||||
const size = 22.0;
|
const size = 22.0;
|
||||||
const padding = 0.8;
|
const padding = 0.8;
|
||||||
const offset = size - gap;
|
final offset = size - gap;
|
||||||
const imgSize = size - 2 * padding;
|
const imgSize = size - 2 * padding;
|
||||||
if (users.length == 1) {
|
if (users.length == 1) {
|
||||||
return NetworkImgLayer(
|
return NetworkImgLayer(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ColorPalette extends StatelessWidget {
|
class ColorPalette extends StatelessWidget {
|
||||||
@@ -62,7 +62,7 @@ class ColorPalette extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(6),
|
padding: const EdgeInsets.all(6),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: colorScheme.onInverseSurface,
|
color: colorScheme.onInverseSurface,
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: Style.mdRadius,
|
||||||
),
|
),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,23 +10,21 @@ class CustomToast extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final colorScheme = ColorScheme.of(context);
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
bottom: MediaQuery.viewPaddingOf(context).bottom + 30,
|
bottom: MediaQuery.viewPaddingOf(context).bottom + 30,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.colorScheme.primaryContainer.withValues(
|
color: colorScheme.primaryContainer.withValues(alpha: toastOpacity),
|
||||||
alpha: toastOpacity,
|
|
||||||
),
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
msg,
|
msg,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: theme.colorScheme.onPrimaryContainer,
|
color: colorScheme.onPrimaryContainer,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -41,7 +39,7 @@ class LoadingWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final onSurfaceVariant = theme.colorScheme.onSurfaceVariant;
|
final onSurfaceVariant = theme.colorScheme.onSurfaceVariant;
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
|
||||||
@@ -58,7 +56,6 @@ class LoadingWidget extends StatelessWidget {
|
|||||||
strokeWidth: 3,
|
strokeWidth: 3,
|
||||||
valueColor: AlwaysStoppedAnimation(onSurfaceVariant),
|
valueColor: AlwaysStoppedAnimation(onSurfaceVariant),
|
||||||
),
|
),
|
||||||
|
|
||||||
//msg
|
//msg
|
||||||
Text(msg, style: TextStyle(color: onSurfaceVariant)),
|
Text(msg, style: TextStyle(color: onSurfaceVariant)),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,21 +3,16 @@ import 'package:get/get.dart';
|
|||||||
|
|
||||||
Future<bool> showConfirmDialog({
|
Future<bool> showConfirmDialog({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required String title,
|
required Widget title,
|
||||||
Object? content,
|
Widget? content,
|
||||||
// @Deprecated('use `bool result = await showConfirmDialog()` instead')
|
// @Deprecated('use `bool result = await showConfirmDialog()` instead')
|
||||||
VoidCallback? onConfirm,
|
VoidCallback? onConfirm,
|
||||||
}) async {
|
}) async {
|
||||||
assert(content is String? || content is Widget);
|
|
||||||
return await showDialog<bool>(
|
return await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: Text(title),
|
title: title,
|
||||||
content: content is String
|
content: content,
|
||||||
? Text(content)
|
|
||||||
: content is Widget
|
|
||||||
? content
|
|
||||||
: null,
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: Get.back,
|
onPressed: Get.back,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'dart:async' show FutureOr;
|
|||||||
import 'dart:convert' show utf8, jsonDecode;
|
import 'dart:convert' show utf8, jsonDecode;
|
||||||
import 'dart:io' show File;
|
import 'dart:io' show File;
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart' show StyleString;
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/utils/extension/context_ext.dart';
|
import 'package:PiliPlus/utils/extension/context_ext.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
@@ -155,7 +155,7 @@ void importFromInput<T>(
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: Text('输入$title'),
|
title: Text('输入$title'),
|
||||||
constraints: StyleString.dialogFixedConstraints,
|
constraints: Style.dialogFixedConstraints,
|
||||||
content: TextFormField(
|
content: TextFormField(
|
||||||
key: key,
|
key: key,
|
||||||
minLines: 4,
|
minLines: 4,
|
||||||
|
|||||||
31
lib/common/widgets/extra_hit_test_widget.dart
Normal file
31
lib/common/widgets/extra_hit_test_widget.dart
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:flutter/rendering.dart' show RenderProxyBox, BoxHitTestResult;
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class ExtraHitTestWidget extends SingleChildRenderObjectWidget {
|
||||||
|
const ExtraHitTestWidget({
|
||||||
|
super.key,
|
||||||
|
required this.width,
|
||||||
|
required Widget super.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
|
return RenderExtraHitTestWidget(width: width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RenderExtraHitTestWidget extends RenderProxyBox {
|
||||||
|
RenderExtraHitTestWidget({
|
||||||
|
required double width,
|
||||||
|
}) : _width = width;
|
||||||
|
|
||||||
|
final double _width;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
|
||||||
|
return super.hitTestChildren(result, position: position) ||
|
||||||
|
position.dx <= _width;
|
||||||
|
}
|
||||||
|
}
|
||||||
777
lib/common/widgets/floating_navigation_bar.dart
Normal file
777
lib/common/widgets/floating_navigation_bar.dart
Normal file
@@ -0,0 +1,777 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
const double _kMaxLabelTextScaleFactor = 1.3;
|
||||||
|
|
||||||
|
const _kNavigationHeight = 64.0;
|
||||||
|
const _kIndicatorHeight = _kNavigationHeight - 2 * _kIndicatorPaddingInt;
|
||||||
|
const _kIndicatorWidth = 86.0;
|
||||||
|
const _kIndicatorPaddingInt = 4.0;
|
||||||
|
const _kIndicatorPadding = EdgeInsets.all(_kIndicatorPaddingInt);
|
||||||
|
const _kBorderRadius = BorderRadius.all(.circular(_kNavigationHeight / 2));
|
||||||
|
const _kNavigationShape = RoundedSuperellipseBorder(
|
||||||
|
borderRadius: _kBorderRadius,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// ref [NavigationBar]
|
||||||
|
class FloatingNavigationBar extends StatelessWidget {
|
||||||
|
// ignore: prefer_const_constructors_in_immutables
|
||||||
|
FloatingNavigationBar({
|
||||||
|
super.key,
|
||||||
|
this.animationDuration = const Duration(milliseconds: 500),
|
||||||
|
this.selectedIndex = 0,
|
||||||
|
required this.destinations,
|
||||||
|
this.onDestinationSelected,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.elevation,
|
||||||
|
this.shadowColor,
|
||||||
|
this.surfaceTintColor,
|
||||||
|
this.indicatorColor,
|
||||||
|
this.indicatorShape,
|
||||||
|
this.labelBehavior,
|
||||||
|
this.overlayColor,
|
||||||
|
this.labelTextStyle,
|
||||||
|
this.labelPadding,
|
||||||
|
this.bottomPadding = 8.0,
|
||||||
|
}) : assert(destinations.length >= 2),
|
||||||
|
assert(0 <= selectedIndex && selectedIndex < destinations.length);
|
||||||
|
|
||||||
|
final Duration animationDuration;
|
||||||
|
final int selectedIndex;
|
||||||
|
final List<Widget> destinations;
|
||||||
|
final ValueChanged<int>? onDestinationSelected;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
final double? elevation;
|
||||||
|
final Color? shadowColor;
|
||||||
|
final Color? surfaceTintColor;
|
||||||
|
final Color? indicatorColor;
|
||||||
|
final ShapeBorder? indicatorShape;
|
||||||
|
final NavigationDestinationLabelBehavior? labelBehavior;
|
||||||
|
final WidgetStateProperty<Color?>? overlayColor;
|
||||||
|
final WidgetStateProperty<TextStyle?>? labelTextStyle;
|
||||||
|
final EdgeInsetsGeometry? labelPadding;
|
||||||
|
final double bottomPadding;
|
||||||
|
|
||||||
|
VoidCallback _handleTap(int index) {
|
||||||
|
return onDestinationSelected != null
|
||||||
|
? () => onDestinationSelected!(index)
|
||||||
|
: () {};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final defaults = _NavigationBarDefaultsM3(context);
|
||||||
|
|
||||||
|
final navigationBarTheme = NavigationBarTheme.of(context);
|
||||||
|
final effectiveLabelBehavior =
|
||||||
|
labelBehavior ??
|
||||||
|
navigationBarTheme.labelBehavior ??
|
||||||
|
defaults.labelBehavior!;
|
||||||
|
|
||||||
|
final padding = MediaQuery.viewPaddingOf(context);
|
||||||
|
|
||||||
|
return UnconstrainedBox(
|
||||||
|
child: Padding(
|
||||||
|
padding: .fromLTRB(
|
||||||
|
padding.left,
|
||||||
|
0,
|
||||||
|
padding.right,
|
||||||
|
bottomPadding + padding.bottom,
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
height: _kNavigationHeight,
|
||||||
|
width: destinations.length * _kIndicatorWidth,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: ShapeDecoration(
|
||||||
|
color: ElevationOverlay.applySurfaceTint(
|
||||||
|
backgroundColor ??
|
||||||
|
navigationBarTheme.backgroundColor ??
|
||||||
|
defaults.backgroundColor!,
|
||||||
|
surfaceTintColor ??
|
||||||
|
navigationBarTheme.surfaceTintColor ??
|
||||||
|
defaults.surfaceTintColor,
|
||||||
|
elevation ??
|
||||||
|
navigationBarTheme.elevation ??
|
||||||
|
defaults.elevation!,
|
||||||
|
),
|
||||||
|
shape: RoundedSuperellipseBorder(
|
||||||
|
side: defaults.borderSide,
|
||||||
|
borderRadius: _kBorderRadius,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: _kIndicatorPadding,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: .stretch,
|
||||||
|
children: <Widget>[
|
||||||
|
for (int i = 0; i < destinations.length; i++)
|
||||||
|
Expanded(
|
||||||
|
child: _SelectableAnimatedBuilder(
|
||||||
|
duration: animationDuration,
|
||||||
|
isSelected: i == selectedIndex,
|
||||||
|
builder: (context, animation) {
|
||||||
|
return _NavigationDestinationInfo(
|
||||||
|
index: i,
|
||||||
|
selectedIndex: selectedIndex,
|
||||||
|
totalNumberOfDestinations: destinations.length,
|
||||||
|
selectedAnimation: animation,
|
||||||
|
labelBehavior: effectiveLabelBehavior,
|
||||||
|
indicatorColor: indicatorColor,
|
||||||
|
indicatorShape: indicatorShape,
|
||||||
|
overlayColor: overlayColor,
|
||||||
|
onTap: _handleTap(i),
|
||||||
|
labelTextStyle: labelTextStyle,
|
||||||
|
labelPadding: labelPadding,
|
||||||
|
child: destinations[i],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FloatingNavigationDestination extends StatelessWidget {
|
||||||
|
const FloatingNavigationDestination({
|
||||||
|
super.key,
|
||||||
|
required this.icon,
|
||||||
|
this.selectedIcon,
|
||||||
|
required this.label,
|
||||||
|
this.tooltip,
|
||||||
|
this.enabled = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget icon;
|
||||||
|
|
||||||
|
final Widget? selectedIcon;
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
final String? tooltip;
|
||||||
|
|
||||||
|
final bool enabled;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final info = _NavigationDestinationInfo.of(context);
|
||||||
|
const selectedState = <WidgetState>{WidgetState.selected};
|
||||||
|
const unselectedState = <WidgetState>{};
|
||||||
|
const disabledState = <WidgetState>{WidgetState.disabled};
|
||||||
|
|
||||||
|
final navigationBarTheme = NavigationBarTheme.of(context);
|
||||||
|
final defaults = _NavigationBarDefaultsM3(context);
|
||||||
|
final animation = info.selectedAnimation;
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
alignment: .center,
|
||||||
|
clipBehavior: .none,
|
||||||
|
children: [
|
||||||
|
NavigationIndicator(
|
||||||
|
animation: animation,
|
||||||
|
color:
|
||||||
|
info.indicatorColor ??
|
||||||
|
navigationBarTheme.indicatorColor ??
|
||||||
|
defaults.indicatorColor!,
|
||||||
|
),
|
||||||
|
_NavigationDestinationBuilder(
|
||||||
|
label: label,
|
||||||
|
tooltip: tooltip,
|
||||||
|
enabled: enabled,
|
||||||
|
buildIcon: (context) {
|
||||||
|
final IconThemeData selectedIconTheme =
|
||||||
|
navigationBarTheme.iconTheme?.resolve(selectedState) ??
|
||||||
|
defaults.iconTheme!.resolve(selectedState)!;
|
||||||
|
final IconThemeData unselectedIconTheme =
|
||||||
|
navigationBarTheme.iconTheme?.resolve(unselectedState) ??
|
||||||
|
defaults.iconTheme!.resolve(unselectedState)!;
|
||||||
|
final IconThemeData disabledIconTheme =
|
||||||
|
navigationBarTheme.iconTheme?.resolve(disabledState) ??
|
||||||
|
defaults.iconTheme!.resolve(disabledState)!;
|
||||||
|
|
||||||
|
final Widget selectedIconWidget = IconTheme.merge(
|
||||||
|
data: enabled ? selectedIconTheme : disabledIconTheme,
|
||||||
|
child: selectedIcon ?? icon,
|
||||||
|
);
|
||||||
|
final Widget unselectedIconWidget = IconTheme.merge(
|
||||||
|
data: enabled ? unselectedIconTheme : disabledIconTheme,
|
||||||
|
child: icon,
|
||||||
|
);
|
||||||
|
return _StatusTransitionWidgetBuilder(
|
||||||
|
animation: animation,
|
||||||
|
builder: (context, child) {
|
||||||
|
return animation.isForwardOrCompleted
|
||||||
|
? selectedIconWidget
|
||||||
|
: unselectedIconWidget;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
buildLabel: (context) {
|
||||||
|
final TextStyle? effectiveSelectedLabelTextStyle =
|
||||||
|
info.labelTextStyle?.resolve(selectedState) ??
|
||||||
|
navigationBarTheme.labelTextStyle?.resolve(selectedState) ??
|
||||||
|
defaults.labelTextStyle!.resolve(selectedState);
|
||||||
|
final TextStyle? effectiveUnselectedLabelTextStyle =
|
||||||
|
info.labelTextStyle?.resolve(unselectedState) ??
|
||||||
|
navigationBarTheme.labelTextStyle?.resolve(unselectedState) ??
|
||||||
|
defaults.labelTextStyle!.resolve(unselectedState);
|
||||||
|
final TextStyle? effectiveDisabledLabelTextStyle =
|
||||||
|
info.labelTextStyle?.resolve(disabledState) ??
|
||||||
|
navigationBarTheme.labelTextStyle?.resolve(disabledState) ??
|
||||||
|
defaults.labelTextStyle!.resolve(disabledState);
|
||||||
|
final EdgeInsetsGeometry labelPadding =
|
||||||
|
info.labelPadding ??
|
||||||
|
navigationBarTheme.labelPadding ??
|
||||||
|
defaults.labelPadding!;
|
||||||
|
|
||||||
|
final textStyle = enabled
|
||||||
|
? animation.isForwardOrCompleted
|
||||||
|
? effectiveSelectedLabelTextStyle
|
||||||
|
: effectiveUnselectedLabelTextStyle
|
||||||
|
: effectiveDisabledLabelTextStyle;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: labelPadding,
|
||||||
|
child: MediaQuery.withClampedTextScaling(
|
||||||
|
maxScaleFactor: _kMaxLabelTextScaleFactor,
|
||||||
|
child: Text(label, style: textStyle),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NavigationDestinationBuilder extends StatefulWidget {
|
||||||
|
const _NavigationDestinationBuilder({
|
||||||
|
required this.buildIcon,
|
||||||
|
required this.buildLabel,
|
||||||
|
required this.label,
|
||||||
|
this.tooltip,
|
||||||
|
this.enabled = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
final WidgetBuilder buildIcon;
|
||||||
|
|
||||||
|
final WidgetBuilder buildLabel;
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
final String? tooltip;
|
||||||
|
|
||||||
|
final bool enabled;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_NavigationDestinationBuilder> createState() =>
|
||||||
|
_NavigationDestinationBuilderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NavigationDestinationBuilderState
|
||||||
|
extends State<_NavigationDestinationBuilder> {
|
||||||
|
final GlobalKey iconKey = GlobalKey();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final info = _NavigationDestinationInfo.of(context);
|
||||||
|
|
||||||
|
final child = GestureDetector(
|
||||||
|
behavior: .opaque,
|
||||||
|
onTap: widget.enabled ? info.onTap : null,
|
||||||
|
child: _NavigationBarDestinationLayout(
|
||||||
|
icon: widget.buildIcon(context),
|
||||||
|
iconKey: iconKey,
|
||||||
|
label: widget.buildLabel(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (info.labelBehavior == .alwaysShow) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
return _NavigationBarDestinationTooltip(
|
||||||
|
message: widget.tooltip ?? widget.label,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NavigationDestinationInfo extends InheritedWidget {
|
||||||
|
const _NavigationDestinationInfo({
|
||||||
|
required this.index,
|
||||||
|
required this.selectedIndex,
|
||||||
|
required this.totalNumberOfDestinations,
|
||||||
|
required this.selectedAnimation,
|
||||||
|
required this.labelBehavior,
|
||||||
|
required this.indicatorColor,
|
||||||
|
required this.indicatorShape,
|
||||||
|
required this.overlayColor,
|
||||||
|
required this.onTap,
|
||||||
|
this.labelTextStyle,
|
||||||
|
this.labelPadding,
|
||||||
|
required super.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
final int selectedIndex;
|
||||||
|
|
||||||
|
final int totalNumberOfDestinations;
|
||||||
|
|
||||||
|
final Animation<double> selectedAnimation;
|
||||||
|
|
||||||
|
final NavigationDestinationLabelBehavior labelBehavior;
|
||||||
|
|
||||||
|
final Color? indicatorColor;
|
||||||
|
|
||||||
|
final ShapeBorder? indicatorShape;
|
||||||
|
|
||||||
|
final WidgetStateProperty<Color?>? overlayColor;
|
||||||
|
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
final WidgetStateProperty<TextStyle?>? labelTextStyle;
|
||||||
|
|
||||||
|
final EdgeInsetsGeometry? labelPadding;
|
||||||
|
|
||||||
|
static _NavigationDestinationInfo of(BuildContext context) {
|
||||||
|
final _NavigationDestinationInfo? result = context
|
||||||
|
.dependOnInheritedWidgetOfExactType<_NavigationDestinationInfo>();
|
||||||
|
assert(
|
||||||
|
result != null,
|
||||||
|
'Navigation destinations need a _NavigationDestinationInfo parent, '
|
||||||
|
'which is usually provided by NavigationBar.',
|
||||||
|
);
|
||||||
|
return result!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(_NavigationDestinationInfo oldWidget) {
|
||||||
|
return index != oldWidget.index ||
|
||||||
|
totalNumberOfDestinations != oldWidget.totalNumberOfDestinations ||
|
||||||
|
selectedAnimation != oldWidget.selectedAnimation ||
|
||||||
|
labelBehavior != oldWidget.labelBehavior ||
|
||||||
|
onTap != oldWidget.onTap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NavigationIndicator extends StatelessWidget {
|
||||||
|
const NavigationIndicator({
|
||||||
|
super.key,
|
||||||
|
required this.animation,
|
||||||
|
this.color,
|
||||||
|
this.width = _kIndicatorWidth,
|
||||||
|
this.height = _kIndicatorHeight,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Animation<double> animation;
|
||||||
|
|
||||||
|
final Color? color;
|
||||||
|
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
final double height;
|
||||||
|
|
||||||
|
static final _anim = Tween<double>(
|
||||||
|
begin: .5,
|
||||||
|
end: 1.0,
|
||||||
|
).chain(CurveTween(curve: Curves.easeInOutCubicEmphasized));
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: animation,
|
||||||
|
builder: (context, child) {
|
||||||
|
final double scale = animation.isDismissed
|
||||||
|
? 0.0
|
||||||
|
: _anim.evaluate(animation);
|
||||||
|
|
||||||
|
return Transform(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
transform: Matrix4.diagonal3Values(scale, 1.0, 1.0),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
child: _StatusTransitionWidgetBuilder(
|
||||||
|
animation: animation,
|
||||||
|
builder: (context, child) {
|
||||||
|
return _SelectableAnimatedBuilder(
|
||||||
|
isSelected: animation.isForwardOrCompleted,
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
alwaysDoFullAnimation: true,
|
||||||
|
builder: (context, fadeAnimation) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: fadeAnimation,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: ShapeDecoration(
|
||||||
|
shape: _kNavigationShape,
|
||||||
|
color: color ?? Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
child: const SizedBox(
|
||||||
|
width: _kIndicatorWidth,
|
||||||
|
height: _kIndicatorHeight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NavigationBarDestinationLayout extends StatelessWidget {
|
||||||
|
const _NavigationBarDestinationLayout({
|
||||||
|
required this.icon,
|
||||||
|
required this.iconKey,
|
||||||
|
required this.label,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget icon;
|
||||||
|
|
||||||
|
final GlobalKey iconKey;
|
||||||
|
|
||||||
|
final Widget label;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _DestinationLayoutAnimationBuilder(
|
||||||
|
builder: (context, animation) {
|
||||||
|
return CustomMultiChildLayout(
|
||||||
|
delegate: _NavigationDestinationLayoutDelegate(animation: animation),
|
||||||
|
children: <Widget>[
|
||||||
|
LayoutId(
|
||||||
|
id: _NavigationDestinationLayoutDelegate.iconId,
|
||||||
|
child: KeyedSubtree(key: iconKey, child: icon),
|
||||||
|
),
|
||||||
|
LayoutId(
|
||||||
|
id: _NavigationDestinationLayoutDelegate.labelId,
|
||||||
|
child: FadeTransition(
|
||||||
|
alwaysIncludeSemantics: true,
|
||||||
|
opacity: animation,
|
||||||
|
child: label,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DestinationLayoutAnimationBuilder extends StatelessWidget {
|
||||||
|
const _DestinationLayoutAnimationBuilder({required this.builder});
|
||||||
|
|
||||||
|
final Widget Function(BuildContext, Animation<double>) builder;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final info = _NavigationDestinationInfo.of(context);
|
||||||
|
switch (info.labelBehavior) {
|
||||||
|
case NavigationDestinationLabelBehavior.alwaysShow:
|
||||||
|
return builder(context, kAlwaysCompleteAnimation);
|
||||||
|
case NavigationDestinationLabelBehavior.alwaysHide:
|
||||||
|
return builder(context, kAlwaysDismissedAnimation);
|
||||||
|
case NavigationDestinationLabelBehavior.onlyShowSelected:
|
||||||
|
return _CurvedAnimationBuilder(
|
||||||
|
animation: info.selectedAnimation,
|
||||||
|
curve: Curves.easeInOutCubicEmphasized,
|
||||||
|
reverseCurve: Curves.easeInOutCubicEmphasized.flipped,
|
||||||
|
builder: builder,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NavigationBarDestinationTooltip extends StatelessWidget {
|
||||||
|
const _NavigationBarDestinationTooltip({
|
||||||
|
required this.message,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Tooltip(
|
||||||
|
message: message,
|
||||||
|
verticalOffset: 34,
|
||||||
|
excludeFromSemantics: true,
|
||||||
|
preferBelow: false,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NavigationDestinationLayoutDelegate extends MultiChildLayoutDelegate {
|
||||||
|
_NavigationDestinationLayoutDelegate({required this.animation})
|
||||||
|
: super(relayout: animation);
|
||||||
|
|
||||||
|
final Animation<double> animation;
|
||||||
|
|
||||||
|
static const int iconId = 1;
|
||||||
|
|
||||||
|
static const int labelId = 2;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performLayout(Size size) {
|
||||||
|
double halfWidth(Size size) => size.width / 2;
|
||||||
|
double halfHeight(Size size) => size.height / 2;
|
||||||
|
|
||||||
|
final Size iconSize = layoutChild(iconId, BoxConstraints.loose(size));
|
||||||
|
final Size labelSize = layoutChild(labelId, BoxConstraints.loose(size));
|
||||||
|
|
||||||
|
final double yPositionOffset = Tween<double>(
|
||||||
|
begin: halfHeight(iconSize),
|
||||||
|
|
||||||
|
end: halfHeight(iconSize) + halfHeight(labelSize),
|
||||||
|
).transform(animation.value);
|
||||||
|
final double iconYPosition = halfHeight(size) - yPositionOffset;
|
||||||
|
|
||||||
|
positionChild(
|
||||||
|
iconId,
|
||||||
|
Offset(
|
||||||
|
halfWidth(size) - halfWidth(iconSize),
|
||||||
|
iconYPosition,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
positionChild(
|
||||||
|
labelId,
|
||||||
|
Offset(
|
||||||
|
halfWidth(size) - halfWidth(labelSize),
|
||||||
|
|
||||||
|
iconYPosition + iconSize.height,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRelayout(_NavigationDestinationLayoutDelegate oldDelegate) {
|
||||||
|
return oldDelegate.animation != animation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StatusTransitionWidgetBuilder extends StatusTransitionWidget {
|
||||||
|
const _StatusTransitionWidgetBuilder({
|
||||||
|
required super.animation,
|
||||||
|
required this.builder,
|
||||||
|
// ignore: unused_element_parameter
|
||||||
|
this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final TransitionBuilder builder;
|
||||||
|
|
||||||
|
final Widget? child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => builder(context, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectableAnimatedBuilder extends StatefulWidget {
|
||||||
|
const _SelectableAnimatedBuilder({
|
||||||
|
required this.isSelected,
|
||||||
|
this.duration = const Duration(milliseconds: 200),
|
||||||
|
this.alwaysDoFullAnimation = false,
|
||||||
|
required this.builder,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool isSelected;
|
||||||
|
|
||||||
|
final Duration duration;
|
||||||
|
|
||||||
|
final bool alwaysDoFullAnimation;
|
||||||
|
|
||||||
|
final Widget Function(BuildContext, Animation<double>) builder;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SelectableAnimatedBuilderState createState() =>
|
||||||
|
_SelectableAnimatedBuilderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectableAnimatedBuilderState extends State<_SelectableAnimatedBuilder>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(vsync: this);
|
||||||
|
_controller.duration = widget.duration;
|
||||||
|
_controller.value = widget.isSelected ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(_SelectableAnimatedBuilder oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.duration != widget.duration) {
|
||||||
|
_controller.duration = widget.duration;
|
||||||
|
}
|
||||||
|
if (oldWidget.isSelected != widget.isSelected) {
|
||||||
|
if (widget.isSelected) {
|
||||||
|
_controller.forward(from: widget.alwaysDoFullAnimation ? 0 : null);
|
||||||
|
} else {
|
||||||
|
_controller.reverse(from: widget.alwaysDoFullAnimation ? 1 : null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return widget.builder(context, _controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CurvedAnimationBuilder extends StatefulWidget {
|
||||||
|
const _CurvedAnimationBuilder({
|
||||||
|
required this.animation,
|
||||||
|
required this.curve,
|
||||||
|
required this.reverseCurve,
|
||||||
|
required this.builder,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Animation<double> animation;
|
||||||
|
final Curve curve;
|
||||||
|
final Curve reverseCurve;
|
||||||
|
final Widget Function(BuildContext, Animation<double>) builder;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CurvedAnimationBuilderState createState() => _CurvedAnimationBuilderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CurvedAnimationBuilderState extends State<_CurvedAnimationBuilder> {
|
||||||
|
late AnimationStatus _animationDirection;
|
||||||
|
AnimationStatus? _preservedDirection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animationDirection = widget.animation.status;
|
||||||
|
_updateStatus(widget.animation.status);
|
||||||
|
widget.animation.addStatusListener(_updateStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.animation.removeStatusListener(_updateStatus);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateStatus(AnimationStatus status) {
|
||||||
|
if (_animationDirection != status) {
|
||||||
|
setState(() {
|
||||||
|
_animationDirection = status;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
switch (status) {
|
||||||
|
case AnimationStatus.forward || AnimationStatus.reverse
|
||||||
|
when _preservedDirection != null:
|
||||||
|
break;
|
||||||
|
case AnimationStatus.forward || AnimationStatus.reverse:
|
||||||
|
setState(() {
|
||||||
|
_preservedDirection = status;
|
||||||
|
});
|
||||||
|
case AnimationStatus.completed || AnimationStatus.dismissed:
|
||||||
|
setState(() {
|
||||||
|
_preservedDirection = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final shouldUseForwardCurve =
|
||||||
|
(_preservedDirection ?? _animationDirection) != AnimationStatus.reverse;
|
||||||
|
|
||||||
|
final Animation<double> curvedAnimation = CurveTween(
|
||||||
|
curve: shouldUseForwardCurve ? widget.curve : widget.reverseCurve,
|
||||||
|
).animate(widget.animation);
|
||||||
|
|
||||||
|
return widget.builder(context, curvedAnimation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _indicatorDark = Color(0x15FFFFFF);
|
||||||
|
const _indicatorLight = Color(0x10000000);
|
||||||
|
|
||||||
|
class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
|
||||||
|
_NavigationBarDefaultsM3(this.context)
|
||||||
|
: super(
|
||||||
|
height: _kNavigationHeight,
|
||||||
|
elevation: 3.0,
|
||||||
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||||||
|
);
|
||||||
|
|
||||||
|
final BuildContext context;
|
||||||
|
late final _colors = Theme.of(context).colorScheme;
|
||||||
|
late final _textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
|
BorderSide get borderSide => _colors.brightness.isDark
|
||||||
|
? const BorderSide(color: Color(0x08FFFFFF))
|
||||||
|
: const BorderSide(color: Color(0x08000000));
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color? get backgroundColor => _colors.surfaceContainer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color? get shadowColor => Colors.transparent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color? get surfaceTintColor => Colors.transparent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
WidgetStateProperty<IconThemeData?>? get iconTheme {
|
||||||
|
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||||
|
return IconThemeData(
|
||||||
|
size: 24.0,
|
||||||
|
color: states.contains(WidgetState.disabled)
|
||||||
|
? _colors.onSurfaceVariant.withValues(alpha: 0.38)
|
||||||
|
: states.contains(WidgetState.selected)
|
||||||
|
? _colors.onSecondaryContainer
|
||||||
|
: _colors.onSurfaceVariant,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color? get indicatorColor =>
|
||||||
|
_colors.brightness.isDark ? _indicatorDark : _indicatorLight;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ShapeBorder? get indicatorShape => const StadiumBorder();
|
||||||
|
|
||||||
|
@override
|
||||||
|
WidgetStateProperty<TextStyle?>? get labelTextStyle {
|
||||||
|
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||||
|
final TextStyle style = _textTheme.labelMedium!;
|
||||||
|
return style.apply(
|
||||||
|
color: states.contains(WidgetState.disabled)
|
||||||
|
? _colors.onSurfaceVariant.withValues(alpha: 0.38)
|
||||||
|
: states.contains(WidgetState.selected)
|
||||||
|
? _colors.onSurface
|
||||||
|
: _colors.onSurfaceVariant,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
EdgeInsetsGeometry? get labelPadding => const EdgeInsets.only(top: 2);
|
||||||
|
}
|
||||||
@@ -217,8 +217,8 @@ class RefreshIndicatorState extends State<RefreshIndicator>
|
|||||||
RefreshIndicatorStatus? _status;
|
RefreshIndicatorStatus? _status;
|
||||||
late Future<void> _pendingRefreshFuture;
|
late Future<void> _pendingRefreshFuture;
|
||||||
double? _dragOffset;
|
double? _dragOffset;
|
||||||
late Color _effectiveValueColor =
|
late Color _effectiveValueColor;
|
||||||
widget.color ?? Theme.of(context).colorScheme.primary;
|
// late Color _backgroundColor;
|
||||||
|
|
||||||
static final Animatable<double> _threeQuarterTween = Tween<double>(
|
static final Animatable<double> _threeQuarterTween = Tween<double>(
|
||||||
begin: 0.0,
|
begin: 0.0,
|
||||||
@@ -274,9 +274,10 @@ class RefreshIndicatorState extends State<RefreshIndicator>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _setupColorTween() {
|
void _setupColorTween() {
|
||||||
|
final colorScheme = ColorScheme.of(context);
|
||||||
|
// _backgroundColor = colorScheme.surfaceContainerHighest;
|
||||||
// Reset the current value color.
|
// Reset the current value color.
|
||||||
_effectiveValueColor =
|
_effectiveValueColor = widget.color ?? colorScheme.primary;
|
||||||
widget.color ?? Theme.of(context).colorScheme.primary;
|
|
||||||
final Color color = _effectiveValueColor;
|
final Color color = _effectiveValueColor;
|
||||||
if (color.a == 0) {
|
if (color.a == 0) {
|
||||||
// Set an always stopped animation instead of a driven tween.
|
// Set an always stopped animation instead of a driven tween.
|
||||||
@@ -558,14 +559,52 @@ class RefreshIndicatorState extends State<RefreshIndicator>
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _onDrag(double offset, double viewportDimension) {
|
bool _onDrag(double offset, double viewportDimension) {
|
||||||
if (_positionController.value > 0.0 &&
|
if (_positionController.value > 0.0 && _status == .drag) {
|
||||||
_status == RefreshIndicatorStatus.drag) {
|
|
||||||
_dragOffset = _dragOffset! + offset;
|
_dragOffset = _dragOffset! + offset;
|
||||||
_checkDragOffset(viewportDimension);
|
_checkDragOffset(viewportDimension);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// late final _refreshKey = GlobalKey();
|
||||||
|
// Widget _m3eRefreshProgressIndicator(bool showIndeterminateIndicator) {
|
||||||
|
// const indicatorMargin = EdgeInsets.all(4);
|
||||||
|
// const indicatorPadding = EdgeInsets.all(6);
|
||||||
|
// const indicatorSize = 41.0;
|
||||||
|
|
||||||
|
// final progress = _value.value;
|
||||||
|
// return Padding(
|
||||||
|
// padding: indicatorMargin,
|
||||||
|
// child: SizedBox(
|
||||||
|
// width: indicatorSize,
|
||||||
|
// height: indicatorSize,
|
||||||
|
// child: Material(
|
||||||
|
// type: MaterialType.circle,
|
||||||
|
// color: _backgroundColor,
|
||||||
|
// elevation: widget.elevation,
|
||||||
|
// child: Padding(
|
||||||
|
// padding: indicatorPadding,
|
||||||
|
// child: showIndeterminateIndicator
|
||||||
|
// ? M3ELoadingIndicator(
|
||||||
|
// childKey: _refreshKey,
|
||||||
|
// color: _effectiveValueColor,
|
||||||
|
// morphs: Morphs.refreshMorphs,
|
||||||
|
// size: null,
|
||||||
|
// )
|
||||||
|
// : RawM3ELoadingIndicator(
|
||||||
|
// key: _refreshKey,
|
||||||
|
// morph: Morphs.manualMorph,
|
||||||
|
// progress: progress,
|
||||||
|
// angle: -progress * math.pi,
|
||||||
|
// color: _valueColor.value!,
|
||||||
|
// size: null,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: camel_case_types
|
// ignore: camel_case_types
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
class CustomHorizontalDragGestureRecognizer
|
mixin InitialPositionMixin on GestureRecognizer {
|
||||||
extends HorizontalDragGestureRecognizer {
|
|
||||||
CustomHorizontalDragGestureRecognizer({
|
|
||||||
super.debugOwner,
|
|
||||||
super.supportedDevices,
|
|
||||||
super.allowedButtonsFilter,
|
|
||||||
});
|
|
||||||
|
|
||||||
Offset? _initialPosition;
|
Offset? _initialPosition;
|
||||||
Offset? get initialPosition => _initialPosition;
|
Offset? get initialPosition => _initialPosition;
|
||||||
|
|
||||||
@override
|
|
||||||
DeviceGestureSettings get gestureSettings => _gestureSettings;
|
|
||||||
final _gestureSettings = DeviceGestureSettings(touchSlop: touchSlopH);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addAllowedPointer(PointerDownEvent event) {
|
void addAllowedPointer(PointerDownEvent event) {
|
||||||
super.addAllowedPointer(event);
|
super.addAllowedPointer(event);
|
||||||
_initialPosition = event.position;
|
_initialPosition = event.position;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomHorizontalDragGestureRecognizer
|
||||||
|
extends HorizontalDragGestureRecognizer
|
||||||
|
with InitialPositionMixin {
|
||||||
|
CustomHorizontalDragGestureRecognizer({
|
||||||
|
super.debugOwner,
|
||||||
|
super.supportedDevices,
|
||||||
|
super.allowedButtonsFilter,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
DeviceGestureSettings get gestureSettings => _gestureSettings;
|
||||||
|
final _gestureSettings = DeviceGestureSettings(touchSlop: touchSlopH);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool hasSufficientGlobalDistanceToAccept(
|
bool hasSufficientGlobalDistanceToAccept(
|
||||||
@@ -41,7 +44,7 @@ double touchSlopH = Pref.touchSlopH;
|
|||||||
|
|
||||||
bool _computeHitSlop(
|
bool _computeHitSlop(
|
||||||
double globalDistanceMoved,
|
double globalDistanceMoved,
|
||||||
DeviceGestureSettings? settings,
|
DeviceGestureSettings settings,
|
||||||
PointerDeviceKind kind,
|
PointerDeviceKind kind,
|
||||||
Offset? initialPosition,
|
Offset? initialPosition,
|
||||||
Offset lastPosition,
|
Offset lastPosition,
|
||||||
@@ -53,10 +56,10 @@ bool _computeHitSlop(
|
|||||||
case PointerDeviceKind.invertedStylus:
|
case PointerDeviceKind.invertedStylus:
|
||||||
case PointerDeviceKind.unknown:
|
case PointerDeviceKind.unknown:
|
||||||
case PointerDeviceKind.touch:
|
case PointerDeviceKind.touch:
|
||||||
return globalDistanceMoved > touchSlopH &&
|
return globalDistanceMoved > settings.touchSlop! &&
|
||||||
_calc(initialPosition!, lastPosition);
|
_calc(initialPosition!, lastPosition);
|
||||||
case PointerDeviceKind.trackpad:
|
case PointerDeviceKind.trackpad:
|
||||||
return globalDistanceMoved > (settings?.touchSlop ?? kTouchSlop);
|
return globalDistanceMoved > settings.touchSlop!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import 'package:PiliPlus/common/widgets/gesture/horizontal_drag_gesture_recognizer.dart';
|
import 'package:PiliPlus/common/widgets/gesture/horizontal_drag_gesture_recognizer.dart';
|
||||||
|
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
mixin ImageGestureRecognizerMixin on GestureRecognizer {
|
mixin ImageGestureRecognizerMixin on GestureRecognizer {
|
||||||
int? _pointer;
|
int? _pointer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addPointer(PointerDownEvent event) {
|
void addPointer(PointerDownEvent event, {bool isPointerAllowed = true}) {
|
||||||
if (_pointer == event.pointer) {
|
if (_pointer == event.pointer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_pointer = event.pointer;
|
_pointer = event.pointer;
|
||||||
super.addPointer(event);
|
if (isPointerAllowed) {
|
||||||
|
super.addPointer(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef IsBoundaryAllowed =
|
|
||||||
bool Function(Offset? initialPosition, OffsetPair lastPosition);
|
|
||||||
|
|
||||||
class ImageHorizontalDragGestureRecognizer
|
class ImageHorizontalDragGestureRecognizer
|
||||||
extends CustomHorizontalDragGestureRecognizer
|
extends CustomHorizontalDragGestureRecognizer
|
||||||
with ImageGestureRecognizerMixin {
|
with ImageGestureRecognizerMixin {
|
||||||
@@ -26,23 +26,62 @@ class ImageHorizontalDragGestureRecognizer
|
|||||||
super.allowedButtonsFilter,
|
super.allowedButtonsFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
IsBoundaryAllowed? isBoundaryAllowed;
|
static final double _touchSlop = PlatformUtils.isDesktop
|
||||||
|
? kPrecisePointerHitSlop
|
||||||
|
: 3.0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool hasSufficientGlobalDistanceToAccept(
|
DeviceGestureSettings get gestureSettings => _gestureSettings;
|
||||||
PointerDeviceKind pointerDeviceKind,
|
final _gestureSettings = DeviceGestureSettings(touchSlop: _touchSlop);
|
||||||
double? deviceTouchSlop,
|
|
||||||
) {
|
bool isAtLeftEdge = false;
|
||||||
return super.hasSufficientGlobalDistanceToAccept(
|
bool isAtRightEdge = false;
|
||||||
pointerDeviceKind,
|
|
||||||
deviceTouchSlop,
|
void setAtBothEdges() {
|
||||||
) &&
|
isAtLeftEdge = isAtRightEdge = true;
|
||||||
(isBoundaryAllowed?.call(initialPosition, lastPosition) ?? true);
|
}
|
||||||
|
|
||||||
|
bool _isEdgeAllowed(double dx) {
|
||||||
|
if ((initialPosition!.dx - dx).abs() < _touchSlop) return true;
|
||||||
|
if (isAtLeftEdge) {
|
||||||
|
if (isAtRightEdge) {
|
||||||
|
return _hasAcceptedOrChecked = true;
|
||||||
|
}
|
||||||
|
_hasAcceptedOrChecked = true;
|
||||||
|
return initialPosition!.dx < dx;
|
||||||
|
} else if (isAtRightEdge) {
|
||||||
|
_hasAcceptedOrChecked = true;
|
||||||
|
return initialPosition!.dx > dx;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void handleEvent(PointerEvent event) {
|
||||||
isBoundaryAllowed = null;
|
if (!_hasAcceptedOrChecked &&
|
||||||
super.dispose();
|
event is PointerMoveEvent &&
|
||||||
|
_pointer == event.pointer) {
|
||||||
|
if (!_isEdgeAllowed(event.position.dx)) {
|
||||||
|
rejectGesture(event.pointer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.handleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _hasAcceptedOrChecked = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void acceptGesture(int pointer) {
|
||||||
|
_hasAcceptedOrChecked = true;
|
||||||
|
super.acceptGesture(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void stopTrackingPointer(int pointer) {
|
||||||
|
_hasAcceptedOrChecked = false;
|
||||||
|
isAtLeftEdge = false;
|
||||||
|
isAtRightEdge = false;
|
||||||
|
super.stopTrackingPointer(pointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||||
import 'package:PiliPlus/http/user.dart';
|
import 'package:PiliPlus/http/user.dart';
|
||||||
@@ -22,10 +22,10 @@ void imageSaveDialog({
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
return Container(
|
return Container(
|
||||||
width: imgWidth,
|
width: imgWidth,
|
||||||
margin: const .symmetric(horizontal: StyleString.safeSpace),
|
margin: const .symmetric(horizontal: Style.safeSpace),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.colorScheme.surface,
|
color: theme.colorScheme.surface,
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: Style.mdRadius,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -39,8 +39,8 @@ void imageSaveDialog({
|
|||||||
src: cover,
|
src: cover,
|
||||||
quality: 100,
|
quality: 100,
|
||||||
width: imgWidth,
|
width: imgWidth,
|
||||||
height: imgWidth / StyleString.aspectRatio16x9,
|
height: imgWidth / Style.aspectRatio16x9,
|
||||||
borderRadius: const .vertical(top: StyleString.imgRadius),
|
borderRadius: const .vertical(top: Style.imgRadius),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/assets.dart';
|
||||||
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/models/common/image_type.dart';
|
import 'package:PiliPlus/models/common/image_type.dart';
|
||||||
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||||
import 'package:PiliPlus/utils/image_utils.dart';
|
import 'package:PiliPlus/utils/image_utils.dart';
|
||||||
@@ -16,7 +17,7 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
this.fadeOutDuration = const Duration(milliseconds: 120),
|
this.fadeOutDuration = const Duration(milliseconds: 120),
|
||||||
this.fadeInDuration = const Duration(milliseconds: 120),
|
this.fadeInDuration = const Duration(milliseconds: 120),
|
||||||
this.quality = 1,
|
this.quality = 1,
|
||||||
this.borderRadius = StyleString.mdRadius,
|
this.borderRadius = Style.mdRadius,
|
||||||
this.getPlaceHolder,
|
this.getPlaceHolder,
|
||||||
this.fit = .cover,
|
this.fit = .cover,
|
||||||
this.alignment = .center,
|
this.alignment = .center,
|
||||||
@@ -108,7 +109,7 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
isAvatar ? 'assets/images/noface.jpeg' : 'assets/images/loading.png',
|
isAvatar ? Assets.avatarPlaceHolder : Assets.loading,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
cacheWidth: width.cacheSize(context),
|
cacheWidth: width.cacheSize(context),
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
import 'dart:collection' show HashSet;
|
import 'dart:collection' show HashSet;
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart' show StyleString;
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image_grid/image_grid_view.dart'
|
import 'package:PiliPlus/common/widgets/image_grid/image_grid_view.dart'
|
||||||
show ImageModel;
|
show ImageModel;
|
||||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||||
@@ -505,7 +505,7 @@ class ImageGridRenderObjectElement extends RenderObjectElement {
|
|||||||
if (width != 1) {
|
if (width != 1) {
|
||||||
imageWidth = math.min(imageWidth, width.toDouble());
|
imageWidth = math.min(imageWidth, width.toDouble());
|
||||||
}
|
}
|
||||||
imageHeight = imageWidth * math.min(ratioHW, StyleString.imgMaxRatio);
|
imageHeight = imageWidth * math.min(ratioHW, Style.imgMaxRatio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
|
|
||||||
import 'dart:io' show Platform;
|
import 'dart:io' show Platform;
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/assets.dart';
|
||||||
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image_grid/image_grid_builder.dart';
|
import 'package:PiliPlus/common/widgets/image_grid/image_grid_builder.dart';
|
||||||
@@ -53,8 +54,7 @@ class ImageModel {
|
|||||||
bool? _isLongPic;
|
bool? _isLongPic;
|
||||||
bool? _isLivePhoto;
|
bool? _isLivePhoto;
|
||||||
|
|
||||||
bool get isLongPic =>
|
bool get isLongPic => _isLongPic ??= (height / width) > Style.imgMaxRatio;
|
||||||
_isLongPic ??= (height / width) > StyleString.imgMaxRatio;
|
|
||||||
bool get isLivePhoto =>
|
bool get isLivePhoto =>
|
||||||
_isLivePhoto ??= enableLivePhoto && liveUrl?.isNotEmpty == true;
|
_isLivePhoto ??= enableLivePhoto && liveUrl?.isNotEmpty == true;
|
||||||
|
|
||||||
@@ -116,9 +116,9 @@ class ImageGridView extends StatelessWidget {
|
|||||||
int col,
|
int col,
|
||||||
int length,
|
int length,
|
||||||
int index, {
|
int index, {
|
||||||
Radius r = StyleString.imgRadius,
|
Radius r = Style.imgRadius,
|
||||||
}) {
|
}) {
|
||||||
if (length == 1) return StyleString.mdRadius;
|
if (length == 1) return Style.mdRadius;
|
||||||
|
|
||||||
final bool hasUp = index - col >= 0;
|
final bool hasUp = index - col >= 0;
|
||||||
final bool hasDown = index + col < length;
|
final bool hasDown = index + col < length;
|
||||||
@@ -213,7 +213,7 @@ class ImageGridView extends StatelessWidget {
|
|||||||
).colorScheme.onInverseSurface.withValues(alpha: 0.4),
|
).colorScheme.onInverseSurface.withValues(alpha: 0.4),
|
||||||
),
|
),
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'assets/images/loading.png',
|
Assets.loading,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
cacheWidth: width.cacheSize(context),
|
cacheWidth: width.cacheSize(context),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import 'dart:io' show File;
|
import 'dart:io' show File;
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
|
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image_viewer/viewer.dart';
|
import 'package:PiliPlus/common/widgets/image_viewer/viewer.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@@ -603,7 +603,7 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
|
|||||||
final imgHeight = _imageInfo!.image.height.toDouble();
|
final imgHeight = _imageInfo!.image.height.toDouble();
|
||||||
final imgRatio = imgHeight / imgWidth;
|
final imgRatio = imgHeight / imgWidth;
|
||||||
isLongPic =
|
isLongPic =
|
||||||
imgRatio > StyleString.imgMaxRatio &&
|
imgRatio > Style.imgMaxRatio &&
|
||||||
imgHeight > widget.containerSize.height;
|
imgHeight > widget.containerSize.height;
|
||||||
if (isLongPic) {
|
if (isLongPic) {
|
||||||
final compatWidth = math.min(650.0, widget.containerSize.width);
|
final compatWidth = math.min(650.0, widget.containerSize.width);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
import 'dart:math' show pi;
|
import 'dart:math' show pi;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/semantics.dart' show SemanticsConfiguration;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// created by dom on 2026/02/14
|
/// created by dom on 2026/02/14
|
||||||
@@ -73,6 +74,7 @@ class RenderLoadingIndicator extends RenderBox {
|
|||||||
if (_progress == value) return;
|
if (_progress == value) return;
|
||||||
_progress = value;
|
_progress = value;
|
||||||
markNeedsPaint();
|
markNeedsPaint();
|
||||||
|
markNeedsSemanticsUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -119,6 +121,16 @@ class RenderLoadingIndicator extends RenderBox {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||||
|
super.describeSemanticsConfiguration(config);
|
||||||
|
config
|
||||||
|
..role = .progressBar
|
||||||
|
..minValue = '0'
|
||||||
|
..maxValue = '100'
|
||||||
|
..value = (_progress * 100).round().toString();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get isRepaintBoundary => true;
|
bool get isRepaintBoundary => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/physics.dart' show FrictionSimulation;
|
import 'package:flutter/physics.dart' show FrictionSimulation;
|
||||||
|
import 'package:flutter/scheduler.dart' show SchedulerBinding;
|
||||||
import 'package:flutter/services.dart' show HardwareKeyboard;
|
import 'package:flutter/services.dart' show HardwareKeyboard;
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -69,7 +70,6 @@ class Viewer extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
||||||
double get _interactionEndFrictionCoefficient => 0.0001 * _scale; // 0.0000135
|
|
||||||
static const double _scaleFactor = kDefaultMouseScrollToScaleFactor;
|
static const double _scaleFactor = kDefaultMouseScrollToScaleFactor;
|
||||||
|
|
||||||
_GestureType? _gestureType;
|
_GestureType? _gestureType;
|
||||||
@@ -171,6 +171,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_stopFling();
|
||||||
_animationController
|
_animationController
|
||||||
..removeListener(_listener)
|
..removeListener(_listener)
|
||||||
..dispose();
|
..dispose();
|
||||||
@@ -234,6 +235,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
void _handleDoubleTap() {
|
void _handleDoubleTap() {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
if (_animationController.isAnimating) return;
|
if (_animationController.isAnimating) return;
|
||||||
|
_stopFling();
|
||||||
_scaleFrom = _scale;
|
_scaleFrom = _scale;
|
||||||
_positionFrom = _position;
|
_positionFrom = _position;
|
||||||
|
|
||||||
@@ -265,6 +267,8 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onScaleStart(ScaleStartDetails details) {
|
void _onScaleStart(ScaleStartDetails details) {
|
||||||
|
_stopFling();
|
||||||
|
|
||||||
if (_animationController.isAnimating) {
|
if (_animationController.isAnimating) {
|
||||||
_animationController.stop();
|
_animationController.stop();
|
||||||
}
|
}
|
||||||
@@ -329,40 +333,106 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ref https://github.com/ahnaineh/custom_interactive_viewer
|
||||||
|
int? _flingFrameCallbackId;
|
||||||
|
Simulation? _flingSimulation;
|
||||||
|
Duration? _flingStartTime;
|
||||||
|
double _lastFlingElapsedSeconds = 0.0;
|
||||||
|
Offset _flingDirection = Offset.zero;
|
||||||
|
|
||||||
|
/// Calculate appropriate friction based on velocity magnitude
|
||||||
|
double _calculateDynamicFriction(double velocityMagnitude) {
|
||||||
|
// Use higher friction for faster flicks
|
||||||
|
// These values can be tuned for the feel you want
|
||||||
|
if (velocityMagnitude > 5000) {
|
||||||
|
return 0.03; // Higher friction for very fast flicks
|
||||||
|
} else if (velocityMagnitude > 3000) {
|
||||||
|
return 0.02; // Medium friction for moderate flicks
|
||||||
|
} else {
|
||||||
|
return 0.01; // Lower friction for gentle movements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _startFling(Velocity velocity) {
|
||||||
|
_stopFling();
|
||||||
|
|
||||||
|
final double velocityMagnitude = velocity.pixelsPerSecond.distance;
|
||||||
|
final double frictionCoefficient = _calculateDynamicFriction(
|
||||||
|
velocityMagnitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
_flingSimulation = FrictionSimulation(
|
||||||
|
frictionCoefficient,
|
||||||
|
0.0,
|
||||||
|
velocityMagnitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
_flingDirection = velocityMagnitude > 0
|
||||||
|
? velocity.pixelsPerSecond / velocityMagnitude
|
||||||
|
: Offset.zero;
|
||||||
|
|
||||||
|
_flingStartTime = null;
|
||||||
|
_lastFlingElapsedSeconds = 0.0;
|
||||||
|
_scheduleFlingFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _scheduleFlingFrame() {
|
||||||
|
_flingFrameCallbackId = SchedulerBinding.instance.scheduleFrameCallback(
|
||||||
|
_handleFlingFrame,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleFlingFrame(Duration timeStamp) {
|
||||||
|
if (_flingSimulation == null) return;
|
||||||
|
|
||||||
|
_flingStartTime ??= timeStamp;
|
||||||
|
final double elapsedSeconds =
|
||||||
|
(timeStamp - _flingStartTime!).inMicroseconds / 1e6;
|
||||||
|
|
||||||
|
final double distance = _flingSimulation!.x(elapsedSeconds);
|
||||||
|
final double prevDistance = _flingSimulation!.x(_lastFlingElapsedSeconds);
|
||||||
|
final double delta = distance - prevDistance;
|
||||||
|
_lastFlingElapsedSeconds = elapsedSeconds;
|
||||||
|
|
||||||
|
if ((prevDistance != 0.0 && delta.abs() < 0.1) ||
|
||||||
|
_flingSimulation!.isDone(elapsedSeconds)) {
|
||||||
|
_stopFling();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Offset movement = _flingDirection * delta;
|
||||||
|
_position = _clampPosition(_position + movement, _scale);
|
||||||
|
setState(() {});
|
||||||
|
|
||||||
|
if (_flingSimulation!.isDone(elapsedSeconds)) {
|
||||||
|
_stopFling();
|
||||||
|
} else {
|
||||||
|
_scheduleFlingFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _stopFling() {
|
||||||
|
if (_flingFrameCallbackId != null) {
|
||||||
|
SchedulerBinding.instance.cancelFrameCallbackWithId(
|
||||||
|
_flingFrameCallbackId!,
|
||||||
|
);
|
||||||
|
_flingFrameCallbackId = null;
|
||||||
|
}
|
||||||
|
_flingStartTime = null;
|
||||||
|
_lastFlingElapsedSeconds = 0.0;
|
||||||
|
_flingSimulation = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// ref [InteractiveViewer]
|
/// ref [InteractiveViewer]
|
||||||
void _onScaleEnd(ScaleEndDetails details) {
|
void _onScaleEnd(ScaleEndDetails details) {
|
||||||
switch (_gestureType) {
|
switch (_gestureType) {
|
||||||
case _GestureType.pan:
|
case _GestureType.pan:
|
||||||
if (details.velocity.pixelsPerSecond.distance < kMinFlingVelocity) {
|
final double velocityMagnitude =
|
||||||
return;
|
details.velocity.pixelsPerSecond.distance;
|
||||||
|
if (velocityMagnitude >= 200.0) {
|
||||||
|
_startFling(details.velocity);
|
||||||
}
|
}
|
||||||
final drag = _interactionEndFrictionCoefficient;
|
|
||||||
final FrictionSimulation frictionSimulationX = FrictionSimulation(
|
|
||||||
drag,
|
|
||||||
_position.dx,
|
|
||||||
details.velocity.pixelsPerSecond.dx,
|
|
||||||
);
|
|
||||||
final FrictionSimulation frictionSimulationY = FrictionSimulation(
|
|
||||||
drag,
|
|
||||||
_position.dy,
|
|
||||||
details.velocity.pixelsPerSecond.dy,
|
|
||||||
);
|
|
||||||
final double tFinal = _getFinalTime(
|
|
||||||
details.velocity.pixelsPerSecond.distance,
|
|
||||||
drag,
|
|
||||||
);
|
|
||||||
final position = _clampPosition(
|
|
||||||
Offset(frictionSimulationX.finalX, frictionSimulationY.finalX),
|
|
||||||
_scale,
|
|
||||||
);
|
|
||||||
|
|
||||||
_scaleFrom = _scaleTo = _scale;
|
|
||||||
_positionFrom = _position;
|
|
||||||
_positionTo = position;
|
|
||||||
|
|
||||||
_animationController
|
|
||||||
..duration = Duration(milliseconds: (tFinal * 1000).round())
|
|
||||||
..forward(from: 0);
|
|
||||||
case _GestureType.scale:
|
case _GestureType.scale:
|
||||||
// if (details.scaleVelocity.abs() < 0.1) {
|
// if (details.scaleVelocity.abs() < 0.1) {
|
||||||
// return;
|
// return;
|
||||||
@@ -417,9 +487,10 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
_doubleTapGestureRecognizer
|
_doubleTapGestureRecognizer
|
||||||
..onDoubleTapDown = _onDoubleTapDown
|
..onDoubleTapDown = _onDoubleTapDown
|
||||||
..onDoubleTap = _onDoubleTap;
|
..onDoubleTap = _onDoubleTap;
|
||||||
_horizontalDragGestureRecognizer
|
_horizontalDragGestureRecognizer.addPointer(
|
||||||
..isBoundaryAllowed = _isBoundaryAllowed
|
event,
|
||||||
..addPointer(event);
|
isPointerAllowed: _isAtEdge(event.localPosition),
|
||||||
|
);
|
||||||
_scaleGestureRecognizer.addPointer(event);
|
_scaleGestureRecognizer.addPointer(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,25 +498,28 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
_scaleGestureRecognizer.addPointerPanZoom(event);
|
_scaleGestureRecognizer.addPointerPanZoom(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isBoundaryAllowed(Offset? initialPosition, OffsetPair lastPosition) {
|
bool _isAtEdge(Offset position) {
|
||||||
if (initialPosition == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (_scale <= widget.minScale) {
|
if (_scale <= widget.minScale) {
|
||||||
|
_horizontalDragGestureRecognizer.setAtBothEdges();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
final containerWidth = widget.containerSize.width;
|
final containerWidth = widget.containerSize.width;
|
||||||
final imageWidth = _imageSize.width * _scale;
|
final imageWidth = _imageSize.width * _scale;
|
||||||
if (imageWidth <= containerWidth) {
|
if (imageWidth <= containerWidth) {
|
||||||
|
_horizontalDragGestureRecognizer.setAtBothEdges();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
final dx = (1 - _scale) * containerWidth / 2;
|
final dx = (1 - _scale) * containerWidth / 2;
|
||||||
final dxOffset = (imageWidth - containerWidth) / 2;
|
final dxOffset = (imageWidth - containerWidth) / 2;
|
||||||
if (initialPosition.dx < lastPosition.global.dx) {
|
if (_position.dx.equals(dx + dxOffset, 1e-6)) {
|
||||||
return _position.dx.equals(dx + dxOffset, 1e-6);
|
_horizontalDragGestureRecognizer.isAtLeftEdge = true;
|
||||||
} else {
|
return true;
|
||||||
return _position.dx.equals(dx - dxOffset, 1e-6);
|
|
||||||
}
|
}
|
||||||
|
if (_position.dx.equals(dx - dxOffset, 1e-6)) {
|
||||||
|
_horizontalDragGestureRecognizer.isAtRightEdge = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onPointerSignal(PointerSignalEvent event) {
|
void _onPointerSignal(PointerSignalEvent event) {
|
||||||
@@ -455,6 +529,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
widget.onChangePage!.call(event.scrollDelta.dy < 0 ? -1 : 1);
|
widget.onChangePage!.call(event.scrollDelta.dy < 0 ? -1 : 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_stopFling();
|
||||||
final double scaleChange = math.exp(-event.scrollDelta.dy / _scaleFactor);
|
final double scaleChange = math.exp(-event.scrollDelta.dy / _scaleFactor);
|
||||||
final Offset local = event.localPosition;
|
final Offset local = event.localPosition;
|
||||||
final Offset focalPointScene = _toScene(local);
|
final Offset focalPointScene = _toScene(local);
|
||||||
@@ -471,11 +546,3 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum _GestureType { pan, scale, drag }
|
enum _GestureType { pan, scale, drag }
|
||||||
|
|
||||||
double _getFinalTime(
|
|
||||||
double velocity,
|
|
||||||
double drag, {
|
|
||||||
double effectivelyMotionless = 10,
|
|
||||||
}) {
|
|
||||||
return math.log(effectivelyMotionless / velocity) / math.log(drag / 100);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
const Widget m3eLoading = Center(child: M3ELoadingIndicator());
|
const Widget m3eLoading = Center(child: M3ELoadingIndicator());
|
||||||
|
|
||||||
const Widget circularLoading = Center(child: CircularProgressIndicator());
|
|
||||||
|
|
||||||
const Widget linearLoading = SliverToBoxAdapter(
|
const Widget linearLoading = SliverToBoxAdapter(
|
||||||
child: LinearProgressIndicator(),
|
child: LinearProgressIndicator(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,14 +17,27 @@
|
|||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:PiliPlus/common/widgets/loading_widget/morphs.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/physics.dart' show SpringSimulation;
|
import 'package:flutter/physics.dart' show SpringSimulation;
|
||||||
|
import 'package:flutter/semantics.dart';
|
||||||
import 'package:material_new_shapes/material_new_shapes.dart';
|
import 'package:material_new_shapes/material_new_shapes.dart';
|
||||||
|
|
||||||
/// reimplement of https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/loading_indicator_m3e
|
/// reimplement of https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/loading_indicator_m3e
|
||||||
|
|
||||||
class M3ELoadingIndicator extends StatefulWidget {
|
class M3ELoadingIndicator extends StatefulWidget {
|
||||||
const M3ELoadingIndicator({super.key});
|
const M3ELoadingIndicator({
|
||||||
|
super.key,
|
||||||
|
// this.childKey,
|
||||||
|
this.morphs,
|
||||||
|
this.color,
|
||||||
|
this.size = const Size.square(40),
|
||||||
|
});
|
||||||
|
final List<Morph>? morphs;
|
||||||
|
|
||||||
|
final Color? color;
|
||||||
|
final Size size;
|
||||||
|
// final Key? childKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<M3ELoadingIndicator> createState() => _M3ELoadingIndicatorState();
|
State<M3ELoadingIndicator> createState() => _M3ELoadingIndicatorState();
|
||||||
@@ -32,42 +45,25 @@ class M3ELoadingIndicator extends StatefulWidget {
|
|||||||
|
|
||||||
class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
static final List<Morph> _morphs = () {
|
|
||||||
final List<RoundedPolygon> shapes = [
|
|
||||||
MaterialShapes.softBurst,
|
|
||||||
MaterialShapes.cookie9Sided,
|
|
||||||
MaterialShapes.pentagon,
|
|
||||||
MaterialShapes.pill,
|
|
||||||
MaterialShapes.sunny,
|
|
||||||
MaterialShapes.cookie4Sided,
|
|
||||||
MaterialShapes.oval,
|
|
||||||
];
|
|
||||||
return [
|
|
||||||
for (var i = 0; i < shapes.length; i++)
|
|
||||||
Morph(
|
|
||||||
shapes[i],
|
|
||||||
shapes[(i + 1) % shapes.length],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}();
|
|
||||||
|
|
||||||
static const int _morphIntervalMs = 650;
|
static const int _morphIntervalMs = 650;
|
||||||
static const double _fullRotation = 360.0;
|
static const double _fullRotation = 2 * math.pi;
|
||||||
static const int _globalRotationDurationMs = 4666;
|
static const int _globalRotationDurationMs = 4666;
|
||||||
static const double _quarterRotation = _fullRotation / 4;
|
static const double _quarterRotation = _fullRotation / 4;
|
||||||
|
|
||||||
|
late final List<Morph> _morphs;
|
||||||
late final AnimationController _controller;
|
late final AnimationController _controller;
|
||||||
|
|
||||||
int _morphIndex = 1;
|
int _morphIndex = 1;
|
||||||
|
|
||||||
double _morphRotationTargetAngle = _quarterRotation;
|
double _morphRotationTarget = _quarterRotation;
|
||||||
|
|
||||||
final _morphAnimationSpec = SpringSimulation(
|
static final _morphAnimationSpec = SpringSimulation(
|
||||||
SpringDescription.withDampingRatio(ratio: 0.6, stiffness: 200.0, mass: 1.0),
|
SpringDescription.withDampingRatio(ratio: 0.6, stiffness: 200.0, mass: 1.0),
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
1.0,
|
||||||
5.0,
|
5.0,
|
||||||
snapToEnd: true,
|
snapToEnd: true,
|
||||||
|
// tolerance: const Tolerance(velocity: 0.1, distance: 0.1),
|
||||||
);
|
);
|
||||||
|
|
||||||
void _statusListener(AnimationStatus status) {
|
void _statusListener(AnimationStatus status) {
|
||||||
@@ -78,23 +74,21 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
|||||||
|
|
||||||
void _startAnimation() {
|
void _startAnimation() {
|
||||||
_morphIndex++;
|
_morphIndex++;
|
||||||
_morphRotationTargetAngle =
|
_morphRotationTarget =
|
||||||
(_morphRotationTargetAngle + _quarterRotation) % _fullRotation;
|
(_morphRotationTarget + _quarterRotation) % _fullRotation;
|
||||||
_controller
|
_controller.animateWith(_morphAnimationSpec);
|
||||||
..value = 0.0
|
|
||||||
..animateWith(_morphAnimationSpec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_morphs = widget.morphs ?? Morphs.loadingMorphs;
|
||||||
_controller =
|
_controller =
|
||||||
AnimationController(
|
AnimationController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
duration: const Duration(milliseconds: _morphIntervalMs),
|
duration: const Duration(milliseconds: _morphIntervalMs),
|
||||||
)
|
)
|
||||||
..addStatusListener(_statusListener)
|
..addStatusListener(_statusListener)
|
||||||
..value = 0.0
|
|
||||||
..animateWith(_morphAnimationSpec);
|
..animateWith(_morphAnimationSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,82 +104,86 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
|
|||||||
final elapsedInMs =
|
final elapsedInMs =
|
||||||
_morphIntervalMs * (_morphIndex - 1) +
|
_morphIntervalMs * (_morphIndex - 1) +
|
||||||
(_controller.lastElapsedDuration?.inMilliseconds ?? 0);
|
(_controller.lastElapsedDuration?.inMilliseconds ?? 0);
|
||||||
final globalRotationControllerValue =
|
final globalRotation =
|
||||||
(elapsedInMs % _globalRotationDurationMs) / _globalRotationDurationMs;
|
(elapsedInMs % _globalRotationDurationMs) /
|
||||||
final globalRotationDegrees = globalRotationControllerValue * _fullRotation;
|
_globalRotationDurationMs *
|
||||||
final totalRotationDegrees =
|
_fullRotation;
|
||||||
progress * _quarterRotation +
|
|
||||||
_morphRotationTargetAngle +
|
return progress * _quarterRotation + _morphRotationTarget + globalRotation;
|
||||||
globalRotationDegrees;
|
|
||||||
return totalRotationDegrees * (math.pi / 180.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final color = Theme.of(context).colorScheme.secondaryFixedDim;
|
final color = widget.color ?? ColorScheme.of(context).secondaryFixedDim;
|
||||||
return AnimatedBuilder(
|
return AnimatedBuilder(
|
||||||
animation: _controller,
|
animation: _controller,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
final progress = _controller.value;
|
final progress = _controller.value;
|
||||||
return _M3ELoadingIndicator(
|
return RawM3ELoadingIndicator(
|
||||||
|
// key: widget.childKey,
|
||||||
morph: _morphs[_morphIndex % _morphs.length],
|
morph: _morphs[_morphIndex % _morphs.length],
|
||||||
progress: progress,
|
progress: progress,
|
||||||
angle: _calcAngle(progress),
|
angle: _calcAngle(progress),
|
||||||
color: color,
|
color: color,
|
||||||
|
size: widget.size,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _M3ELoadingIndicator extends LeafRenderObjectWidget {
|
class RawM3ELoadingIndicator extends LeafRenderObjectWidget {
|
||||||
const _M3ELoadingIndicator({
|
const RawM3ELoadingIndicator({
|
||||||
|
super.key,
|
||||||
required this.morph,
|
required this.morph,
|
||||||
required this.progress,
|
required this.progress,
|
||||||
required this.angle,
|
required this.angle,
|
||||||
required this.color,
|
required this.color,
|
||||||
|
required this.size,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Morph morph;
|
final Morph morph;
|
||||||
|
|
||||||
final double progress;
|
final double progress;
|
||||||
|
|
||||||
final double angle;
|
final double angle;
|
||||||
|
|
||||||
final Color color;
|
final Color color;
|
||||||
|
final Size size;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderObject createRenderObject(BuildContext context) {
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
return _RenderM3ELoadingIndicator(
|
return RenderM3ELoadingIndicator(
|
||||||
morph: morph,
|
morph: morph,
|
||||||
progress: progress,
|
progress: progress,
|
||||||
angle: angle,
|
angle: angle,
|
||||||
color: color,
|
color: color,
|
||||||
|
size: size,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateRenderObject(
|
void updateRenderObject(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
_RenderM3ELoadingIndicator renderObject,
|
RenderM3ELoadingIndicator renderObject,
|
||||||
) {
|
) {
|
||||||
renderObject
|
renderObject
|
||||||
..morph = morph
|
..morph = morph
|
||||||
..progress = progress
|
..progress = progress
|
||||||
..angle = angle
|
..angle = angle
|
||||||
..color = color;
|
..color = color
|
||||||
|
..preferredSize = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RenderM3ELoadingIndicator extends RenderBox {
|
class RenderM3ELoadingIndicator extends RenderBox {
|
||||||
_RenderM3ELoadingIndicator({
|
RenderM3ELoadingIndicator({
|
||||||
required Morph morph,
|
required Morph morph,
|
||||||
required double progress,
|
required double progress,
|
||||||
required double angle,
|
required double angle,
|
||||||
required Color color,
|
required Color color,
|
||||||
|
required Size size,
|
||||||
}) : _morph = morph,
|
}) : _morph = morph,
|
||||||
_progress = progress,
|
_progress = progress,
|
||||||
_angle = angle,
|
_angle = angle,
|
||||||
|
_preferredSize = size,
|
||||||
_color = color,
|
_color = color,
|
||||||
_paint = Paint()
|
_paint = Paint()
|
||||||
..style = PaintingStyle.fill
|
..style = PaintingStyle.fill
|
||||||
@@ -223,20 +221,38 @@ class _RenderM3ELoadingIndicator extends RenderBox {
|
|||||||
markNeedsPaint();
|
markNeedsPaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Size _preferredSize;
|
||||||
|
set preferredSize(Size value) {
|
||||||
|
if (_preferredSize == value) return;
|
||||||
|
_preferredSize = size;
|
||||||
|
markNeedsLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size computeDryLayout(covariant BoxConstraints constraints) {
|
||||||
|
return constraints.constrain(_preferredSize);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void performLayout() {
|
void performLayout() {
|
||||||
size = constraints.constrainDimensions(40, 40);
|
size = computeDryLayout(constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||||
|
super.describeSemanticsConfiguration(config);
|
||||||
|
config.role = .loadingSpinner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(PaintingContext context, Offset offset) {
|
void paint(PaintingContext context, Offset offset) {
|
||||||
final width = size.width;
|
final width = size.width;
|
||||||
final value = size.width / 2;
|
final value = size.width / 2;
|
||||||
final matrix = Matrix4.identity()
|
final matrix =
|
||||||
..translateByDouble(offset.dx + value, offset.dy + value, 0.0, 1.0)
|
Matrix4.translationValues(offset.dx + value, offset.dy + value, 0.0)
|
||||||
..rotateZ(angle)
|
..rotateZ(angle)
|
||||||
..translateByDouble(-value, -value, 0.0, 1.0)
|
..translateByDouble(-value, -value, 0.0, 1.0)
|
||||||
..scaleByDouble(width, width, width, 1.0);
|
..scaleByDouble(width, width, width, 1.0);
|
||||||
final path = morph.toPath(progress: progress).transform(matrix.storage);
|
final path = morph.toPath(progress: progress).transform(matrix.storage);
|
||||||
|
|
||||||
context.canvas.drawPath(path, _paint);
|
context.canvas.drawPath(path, _paint);
|
||||||
|
|||||||
41
lib/common/widgets/loading_widget/morphs.dart
Normal file
41
lib/common/widgets/loading_widget/morphs.dart
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import 'package:material_new_shapes/material_new_shapes.dart';
|
||||||
|
|
||||||
|
abstract final class Morphs {
|
||||||
|
static List<Morph> buildMorph(
|
||||||
|
List<RoundedPolygon> shapes, {
|
||||||
|
bool loop = true,
|
||||||
|
}) {
|
||||||
|
assert(shapes.length >= 2);
|
||||||
|
return [
|
||||||
|
for (var i = 0; i < shapes.length - 1; i++)
|
||||||
|
Morph(shapes[i], shapes[i + 1]),
|
||||||
|
if (loop) Morph(shapes[shapes.length - 1], shapes[0]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static final loadingMorphs = buildMorph([
|
||||||
|
MaterialShapes.softBurst,
|
||||||
|
MaterialShapes.cookie9Sided,
|
||||||
|
MaterialShapes.pentagon,
|
||||||
|
MaterialShapes.pill,
|
||||||
|
MaterialShapes.sunny,
|
||||||
|
MaterialShapes.cookie4Sided,
|
||||||
|
MaterialShapes.oval,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// static final refreshMorphs = buildMorph([
|
||||||
|
// MaterialShapes.softBurst,
|
||||||
|
// MaterialShapes.cookie9Sided,
|
||||||
|
// MaterialShapes.gem,
|
||||||
|
// MaterialShapes.flower,
|
||||||
|
// MaterialShapes.sunny,
|
||||||
|
// MaterialShapes.cookie4Sided,
|
||||||
|
// MaterialShapes.oval,
|
||||||
|
// MaterialShapes.cookie12Sided,
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// static final manualMorph = Morph(
|
||||||
|
// MaterialShapes.circle,
|
||||||
|
// MaterialShapes.softBurst,
|
||||||
|
// );
|
||||||
|
}
|
||||||
@@ -1,168 +1,163 @@
|
|||||||
|
import 'package:PiliPlus/common/assets.dart';
|
||||||
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||||
import 'package:PiliPlus/models/common/avatar_badge_type.dart';
|
import 'package:PiliPlus/models/common/avatar_badge_type.dart';
|
||||||
import 'package:PiliPlus/models/common/image_type.dart';
|
import 'package:PiliPlus/models/common/image_type.dart';
|
||||||
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||||
import 'package:PiliPlus/utils/extension/string_ext.dart';
|
|
||||||
import 'package:PiliPlus/utils/page_utils.dart';
|
import 'package:PiliPlus/utils/page_utils.dart';
|
||||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class PendantAvatar extends StatelessWidget {
|
class PendantAvatar extends StatelessWidget {
|
||||||
final BadgeType _badgeType;
|
const PendantAvatar(
|
||||||
final String? avatar;
|
this.url, {
|
||||||
final double size;
|
|
||||||
final double badgeSize;
|
|
||||||
final String? garbPendantImage;
|
|
||||||
final int? roomId;
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
final bool isMemberAvatar;
|
|
||||||
|
|
||||||
const PendantAvatar({
|
|
||||||
super.key,
|
super.key,
|
||||||
required this.avatar,
|
required double size,
|
||||||
required this.size,
|
|
||||||
this.isMemberAvatar = false,
|
|
||||||
double? badgeSize,
|
double? badgeSize,
|
||||||
bool isVip = false,
|
int? vipStatus,
|
||||||
int? officialType,
|
int? officialType,
|
||||||
this.garbPendantImage,
|
this.pendantImage,
|
||||||
|
this.pendentOffset = 6,
|
||||||
this.roomId,
|
this.roomId,
|
||||||
|
this.liveBottom,
|
||||||
|
this.liveFontSize,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
}) : _badgeType = officialType == null || officialType < 0
|
}) : preferredSize = size,
|
||||||
? isVip
|
badgeSize = badgeSize ?? size / 3,
|
||||||
? BadgeType.vip
|
badgeType = officialType == null || officialType < 0
|
||||||
: BadgeType.none
|
? vipStatus != null && vipStatus > 0
|
||||||
|
? .vip
|
||||||
|
: .none
|
||||||
: officialType == 0
|
: officialType == 0
|
||||||
? BadgeType.person
|
? .person
|
||||||
: officialType == 1
|
: officialType == 1
|
||||||
? BadgeType.institution
|
? .institution
|
||||||
: BadgeType.none,
|
: .none;
|
||||||
badgeSize = badgeSize ?? size / 3;
|
|
||||||
|
|
||||||
static bool showDynDecorate = Pref.showDynDecorate;
|
static bool showDecorate = Pref.showDecorate;
|
||||||
|
|
||||||
|
final BadgeType badgeType;
|
||||||
|
final String? url;
|
||||||
|
final double preferredSize;
|
||||||
|
final double badgeSize;
|
||||||
|
final String? pendantImage;
|
||||||
|
final double pendentOffset;
|
||||||
|
final int? roomId;
|
||||||
|
final double? liveBottom;
|
||||||
|
final double? liveFontSize;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
final showPendant = showDecorate && pendantImage?.isNotEmpty == true;
|
||||||
|
final size = showPendant ? preferredSize - pendentOffset : preferredSize;
|
||||||
Widget? pendant;
|
Widget? pendant;
|
||||||
if (showDynDecorate && !garbPendantImage.isNullOrEmpty) {
|
if (showPendant) {
|
||||||
final pendantSize = size * 1.75;
|
final pendantSize = size * 1.75;
|
||||||
pendant = Positioned(
|
pendant = Positioned(
|
||||||
// -(size * 1.75 - size) / 2
|
// -(size * 1.75 - size) / 2
|
||||||
top: -0.375 * size + (isMemberAvatar ? 2 : 0),
|
top: -0.375 * size + pendentOffset / 2,
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
type: .emote,
|
type: .emote,
|
||||||
width: pendantSize,
|
width: pendantSize,
|
||||||
height: pendantSize,
|
height: pendantSize,
|
||||||
src: garbPendantImage,
|
src: pendantImage,
|
||||||
getPlaceHolder: () => const SizedBox.shrink(),
|
getPlaceHolder: () => const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Stack(
|
Widget avatar = NetworkImgLayer(
|
||||||
alignment: Alignment.bottomCenter,
|
src: url,
|
||||||
clipBehavior: Clip.none,
|
width: size,
|
||||||
|
height: size,
|
||||||
|
type: ImageType.avatar,
|
||||||
|
);
|
||||||
|
if (onTap != null) {
|
||||||
|
avatar = GestureDetector(
|
||||||
|
behavior: .opaque,
|
||||||
|
onTap: onTap,
|
||||||
|
child: avatar,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Widget child = Stack(
|
||||||
|
clipBehavior: .none,
|
||||||
|
alignment: .center,
|
||||||
children: [
|
children: [
|
||||||
onTap == null
|
avatar,
|
||||||
? _buildAvatar(colorScheme, isMemberAvatar)
|
|
||||||
: GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: onTap,
|
|
||||||
child: _buildAvatar(colorScheme, isMemberAvatar),
|
|
||||||
),
|
|
||||||
?pendant,
|
?pendant,
|
||||||
if (roomId != null)
|
if (roomId != null)
|
||||||
Positioned(
|
_buildLive(colorScheme)
|
||||||
bottom: 0,
|
else if (badgeType != .none)
|
||||||
child: InkWell(
|
_buildBadge(context, colorScheme),
|
||||||
onTap: () => PageUtils.toLiveRoom(roomId),
|
],
|
||||||
child: Container(
|
);
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1),
|
if (showPendant) {
|
||||||
decoration: BoxDecoration(
|
return SizedBox.square(
|
||||||
color: colorScheme.secondaryContainer,
|
dimension: preferredSize,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(36)),
|
child: child,
|
||||||
),
|
);
|
||||||
child: Row(
|
}
|
||||||
mainAxisSize: MainAxisSize.min,
|
return child;
|
||||||
children: [
|
}
|
||||||
Icon(
|
|
||||||
size: 16,
|
Widget _buildLive(ColorScheme colorScheme) {
|
||||||
applyTextScaling: true,
|
final fontSize = liveFontSize ?? 13.0;
|
||||||
Icons.equalizer_rounded,
|
return Positioned(
|
||||||
color: colorScheme.onSecondaryContainer,
|
bottom: liveBottom ?? 0.0,
|
||||||
),
|
child: GestureDetector(
|
||||||
Text(
|
onTap: () => PageUtils.toLiveRoom(roomId),
|
||||||
'直播中',
|
child: Container(
|
||||||
style: TextStyle(
|
padding: const .symmetric(horizontal: 5, vertical: 1),
|
||||||
height: 1,
|
decoration: BoxDecoration(
|
||||||
fontSize: 13,
|
color: colorScheme.secondaryContainer,
|
||||||
color: colorScheme.onSecondaryContainer,
|
borderRadius: Style.mdRadius,
|
||||||
),
|
),
|
||||||
),
|
child: Row(
|
||||||
],
|
mainAxisSize: .min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
size: fontSize + 3,
|
||||||
|
applyTextScaling: true,
|
||||||
|
Icons.equalizer_rounded,
|
||||||
|
color: colorScheme.onSecondaryContainer,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'直播中',
|
||||||
|
style: TextStyle(
|
||||||
|
height: 1,
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: colorScheme.onSecondaryContainer,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
)
|
),
|
||||||
else if (_badgeType != BadgeType.none)
|
),
|
||||||
_buildBadge(context, colorScheme, isMemberAvatar),
|
),
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAvatar(ColorScheme colorScheme, bool isMemberAvatar) =>
|
Widget _buildBadge(BuildContext context, ColorScheme colorScheme) {
|
||||||
isMemberAvatar
|
final child = switch (badgeType) {
|
||||||
? DecoratedBox(
|
.vip => Image.asset(
|
||||||
decoration: BoxDecoration(
|
Assets.vipIcon,
|
||||||
border: Border.all(
|
|
||||||
width: 2,
|
|
||||||
color: colorScheme.surface,
|
|
||||||
),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(2),
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
src: avatar,
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
type: ImageType.avatar,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: NetworkImgLayer(
|
|
||||||
src: avatar,
|
|
||||||
width: size,
|
|
||||||
height: size,
|
|
||||||
type: ImageType.avatar,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildBadge(
|
|
||||||
BuildContext context,
|
|
||||||
ColorScheme colorScheme,
|
|
||||||
bool isMemberAvatar,
|
|
||||||
) {
|
|
||||||
final child = switch (_badgeType) {
|
|
||||||
BadgeType.vip => Image.asset(
|
|
||||||
'assets/images/big-vip.png',
|
|
||||||
width: badgeSize,
|
width: badgeSize,
|
||||||
height: badgeSize,
|
height: badgeSize,
|
||||||
cacheWidth: badgeSize.cacheSize(context),
|
cacheWidth: badgeSize.cacheSize(context),
|
||||||
semanticLabel: _badgeType.desc,
|
semanticLabel: badgeType.desc,
|
||||||
),
|
),
|
||||||
_ => Icon(
|
_ => Icon(
|
||||||
Icons.offline_bolt,
|
Icons.offline_bolt,
|
||||||
color: _badgeType.color,
|
color: badgeType.color,
|
||||||
size: badgeSize,
|
size: badgeSize,
|
||||||
semanticLabel: _badgeType.desc,
|
semanticLabel: badgeType.desc,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
final offset = isMemberAvatar ? 2.0 : 0.0;
|
|
||||||
return Positioned(
|
return Positioned(
|
||||||
right: offset,
|
right: 0.0,
|
||||||
bottom: offset,
|
bottom: 0.0,
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ import 'package:flutter/rendering.dart'
|
|||||||
ContainerRenderObjectMixin,
|
ContainerRenderObjectMixin,
|
||||||
MultiChildLayoutParentData,
|
MultiChildLayoutParentData,
|
||||||
RenderBoxContainerDefaultsMixin,
|
RenderBoxContainerDefaultsMixin,
|
||||||
BoxHitTestResult;
|
BoxHitTestResult,
|
||||||
|
TransformLayer;
|
||||||
|
|
||||||
class PlayerBar extends MultiChildRenderObjectWidget {
|
class PlayerBar extends MultiChildRenderObjectWidget {
|
||||||
const PlayerBar({
|
const PlayerBar({
|
||||||
@@ -92,14 +93,16 @@ class RenderBottomBar extends RenderBox
|
|||||||
@override
|
@override
|
||||||
void paint(PaintingContext context, Offset offset) {
|
void paint(PaintingContext context, Offset offset) {
|
||||||
if (_transform != null) {
|
if (_transform != null) {
|
||||||
context.pushTransform(
|
layer = context.pushTransform(
|
||||||
needsCompositing,
|
needsCompositing,
|
||||||
offset,
|
offset,
|
||||||
_transform!,
|
_transform!,
|
||||||
defaultPaint,
|
defaultPaint,
|
||||||
|
oldLayer: layer as TransformLayer?,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
defaultPaint(context, offset);
|
defaultPaint(context, offset);
|
||||||
|
layer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
Widget selectMask(
|
Widget selectMask(
|
||||||
ThemeData theme,
|
ThemeData theme,
|
||||||
bool checked, {
|
bool checked, {
|
||||||
BorderRadiusGeometry borderRadius = StyleString.mdRadius,
|
BorderRadiusGeometry borderRadius = Style.mdRadius,
|
||||||
}) {
|
}) {
|
||||||
return AnimatedOpacity(
|
return AnimatedOpacity(
|
||||||
opacity: checked ? 1 : 0,
|
opacity: checked ? 1 : 0,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||||
@@ -120,14 +120,14 @@ class VideoCardH extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: StyleString.safeSpace,
|
horizontal: Style.safeSpace,
|
||||||
vertical: 5,
|
vertical: 5,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: Style.aspectRatio,
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
final double maxWidth = boxConstraints.maxWidth;
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/style.dart';
|
||||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||||
@@ -22,7 +22,7 @@ import 'package:intl/intl.dart';
|
|||||||
|
|
||||||
// 视频卡片 - 垂直布局
|
// 视频卡片 - 垂直布局
|
||||||
class VideoCardV extends StatelessWidget {
|
class VideoCardV extends StatelessWidget {
|
||||||
final BaseRecVideoItemModel videoItem;
|
final BaseRcmdVideoItemModel videoItem;
|
||||||
final VoidCallback? onRemove;
|
final VoidCallback? onRemove;
|
||||||
|
|
||||||
const VideoCardV({
|
const VideoCardV({
|
||||||
@@ -87,7 +87,7 @@ class VideoCardV extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: Style.aspectRatio,
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
@@ -229,7 +229,7 @@ class VideoCardV extends StatelessWidget {
|
|||||||
value: videoItem.stat.danmu,
|
value: videoItem.stat.danmu,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
if (videoItem is RecVideoItemModel) ...[
|
if (videoItem is RcmdVideoItemModel) ...[
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text.rich(
|
Text.rich(
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@@ -248,7 +248,7 @@ class VideoCardV extends StatelessWidget {
|
|||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
],
|
],
|
||||||
// deprecated
|
// deprecated
|
||||||
// else if (videoItem is RecVideoItemAppModel &&
|
// else if (videoItem is RcmdVideoItemAppModel &&
|
||||||
// videoItem.desc != null &&
|
// videoItem.desc != null &&
|
||||||
// videoItem.desc!.contains(' · ')) ...[
|
// videoItem.desc!.contains(' · ')) ...[
|
||||||
// const Spacer(),
|
// const Spacer(),
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class VideoPopupMenu extends StatelessWidget {
|
|||||||
SmartDialog.showToast("请退出账号后重新登录");
|
SmartDialog.showToast("请退出账号后重新登录");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (videoItem case final RecVideoItemAppModel item) {
|
if (videoItem case final RcmdVideoItemAppModel item) {
|
||||||
ThreePoint? tp = item.threePoint;
|
ThreePoint? tp = item.threePoint;
|
||||||
if (tp == null) {
|
if (tp == null) {
|
||||||
SmartDialog.showToast("未能获取threePoint");
|
SmartDialog.showToast("未能获取threePoint");
|
||||||
|
|||||||
@@ -649,14 +649,9 @@ abstract final class Api {
|
|||||||
/// mid
|
/// mid
|
||||||
static const getMemberViewApi = '/x/space/upstat';
|
static const getMemberViewApi = '/x/space/upstat';
|
||||||
|
|
||||||
/// 查询某个专栏
|
static const seasonArchives = '/x/polymer/web-space/seasons_archives_list';
|
||||||
/// mid
|
|
||||||
/// season_id
|
static const seriesArchives = '/x/series/archives';
|
||||||
/// sort_reverse
|
|
||||||
/// page_num
|
|
||||||
/// page_size
|
|
||||||
static const getSeasonDetailApi =
|
|
||||||
'/x/polymer/web-space/seasons_archives_list';
|
|
||||||
|
|
||||||
/// 获取未读动态数
|
/// 获取未读动态数
|
||||||
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
|
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
|
||||||
@@ -835,6 +830,10 @@ abstract final class Api {
|
|||||||
|
|
||||||
static const String dynReserve = '/x/dynamic/feed/reserve/click';
|
static const String dynReserve = '/x/dynamic/feed/reserve/click';
|
||||||
|
|
||||||
|
static const String spaceReserve = '/x/space/reserve';
|
||||||
|
|
||||||
|
static const String spaceReserveCancel = '/x/space/reserve/cancel';
|
||||||
|
|
||||||
static const String favPugv = '/pugv/app/web/favorite/page';
|
static const String favPugv = '/pugv/app/web/favorite/page';
|
||||||
|
|
||||||
static const String addFavPugv = '/pugv/app/web/favorite/add';
|
static const String addFavPugv = '/pugv/app/web/favorite/add';
|
||||||
@@ -999,4 +998,12 @@ abstract final class Api {
|
|||||||
static const String replySubjectModify = '/x/v2/reply/subject/modify';
|
static const String replySubjectModify = '/x/v2/reply/subject/modify';
|
||||||
|
|
||||||
static const String videoshot = '/x/player/videoshot';
|
static const String videoshot = '/x/player/videoshot';
|
||||||
|
|
||||||
|
static const String liveMedalWall =
|
||||||
|
'${HttpString.liveBaseUrl}/xlive/web-ucenter/user/MedalWall';
|
||||||
|
|
||||||
|
static const String memberGuard =
|
||||||
|
'${HttpString.liveBaseUrl}/xlive/app-ucenter/v1/guard/MainGuardCardAll';
|
||||||
|
|
||||||
|
static const String bubble = '/x/tribee/v1/dyn/all';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import 'package:PiliPlus/models/dynamics/vote_model.dart';
|
|||||||
import 'package:PiliPlus/models_new/article/article_info/data.dart';
|
import 'package:PiliPlus/models_new/article/article_info/data.dart';
|
||||||
import 'package:PiliPlus/models_new/article/article_list/data.dart';
|
import 'package:PiliPlus/models_new/article/article_list/data.dart';
|
||||||
import 'package:PiliPlus/models_new/article/article_view/data.dart';
|
import 'package:PiliPlus/models_new/article/article_view/data.dart';
|
||||||
|
import 'package:PiliPlus/models_new/bubble/data.dart';
|
||||||
import 'package:PiliPlus/models_new/dynamic/dyn_mention/data.dart';
|
import 'package:PiliPlus/models_new/dynamic/dyn_mention/data.dart';
|
||||||
import 'package:PiliPlus/models_new/dynamic/dyn_mention/group.dart';
|
import 'package:PiliPlus/models_new/dynamic/dyn_mention/group.dart';
|
||||||
import 'package:PiliPlus/models_new/dynamic/dyn_reserve/data.dart';
|
import 'package:PiliPlus/models_new/dynamic/dyn_reserve/data.dart';
|
||||||
@@ -777,4 +778,30 @@ abstract final class DynamicsHttp {
|
|||||||
return Error(res.data['message']);
|
return Error(res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<BubbleData>> bubble({
|
||||||
|
required Object tribeId,
|
||||||
|
Object? categoryId,
|
||||||
|
int? sortType,
|
||||||
|
required int page,
|
||||||
|
}) async {
|
||||||
|
final res = await Request().get(
|
||||||
|
Api.bubble,
|
||||||
|
queryParameters: {
|
||||||
|
'tribee_id': tribeId,
|
||||||
|
'category_id': ?categoryId,
|
||||||
|
'sort_type': ?sortType,
|
||||||
|
'page_size': 20,
|
||||||
|
'page_num': page,
|
||||||
|
'web_location': 333.40165,
|
||||||
|
'x-bili-device-req-json':
|
||||||
|
'{"platform":"web","device":"pc","spmid":"333.40165"}',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return Success(BubbleData.fromJson(res.data['data']));
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
lib/http/error_msg.dart
Normal file
3
lib/http/error_msg.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
const errorMsg = {
|
||||||
|
-352: '风控校验失败,请检查登录状态',
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:PiliPlus/http/api.dart';
|
import 'package:PiliPlus/http/api.dart';
|
||||||
|
import 'package:PiliPlus/http/error_msg.dart';
|
||||||
import 'package:PiliPlus/http/init.dart';
|
import 'package:PiliPlus/http/init.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models_new/follow/data.dart';
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
@@ -23,7 +24,7 @@ abstract final class FanHttp {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return Success(FollowData.fromJson(res.data['data']));
|
return Success(FollowData.fromJson(res.data['data']));
|
||||||
} else {
|
} else {
|
||||||
return Error(res.data['message']);
|
return Error(errorMsg[res.data['code']] ?? res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:PiliPlus/http/api.dart';
|
import 'package:PiliPlus/http/api.dart';
|
||||||
|
import 'package:PiliPlus/http/error_msg.dart';
|
||||||
import 'package:PiliPlus/http/init.dart';
|
import 'package:PiliPlus/http/init.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models_new/follow/data.dart';
|
import 'package:PiliPlus/models_new/follow/data.dart';
|
||||||
@@ -23,7 +24,7 @@ abstract final class FollowHttp {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return Success(FollowData.fromJson(res.data['data']));
|
return Success(FollowData.fromJson(res.data['data']));
|
||||||
} else {
|
} else {
|
||||||
return Error(res.data['message']);
|
return Error(errorMsg[res.data['code']] ?? res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import 'package:PiliPlus/models_new/live/live_emote/data.dart';
|
|||||||
import 'package:PiliPlus/models_new/live/live_emote/datum.dart';
|
import 'package:PiliPlus/models_new/live/live_emote/datum.dart';
|
||||||
import 'package:PiliPlus/models_new/live/live_feed_index/data.dart';
|
import 'package:PiliPlus/models_new/live/live_feed_index/data.dart';
|
||||||
import 'package:PiliPlus/models_new/live/live_follow/data.dart';
|
import 'package:PiliPlus/models_new/live/live_follow/data.dart';
|
||||||
|
import 'package:PiliPlus/models_new/live/live_medal_wall/data.dart';
|
||||||
import 'package:PiliPlus/models_new/live/live_room_info_h5/data.dart';
|
import 'package:PiliPlus/models_new/live/live_room_info_h5/data.dart';
|
||||||
import 'package:PiliPlus/models_new/live/live_room_play_info/data.dart';
|
import 'package:PiliPlus/models_new/live/live_room_play_info/data.dart';
|
||||||
import 'package:PiliPlus/models_new/live/live_search/data.dart';
|
import 'package:PiliPlus/models_new/live/live_search/data.dart';
|
||||||
@@ -742,4 +743,18 @@ abstract final class LiveHttp {
|
|||||||
return Error(res.data['message']);
|
return Error(res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<MedalWallData>> liveMedalWall({
|
||||||
|
required Object mid,
|
||||||
|
}) async {
|
||||||
|
final res = await Request().get(
|
||||||
|
Api.liveMedalWall,
|
||||||
|
queryParameters: {'target_id': mid},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return Success(MedalWallData.fromJson(res.data['data']));
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,14 @@ import 'package:PiliPlus/common/constants.dart';
|
|||||||
import 'package:PiliPlus/http/api.dart';
|
import 'package:PiliPlus/http/api.dart';
|
||||||
import 'package:PiliPlus/http/browser_ua.dart';
|
import 'package:PiliPlus/http/browser_ua.dart';
|
||||||
import 'package:PiliPlus/http/constants.dart';
|
import 'package:PiliPlus/http/constants.dart';
|
||||||
|
import 'package:PiliPlus/http/error_msg.dart';
|
||||||
import 'package:PiliPlus/http/init.dart';
|
import 'package:PiliPlus/http/init.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/models/common/member/archive_order_type_app.dart';
|
||||||
|
import 'package:PiliPlus/models/common/member/archive_order_type_web.dart';
|
||||||
|
import 'package:PiliPlus/models/common/member/archive_sort_type_app.dart';
|
||||||
import 'package:PiliPlus/models/common/member/contribute_type.dart';
|
import 'package:PiliPlus/models/common/member/contribute_type.dart';
|
||||||
|
import 'package:PiliPlus/models/common/member/web_ss_type.dart';
|
||||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||||
import 'package:PiliPlus/models/member/info.dart';
|
import 'package:PiliPlus/models/member/info.dart';
|
||||||
import 'package:PiliPlus/models/member/tags.dart';
|
import 'package:PiliPlus/models/member/tags.dart';
|
||||||
@@ -14,7 +19,9 @@ import 'package:PiliPlus/models_new/follow/data.dart';
|
|||||||
import 'package:PiliPlus/models_new/follow/list.dart';
|
import 'package:PiliPlus/models_new/follow/list.dart';
|
||||||
import 'package:PiliPlus/models_new/member/coin_like_arc/data.dart';
|
import 'package:PiliPlus/models_new/member/coin_like_arc/data.dart';
|
||||||
import 'package:PiliPlus/models_new/member/search_archive/data.dart';
|
import 'package:PiliPlus/models_new/member/search_archive/data.dart';
|
||||||
|
import 'package:PiliPlus/models_new/member/season_web/data.dart';
|
||||||
import 'package:PiliPlus/models_new/member_card_info/data.dart';
|
import 'package:PiliPlus/models_new/member_card_info/data.dart';
|
||||||
|
import 'package:PiliPlus/models_new/member_guard/data.dart';
|
||||||
import 'package:PiliPlus/models_new/space/space/data.dart';
|
import 'package:PiliPlus/models_new/space/space/data.dart';
|
||||||
import 'package:PiliPlus/models_new/space/space_archive/data.dart';
|
import 'package:PiliPlus/models_new/space/space_archive/data.dart';
|
||||||
import 'package:PiliPlus/models_new/space/space_article/data.dart';
|
import 'package:PiliPlus/models_new/space/space_article/data.dart';
|
||||||
@@ -113,8 +120,8 @@ abstract final class MemberHttp {
|
|||||||
required ContributeType type,
|
required ContributeType type,
|
||||||
required int? mid,
|
required int? mid,
|
||||||
String? aid,
|
String? aid,
|
||||||
String? order,
|
ArchiveOrderTypeApp? order,
|
||||||
String? sort,
|
ArchiveSortTypeApp? sort,
|
||||||
int? pn,
|
int? pn,
|
||||||
int? next,
|
int? next,
|
||||||
int? seasonId,
|
int? seasonId,
|
||||||
@@ -135,22 +142,15 @@ abstract final class MemberHttp {
|
|||||||
'next': ?next,
|
'next': ?next,
|
||||||
'season_id': ?seasonId,
|
'season_id': ?seasonId,
|
||||||
'series_id': ?seriesId,
|
'series_id': ?seriesId,
|
||||||
'qn': type == ContributeType.video ? 80 : 32,
|
'qn': type == .video ? 80 : 32,
|
||||||
'order': ?order,
|
'order': ?order?.name,
|
||||||
'sort': ?sort,
|
'sort': ?sort?.name,
|
||||||
'include_cursor': ?includeCursor,
|
'include_cursor': ?includeCursor,
|
||||||
'statistics': Constants.statisticsApp,
|
'statistics': Constants.statisticsApp,
|
||||||
'vmid': mid,
|
'vmid': mid,
|
||||||
};
|
};
|
||||||
final res = await Request().get(
|
final res = await Request().get(
|
||||||
switch (type) {
|
type.api,
|
||||||
ContributeType.video => Api.spaceArchive,
|
|
||||||
ContributeType.charging => Api.spaceChargingArchive,
|
|
||||||
ContributeType.season => Api.spaceSeason,
|
|
||||||
ContributeType.series => Api.spaceSeries,
|
|
||||||
ContributeType.bangumi => Api.spaceBangumi,
|
|
||||||
ContributeType.comic => Api.spaceComic,
|
|
||||||
},
|
|
||||||
queryParameters: params,
|
queryParameters: params,
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
@@ -348,12 +348,12 @@ abstract final class MemberHttp {
|
|||||||
|
|
||||||
static Future<LoadingState<SearchArchiveData>> searchArchive({
|
static Future<LoadingState<SearchArchiveData>> searchArchive({
|
||||||
required Object mid,
|
required Object mid,
|
||||||
|
int tid = 0, // e.g. pugv: 196
|
||||||
int ps = 30,
|
int ps = 30,
|
||||||
int tid = 0,
|
required int pn,
|
||||||
int? pn,
|
|
||||||
String? keyword,
|
String? keyword,
|
||||||
String order = 'pubdate',
|
String? specialType, // e.g. 'charging'
|
||||||
bool orderAvoided = true,
|
ArchiveOrderTypeWeb order = .pubdate,
|
||||||
}) async {
|
}) async {
|
||||||
String dmImgStr = Utils.base64EncodeRandomString(16, 64);
|
String dmImgStr = Utils.base64EncodeRandomString(16, 64);
|
||||||
String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128);
|
String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128);
|
||||||
@@ -361,12 +361,13 @@ abstract final class MemberHttp {
|
|||||||
'mid': mid,
|
'mid': mid,
|
||||||
'ps': ps,
|
'ps': ps,
|
||||||
'tid': tid,
|
'tid': tid,
|
||||||
'pn': ?pn,
|
'pn': pn,
|
||||||
'keyword': ?keyword,
|
'keyword': ?keyword,
|
||||||
'order': order,
|
'special_type': ?specialType,
|
||||||
|
'order': order.name,
|
||||||
'platform': 'web',
|
'platform': 'web',
|
||||||
'web_location': 1550101,
|
'web_location': 333.1387,
|
||||||
'order_avoided': orderAvoided,
|
'order_avoided': true,
|
||||||
'dm_img_list': '[]',
|
'dm_img_list': '[]',
|
||||||
'dm_img_str': dmImgStr,
|
'dm_img_str': dmImgStr,
|
||||||
'dm_cover_img_str': dmCoverImgStr,
|
'dm_cover_img_str': dmCoverImgStr,
|
||||||
@@ -386,10 +387,50 @@ abstract final class MemberHttp {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return Success(SearchArchiveData.fromJson(res.data['data']));
|
return Success(SearchArchiveData.fromJson(res.data['data']));
|
||||||
} else {
|
} else {
|
||||||
Map errMap = const {
|
return Error(errorMsg[res.data['code']] ?? res.data['message']);
|
||||||
-352: '风控校验失败,请检查登录状态',
|
}
|
||||||
};
|
}
|
||||||
return Error(errMap[res.data['code']] ?? res.data['message']);
|
|
||||||
|
static Future<LoadingState<SeasonWebData>> seasonSeriesWeb({
|
||||||
|
required WebSsType type,
|
||||||
|
required Object mid,
|
||||||
|
required Object id,
|
||||||
|
int ps = 30,
|
||||||
|
required int pn,
|
||||||
|
ArchiveSortTypeApp sort = .desc,
|
||||||
|
}) async {
|
||||||
|
final res = await Request().get(
|
||||||
|
type.api,
|
||||||
|
queryParameters: switch (type) {
|
||||||
|
.season => {
|
||||||
|
'mid': mid,
|
||||||
|
'season_id': id,
|
||||||
|
'sort_reverse': sort == .asc,
|
||||||
|
'page_size': ps,
|
||||||
|
'page_num': pn,
|
||||||
|
'web_location': 333.1387,
|
||||||
|
},
|
||||||
|
.series => {
|
||||||
|
'mid': mid,
|
||||||
|
'series_id': id,
|
||||||
|
'sort': sort.name,
|
||||||
|
'ps': ps,
|
||||||
|
'pn': pn,
|
||||||
|
'web_location': 333.1387,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: Options(
|
||||||
|
headers: {
|
||||||
|
HttpHeaders.userAgentHeader: BrowserUa.pc,
|
||||||
|
HttpHeaders.refererHeader: '${HttpString.spaceBaseUrl}/$mid',
|
||||||
|
'origin': HttpString.spaceBaseUrl,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return Success(SeasonWebData.fromJson(res.data['data']));
|
||||||
|
} else {
|
||||||
|
return Error(errorMsg[res.data['code']] ?? res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,10 +478,7 @@ abstract final class MemberHttp {
|
|||||||
return Error('$e\n\n$s');
|
return Error('$e\n\n$s');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Map errMap = const {
|
return Error(errorMsg[res.data['code']] ?? res.data['message']);
|
||||||
-352: '风控校验失败,请检查登录状态',
|
|
||||||
};
|
|
||||||
return Error(errMap[res.data['code']] ?? res.data['message']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,7 +589,7 @@ abstract final class MemberHttp {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Error(res.data['message']);
|
return Error(errorMsg[res.data['code']] ?? res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,4 +831,23 @@ abstract final class MemberHttp {
|
|||||||
return Error(res.data['message']);
|
return Error(res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<MemberGuardData>> memberGuard({
|
||||||
|
required Object ruid,
|
||||||
|
required int page,
|
||||||
|
}) async {
|
||||||
|
final res = await Request().get(
|
||||||
|
Api.memberGuard,
|
||||||
|
queryParameters: {
|
||||||
|
'page': page,
|
||||||
|
'page_size': 20,
|
||||||
|
'ruid': ruid,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return Success(MemberGuardData.fromJson(res.data['data']));
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:PiliPlus/models_new/history/data.dart';
|
|||||||
import 'package:PiliPlus/models_new/later/data.dart';
|
import 'package:PiliPlus/models_new/later/data.dart';
|
||||||
import 'package:PiliPlus/models_new/login_log/data.dart';
|
import 'package:PiliPlus/models_new/login_log/data.dart';
|
||||||
import 'package:PiliPlus/models_new/media_list/data.dart';
|
import 'package:PiliPlus/models_new/media_list/data.dart';
|
||||||
|
import 'package:PiliPlus/models_new/relation/data.dart';
|
||||||
import 'package:PiliPlus/models_new/space_setting/data.dart';
|
import 'package:PiliPlus/models_new/space_setting/data.dart';
|
||||||
import 'package:PiliPlus/models_new/sub/sub/data.dart';
|
import 'package:PiliPlus/models_new/sub/sub/data.dart';
|
||||||
import 'package:PiliPlus/models_new/user_real_name/data.dart';
|
import 'package:PiliPlus/models_new/user_real_name/data.dart';
|
||||||
@@ -269,7 +270,7 @@ abstract final class UserHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LoadingState<Map>> hasFollow(int mid) async {
|
static Future<LoadingState<RelationData>> userRelation(int mid) async {
|
||||||
final res = await Request().get(
|
final res = await Request().get(
|
||||||
Api.relation,
|
Api.relation,
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
@@ -277,7 +278,7 @@ abstract final class UserHttp {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return Success(res.data['data']);
|
return Success(RelationData.fromJson(res.data['data']));
|
||||||
} else {
|
} else {
|
||||||
return Error(res.data['message']);
|
return Error(res.data['message']);
|
||||||
}
|
}
|
||||||
@@ -430,7 +431,9 @@ abstract final class UserHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LoadingState<void>> spaceSettingMod(Map data) async {
|
static Future<LoadingState<void>> spaceSettingMod(
|
||||||
|
Map<String, dynamic> data,
|
||||||
|
) async {
|
||||||
final res = await Request().post(
|
final res = await Request().post(
|
||||||
Api.spaceSettingMod,
|
Api.spaceSettingMod,
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
@@ -568,4 +571,23 @@ abstract final class UserHttp {
|
|||||||
return Error(res.data['message']);
|
return Error(res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<void>> spaceReserve({
|
||||||
|
required Object sid,
|
||||||
|
required bool isFollow,
|
||||||
|
}) async {
|
||||||
|
final res = await Request().post(
|
||||||
|
isFollow ? Api.spaceReserveCancel : Api.spaceReserve,
|
||||||
|
data: {
|
||||||
|
'sid': sid,
|
||||||
|
'csrf': Accounts.main.csrf,
|
||||||
|
},
|
||||||
|
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return const Success(null);
|
||||||
|
} else {
|
||||||
|
return Error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ abstract final class VideoHttp {
|
|||||||
static bool enableFilter = zoneRegExp.pattern.isNotEmpty;
|
static bool enableFilter = zoneRegExp.pattern.isNotEmpty;
|
||||||
|
|
||||||
// 首页推荐视频
|
// 首页推荐视频
|
||||||
static Future<LoadingState<List<RecVideoItemModel>>> rcmdVideoList({
|
static Future<LoadingState<List<RcmdVideoItemModel>>> rcmdVideoList({
|
||||||
required int ps,
|
required int ps,
|
||||||
required int freshIdx,
|
required int freshIdx,
|
||||||
}) async {
|
}) async {
|
||||||
@@ -66,13 +66,13 @@ abstract final class VideoHttp {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
List<RecVideoItemModel> list = <RecVideoItemModel>[];
|
List<RcmdVideoItemModel> list = <RcmdVideoItemModel>[];
|
||||||
for (final i in res.data['data']['item']) {
|
for (final i in res.data['data']['item']) {
|
||||||
//过滤掉live与ad,以及拉黑用户
|
//过滤掉live与ad,以及拉黑用户
|
||||||
if (i['goto'] == 'av' &&
|
if (i['goto'] == 'av' &&
|
||||||
(i['owner'] != null &&
|
(i['owner'] != null &&
|
||||||
!GlobalData().blackMids.contains(i['owner']['mid']))) {
|
!GlobalData().blackMids.contains(i['owner']['mid']))) {
|
||||||
RecVideoItemModel videoItem = RecVideoItemModel.fromJson(i);
|
RcmdVideoItemModel videoItem = RcmdVideoItemModel.fromJson(i);
|
||||||
if (!RecommendFilter.filter(videoItem)) {
|
if (!RecommendFilter.filter(videoItem)) {
|
||||||
list.add(videoItem);
|
list.add(videoItem);
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ abstract final class VideoHttp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 添加额外的loginState变量模拟未登录状态
|
// 添加额外的loginState变量模拟未登录状态
|
||||||
static Future<LoadingState<List<RecVideoItemAppModel>>> rcmdVideoListApp({
|
static Future<LoadingState<List<RcmdVideoItemAppModel>>> rcmdVideoListApp({
|
||||||
required int freshIdx,
|
required int freshIdx,
|
||||||
}) async {
|
}) async {
|
||||||
final params = {
|
final params = {
|
||||||
@@ -139,7 +139,7 @@ abstract final class VideoHttp {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
List<RecVideoItemAppModel> list = <RecVideoItemAppModel>[];
|
List<RcmdVideoItemAppModel> list = <RcmdVideoItemAppModel>[];
|
||||||
for (final i in res.data['data']['items']) {
|
for (final i in res.data['data']['items']) {
|
||||||
// 屏蔽推广和拉黑用户
|
// 屏蔽推广和拉黑用户
|
||||||
if (i['card_goto'] != 'ad_av' &&
|
if (i['card_goto'] != 'ad_av' &&
|
||||||
@@ -152,7 +152,7 @@ abstract final class VideoHttp {
|
|||||||
zoneRegExp.hasMatch(i['args']['tname'])) {
|
zoneRegExp.hasMatch(i['args']['tname'])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i);
|
RcmdVideoItemAppModel videoItem = RcmdVideoItemAppModel.fromJson(i);
|
||||||
if (!RecommendFilter.filter(videoItem)) {
|
if (!RecommendFilter.filter(videoItem)) {
|
||||||
list.add(videoItem);
|
list.add(videoItem);
|
||||||
}
|
}
|
||||||
@@ -564,7 +564,6 @@ abstract final class VideoHttp {
|
|||||||
replyInfo.id.toString(),
|
replyInfo.id.toString(),
|
||||||
(replyInfo.deepCopy()
|
(replyInfo.deepCopy()
|
||||||
..unknownFields.clear()
|
..unknownFields.clear()
|
||||||
..clearMemberV2()
|
|
||||||
..clearTrackInfo())
|
..clearTrackInfo())
|
||||||
.writeToBuffer(),
|
.writeToBuffer(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:PiliPlus/common/widgets/scale_app.dart';
|
|||||||
import 'package:PiliPlus/common/widgets/scroll_behavior.dart';
|
import 'package:PiliPlus/common/widgets/scroll_behavior.dart';
|
||||||
import 'package:PiliPlus/http/init.dart';
|
import 'package:PiliPlus/http/init.dart';
|
||||||
import 'package:PiliPlus/models/common/theme/theme_color_type.dart';
|
import 'package:PiliPlus/models/common/theme/theme_color_type.dart';
|
||||||
|
import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart';
|
||||||
import 'package:PiliPlus/router/app_pages.dart';
|
import 'package:PiliPlus/router/app_pages.dart';
|
||||||
import 'package:PiliPlus/services/account_service.dart';
|
import 'package:PiliPlus/services/account_service.dart';
|
||||||
import 'package:PiliPlus/services/download/download_service.dart';
|
import 'package:PiliPlus/services/download/download_service.dart';
|
||||||
@@ -28,6 +29,7 @@ import 'package:PiliPlus/utils/storage_pref.dart';
|
|||||||
import 'package:PiliPlus/utils/theme_utils.dart';
|
import 'package:PiliPlus/utils/theme_utils.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:catcher_2/catcher_2.dart';
|
import 'package:catcher_2/catcher_2.dart';
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -82,6 +84,10 @@ Future<void> _initAppPath() async {
|
|||||||
appSupportDirPath = (await getApplicationSupportDirectory()).path;
|
appSupportDirPath = (await getApplicationSupportDirectory()).path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _initSdkInt() async {
|
||||||
|
Utils.sdkInt = (await DeviceInfoPlugin().androidInfo).version.sdkInt;
|
||||||
|
}
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
ScaledWidgetsFlutterBinding.ensureInitialized();
|
ScaledWidgetsFlutterBinding.ensureInitialized();
|
||||||
MediaKit.ensureInitialized();
|
MediaKit.ensureInitialized();
|
||||||
@@ -104,15 +110,8 @@ void main() async {
|
|||||||
|
|
||||||
if (PlatformUtils.isMobile) {
|
if (PlatformUtils.isMobile) {
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
SystemChrome.setPreferredOrientations(
|
if (Platform.isAndroid) _initSdkInt(),
|
||||||
[
|
if (Pref.horizontalScreen) ?fullMode() else ?portraitUpMode(),
|
||||||
DeviceOrientation.portraitUp,
|
|
||||||
if (Pref.horizontalScreen) ...[
|
|
||||||
DeviceOrientation.landscapeLeft,
|
|
||||||
DeviceOrientation.landscapeRight,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
setupServiceLocator(),
|
setupServiceLocator(),
|
||||||
]);
|
]);
|
||||||
} else if (Platform.isWindows) {
|
} else if (Platform.isWindows) {
|
||||||
@@ -123,18 +122,18 @@ void main() async {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (Platform.isMacOS) {
|
||||||
|
await setupServiceLocator();
|
||||||
}
|
}
|
||||||
|
|
||||||
Request();
|
Request();
|
||||||
Request.setCookie();
|
Request.setCookie();
|
||||||
RequestUtils.syncHistoryStatus();
|
RequestUtils.syncHistoryStatus();
|
||||||
|
|
||||||
SmartDialog.config.toast = SmartConfigToast(
|
SmartDialog.config.toast = SmartConfigToast(displayType: .onlyRefresh);
|
||||||
displayType: SmartToastType.onlyRefresh,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (PlatformUtils.isMobile) {
|
if (PlatformUtils.isMobile) {
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
SystemChrome.setEnabledSystemUIMode(.edgeToEdge);
|
||||||
SystemChrome.setSystemUIOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
const SystemUiOverlayStyle(
|
const SystemUiOverlayStyle(
|
||||||
systemNavigationBarColor: Colors.transparent,
|
systemNavigationBarColor: Colors.transparent,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ enum AccountType {
|
|||||||
main('主账号'),
|
main('主账号'),
|
||||||
heartbeat('记录观看'),
|
heartbeat('记录观看'),
|
||||||
recommend('推荐'),
|
recommend('推荐'),
|
||||||
video('视频取流')
|
video('视频取流'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ enum AudioNormalization {
|
|||||||
// ref https://github.com/KRTirtho/spotube/commit/da10ab2e291d4ba4d3082b9a6ae535639fb8f1b7
|
// ref https://github.com/KRTirtho/spotube/commit/da10ab2e291d4ba4d3082b9a6ae535639fb8f1b7
|
||||||
dynaudnorm('预设 dynaudnorm', 'dynaudnorm=g=5:f=250:r=0.9:p=0.5'),
|
dynaudnorm('预设 dynaudnorm', 'dynaudnorm=g=5:f=250:r=0.9:p=0.5'),
|
||||||
loudnorm('预设 loudnorm', 'loudnorm=I=-16:LRA=11:TP=-1.5'),
|
loudnorm('预设 loudnorm', 'loudnorm=I=-16:LRA=11:TP=-1.5'),
|
||||||
custom('自定义参数')
|
custom('自定义参数'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ enum BadgeType {
|
|||||||
none(),
|
none(),
|
||||||
vip('大会员'),
|
vip('大会员'),
|
||||||
person('认证个人', Color(0xFFFFCC00)),
|
person('认证个人', Color(0xFFFFCC00)),
|
||||||
institution('认证机构', Colors.lightBlueAccent)
|
institution('认证机构', Colors.lightBlueAccent),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String? desc;
|
final String? desc;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:PiliPlus/models/common/enum_with_label.dart';
|
|||||||
|
|
||||||
enum BarHideType with EnumWithLabel {
|
enum BarHideType with EnumWithLabel {
|
||||||
instant('即时'),
|
instant('即时'),
|
||||||
sync('同步')
|
sync('同步'),
|
||||||
;
|
;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
enum DmBlockType {
|
enum DmBlockType {
|
||||||
keyword('关键词'),
|
keyword('关键词'),
|
||||||
regex('正则'),
|
regex('正则'),
|
||||||
uid('用户')
|
uid('用户'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
enum DynamicBadgeMode {
|
enum DynamicBadgeMode {
|
||||||
hidden('隐藏'),
|
hidden('隐藏'),
|
||||||
point('红点'),
|
point('红点'),
|
||||||
number('数字')
|
number('数字'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String desc;
|
final String desc;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ enum DynamicsTabType {
|
|||||||
video('投稿'),
|
video('投稿'),
|
||||||
pgc('番剧'),
|
pgc('番剧'),
|
||||||
article('专栏'),
|
article('专栏'),
|
||||||
up('UP')
|
up('UP'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ enum UpPanelPosition {
|
|||||||
leftFixed('左侧常驻'),
|
leftFixed('左侧常驻'),
|
||||||
rightFixed('右侧常驻'),
|
rightFixed('右侧常驻'),
|
||||||
leftDrawer('左侧抽屉'),
|
leftDrawer('左侧抽屉'),
|
||||||
rightDrawer('右侧抽屉')
|
rightDrawer('右侧抽屉'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
enum EpisodeType {
|
enum EpisodeType {
|
||||||
part('分P'),
|
part('分P'),
|
||||||
season('合集'),
|
season('合集'),
|
||||||
pgc('剧集')
|
pgc('剧集'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
enum FavOrderType {
|
enum FavOrderType {
|
||||||
mtime('最近收藏'),
|
mtime('最近收藏'),
|
||||||
view('最多播放'),
|
view('最多播放'),
|
||||||
pubtime('最近投稿')
|
pubtime('最近投稿'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ enum FavTabType {
|
|||||||
article('专栏', FavArticlePage()),
|
article('专栏', FavArticlePage()),
|
||||||
note('笔记', FavNotePage()),
|
note('笔记', FavNotePage()),
|
||||||
topic('话题', FavTopicPage()),
|
topic('话题', FavTopicPage()),
|
||||||
cheese('课堂', FavCheesePage())
|
cheese('课堂', FavCheesePage()),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
enum FollowOrderType {
|
enum FollowOrderType {
|
||||||
def('', '最近关注'),
|
def('', '最近关注'),
|
||||||
attention('attention', '最常访问')
|
attention('attention', '最常访问'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String type;
|
final String type;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ enum HomeTabType implements EnumWithLabel {
|
|||||||
hot('热门'),
|
hot('热门'),
|
||||||
rank('分区'),
|
rank('分区'),
|
||||||
bangumi('番剧'),
|
bangumi('番剧'),
|
||||||
cinema('影视')
|
cinema('影视'),
|
||||||
;
|
;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
enum LaterViewType {
|
enum LaterViewType {
|
||||||
all(0, '全部'),
|
all(0, '全部'),
|
||||||
// toView(1, '未看'),
|
// toView(1, '未看'),
|
||||||
unfinished(2, '未看完')
|
unfinished(2, '未看完'),
|
||||||
// viewed(3, '已看完'),
|
// viewed(3, '已看完'),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ enum LiveContributionRankType {
|
|||||||
online_rank('在线榜', 'contribution_rank'),
|
online_rank('在线榜', 'contribution_rank'),
|
||||||
daily_rank('日榜', 'today_rank'),
|
daily_rank('日榜', 'today_rank'),
|
||||||
weekly_rank('周榜', 'current_week_rank'),
|
weekly_rank('周榜', 'current_week_rank'),
|
||||||
monthly_rank('月榜', 'current_month_rank')
|
monthly_rank('月榜', 'current_month_rank'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
11
lib/models/common/member/archive_order_type_app.dart
Normal file
11
lib/models/common/member/archive_order_type_app.dart
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import 'package:PiliPlus/models/common/enum_with_label.dart';
|
||||||
|
|
||||||
|
enum ArchiveOrderTypeApp with EnumWithLabel {
|
||||||
|
pubdate('最新发布'),
|
||||||
|
click('最多播放'),
|
||||||
|
;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String label;
|
||||||
|
const ArchiveOrderTypeApp(this.label);
|
||||||
|
}
|
||||||
12
lib/models/common/member/archive_order_type_web.dart
Normal file
12
lib/models/common/member/archive_order_type_web.dart
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:PiliPlus/models/common/enum_with_label.dart';
|
||||||
|
|
||||||
|
enum ArchiveOrderTypeWeb with EnumWithLabel {
|
||||||
|
pubdate('最新发布'),
|
||||||
|
click('最多播放'),
|
||||||
|
stow('最多收藏'),
|
||||||
|
;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String label;
|
||||||
|
const ArchiveOrderTypeWeb(this.label);
|
||||||
|
}
|
||||||
11
lib/models/common/member/archive_sort_type_app.dart
Normal file
11
lib/models/common/member/archive_sort_type_app.dart
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import 'package:PiliPlus/models/common/enum_with_label.dart';
|
||||||
|
|
||||||
|
enum ArchiveSortTypeApp with EnumWithLabel {
|
||||||
|
desc('默认'),
|
||||||
|
asc('倒序'),
|
||||||
|
;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String label;
|
||||||
|
const ArchiveSortTypeApp(this.label);
|
||||||
|
}
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
|
import 'package:PiliPlus/http/api.dart';
|
||||||
|
|
||||||
enum ContributeType {
|
enum ContributeType {
|
||||||
video,
|
video(Api.spaceArchive),
|
||||||
charging,
|
charging(Api.spaceChargingArchive),
|
||||||
season,
|
season(Api.spaceSeason),
|
||||||
series,
|
series(Api.spaceSeries),
|
||||||
bangumi,
|
bangumi(Api.spaceBangumi),
|
||||||
comic,
|
comic(Api.spaceComic),
|
||||||
|
;
|
||||||
|
|
||||||
|
final String api;
|
||||||
|
const ContributeType(this.api);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ enum MemberTabType {
|
|||||||
favorite('收藏'),
|
favorite('收藏'),
|
||||||
bangumi('番剧'),
|
bangumi('番剧'),
|
||||||
cheese('课堂'),
|
cheese('课堂'),
|
||||||
shop('小店')
|
shop('小店'),
|
||||||
;
|
;
|
||||||
|
|
||||||
static bool showMemberShop = Pref.showMemberShop;
|
static bool showMemberShop = Pref.showMemberShop;
|
||||||
|
|||||||
10
lib/models/common/member/web_ss_type.dart
Normal file
10
lib/models/common/member/web_ss_type.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'package:PiliPlus/http/api.dart';
|
||||||
|
|
||||||
|
enum WebSsType {
|
||||||
|
season(Api.seasonArchives),
|
||||||
|
series(Api.seriesArchives),
|
||||||
|
;
|
||||||
|
|
||||||
|
final String api;
|
||||||
|
const WebSsType(this.api);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ enum MsgUnReadType {
|
|||||||
reply('回复我的'),
|
reply('回复我的'),
|
||||||
at('@我'),
|
at('@我'),
|
||||||
like('收到的赞'),
|
like('收到的赞'),
|
||||||
sysMsg('系统通知')
|
sysMsg('系统通知'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import 'package:flutter/material.dart';
|
|||||||
enum NavigationBarType implements EnumWithLabel {
|
enum NavigationBarType implements EnumWithLabel {
|
||||||
home(
|
home(
|
||||||
'首页',
|
'首页',
|
||||||
Icon(Icons.home_outlined, size: 23),
|
Icon(Icons.home_outlined, size: 24),
|
||||||
Icon(Icons.home, size: 21),
|
Icon(Icons.home, size: 24),
|
||||||
HomePage(),
|
HomePage(),
|
||||||
),
|
),
|
||||||
dynamics(
|
dynamics(
|
||||||
@@ -19,10 +19,10 @@ enum NavigationBarType implements EnumWithLabel {
|
|||||||
),
|
),
|
||||||
mine(
|
mine(
|
||||||
'我的',
|
'我的',
|
||||||
Icon(Icons.person_outline, size: 21),
|
Icon(Icons.person_outline, size: 24),
|
||||||
Icon(Icons.person, size: 21),
|
Icon(Icons.person, size: 24),
|
||||||
MinePage(),
|
MinePage(),
|
||||||
)
|
),
|
||||||
;
|
;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:PiliPlus/http/api.dart';
|
|||||||
|
|
||||||
enum PgcReviewType {
|
enum PgcReviewType {
|
||||||
long(label: '长评', api: Api.pgcReviewL),
|
long(label: '长评', api: Api.pgcReviewL),
|
||||||
short(label: '短评', api: Api.pgcReviewS)
|
short(label: '短评', api: Api.pgcReviewS),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
@@ -15,7 +15,7 @@ enum PgcReviewType {
|
|||||||
|
|
||||||
enum PgcReviewSortType {
|
enum PgcReviewSortType {
|
||||||
def('默认', 0),
|
def('默认', 0),
|
||||||
latest('最新', 1)
|
latest('最新', 1),
|
||||||
;
|
;
|
||||||
|
|
||||||
final int sort;
|
final int sort;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ enum RankType {
|
|||||||
documentary('记录', seasonType: 3),
|
documentary('记录', seasonType: 3),
|
||||||
movie('电影', seasonType: 2),
|
movie('电影', seasonType: 2),
|
||||||
tv('剧集', seasonType: 5),
|
tv('剧集', seasonType: 5),
|
||||||
variety('综艺', seasonType: 7)
|
variety('综艺', seasonType: 7),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart
|
|||||||
enum ReplyOptionType {
|
enum ReplyOptionType {
|
||||||
allow('允许评论'),
|
allow('允许评论'),
|
||||||
close('关闭评论'),
|
close('关闭评论'),
|
||||||
choose('精选评论')
|
choose('精选评论'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
enum ReplySortType {
|
enum ReplySortType {
|
||||||
time('最新评论', '最新'),
|
time('最新评论', '最新'),
|
||||||
hot('最热评论', '最热'),
|
hot('最热评论', '最热'),
|
||||||
select('精选评论', '精选')
|
select('精选评论', '精选'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ enum ArticleOrderType {
|
|||||||
pubdate('最新发布'),
|
pubdate('最新发布'),
|
||||||
click('最多点击'),
|
click('最多点击'),
|
||||||
attention('最多喜欢'),
|
attention('最多喜欢'),
|
||||||
scores('最多评论')
|
scores('最多评论'),
|
||||||
;
|
;
|
||||||
|
|
||||||
String get order => name;
|
String get order => name;
|
||||||
@@ -20,7 +20,7 @@ enum ArticleZoneType {
|
|||||||
interest('兴趣', 29),
|
interest('兴趣', 29),
|
||||||
novel('轻小说', 16),
|
novel('轻小说', 16),
|
||||||
tech('科技', 17),
|
tech('科技', 17),
|
||||||
note('笔记', 41)
|
note('笔记', 41),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ enum SearchType {
|
|||||||
// 用户:bili_user
|
// 用户:bili_user
|
||||||
bili_user('用户'),
|
bili_user('用户'),
|
||||||
// 专栏:article
|
// 专栏:article
|
||||||
article('专栏')
|
article('专栏'),
|
||||||
;
|
;
|
||||||
// 相簿:photo
|
// 相簿:photo
|
||||||
// photo
|
// photo
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ enum UserOrderType {
|
|||||||
fansDesc('粉丝数由高到低', 0, 'fans'),
|
fansDesc('粉丝数由高到低', 0, 'fans'),
|
||||||
fansAsc('粉丝数由低到高', 1, 'fans'),
|
fansAsc('粉丝数由低到高', 1, 'fans'),
|
||||||
levelDesc('Lv等级由高到低', 0, 'level'),
|
levelDesc('Lv等级由高到低', 0, 'level'),
|
||||||
levelAsc('Lv等级由低到高', 1, 'level')
|
levelAsc('Lv等级由低到高', 1, 'level'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
@@ -16,7 +16,7 @@ enum UserType {
|
|||||||
all('全部用户'),
|
all('全部用户'),
|
||||||
up('UP主'),
|
up('UP主'),
|
||||||
common('普通用户'),
|
common('普通用户'),
|
||||||
verified('认证用户')
|
verified('认证用户'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ enum VideoPubTimeType {
|
|||||||
all('不限'),
|
all('不限'),
|
||||||
day('最近一天'),
|
day('最近一天'),
|
||||||
week('最近一周'),
|
week('最近一周'),
|
||||||
halfYear('最近半年')
|
halfYear('最近半年'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
@@ -14,7 +14,7 @@ enum VideoDurationType {
|
|||||||
tenMins('0-10分钟'),
|
tenMins('0-10分钟'),
|
||||||
halfHour('10-30分钟'),
|
halfHour('10-30分钟'),
|
||||||
hour('30-60分钟'),
|
hour('30-60分钟'),
|
||||||
hourPlus('60分钟+')
|
hourPlus('60分钟+'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
@@ -43,7 +43,7 @@ enum VideoZoneType {
|
|||||||
cinephile('影视', tids: 181),
|
cinephile('影视', tids: 181),
|
||||||
documentary('记录', tids: 177),
|
documentary('记录', tids: 177),
|
||||||
movie('电影', tids: 23),
|
movie('电影', tids: 23),
|
||||||
tv('电视', tids: 11)
|
tv('电视', tids: 11),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
@@ -58,7 +58,7 @@ enum ArchiveFilterType {
|
|||||||
pubdate('新发布'),
|
pubdate('新发布'),
|
||||||
dm('弹幕多'),
|
dm('弹幕多'),
|
||||||
stow('收藏多'),
|
stow('收藏多'),
|
||||||
scores('评论多')
|
scores('评论多'),
|
||||||
;
|
;
|
||||||
// 专栏
|
// 专栏
|
||||||
// attention('最多喜欢'),
|
// attention('最多喜欢'),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ enum SettingType {
|
|||||||
styleSetting('外观设置'),
|
styleSetting('外观设置'),
|
||||||
extraSetting('其它设置'),
|
extraSetting('其它设置'),
|
||||||
webdavSetting('WebDAV 设置'),
|
webdavSetting('WebDAV 设置'),
|
||||||
about('关于')
|
about('关于'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ enum ActionType {
|
|||||||
skip('跳过'),
|
skip('跳过'),
|
||||||
mute('静音'),
|
mute('静音'),
|
||||||
full('整个视频'),
|
full('整个视频'),
|
||||||
poi('精彩时刻')
|
poi('精彩时刻'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ enum SkipType implements EnumWithLabel {
|
|||||||
skipOnce('跳过一次'),
|
skipOnce('跳过一次'),
|
||||||
skipManually('手动跳过'),
|
skipManually('手动跳过'),
|
||||||
showOnly('仅显示'),
|
showOnly('仅显示'),
|
||||||
disable('禁用')
|
disable('禁用'),
|
||||||
;
|
;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ enum StatType {
|
|||||||
reply(Icons.comment_outlined, '评论'),
|
reply(Icons.comment_outlined, '评论'),
|
||||||
follow(Icons.favorite_border, '关注'),
|
follow(Icons.favorite_border, '关注'),
|
||||||
play(Icons.play_circle_outlined, '播放'),
|
play(Icons.play_circle_outlined, '播放'),
|
||||||
listen(Icons.headset_outlined, '播放')
|
listen(Icons.headset_outlined, '播放'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final IconData iconData;
|
final IconData iconData;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:PiliPlus/models/common/enum_with_label.dart';
|
|||||||
enum SuperResolutionType with EnumWithLabel {
|
enum SuperResolutionType with EnumWithLabel {
|
||||||
disable('禁用'),
|
disable('禁用'),
|
||||||
efficiency('效率'),
|
efficiency('效率'),
|
||||||
quality('画质')
|
quality('画质'),
|
||||||
;
|
;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart
|
|||||||
enum ThemeType {
|
enum ThemeType {
|
||||||
light('浅色'),
|
light('浅色'),
|
||||||
dark('深色'),
|
dark('深色'),
|
||||||
system('跟随系统')
|
system('跟随系统'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String desc;
|
final String desc;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ enum AudioQuality {
|
|||||||
dolby_30255(30255, '杜比全景声'),
|
dolby_30255(30255, '杜比全景声'),
|
||||||
k192(30280, '192K'),
|
k192(30280, '192K'),
|
||||||
k132(30232, '132K'),
|
k132(30232, '132K'),
|
||||||
k64(30216, '64K')
|
k64(30216, '64K'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final int code;
|
final int code;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ enum CDNService {
|
|||||||
aliov('aliov(阿里云海外)', 'upos-sz-mirroraliov.bilivideo.com'),
|
aliov('aliov(阿里云海外)', 'upos-sz-mirroraliov.bilivideo.com'),
|
||||||
cosov('cosov(腾讯云海外)', 'upos-sz-mirrorcosov.bilivideo.com'),
|
cosov('cosov(腾讯云海外)', 'upos-sz-mirrorcosov.bilivideo.com'),
|
||||||
hwov('hwov(华为云海外)', 'upos-sz-mirrorhwov.bilivideo.com'),
|
hwov('hwov(华为云海外)', 'upos-sz-mirrorhwov.bilivideo.com'),
|
||||||
hk_bcache('hk_bcache(Bilibili海外)', 'cn-hk-eq-bcache-01.bilivideo.com')
|
hk_bcache('hk_bcache(Bilibili海外)', 'cn-hk-eq-bcache-01.bilivideo.com'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String desc;
|
final String desc;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ enum LiveQuality {
|
|||||||
bluRay(400, '蓝光'),
|
bluRay(400, '蓝光'),
|
||||||
superHD(250, '超清'),
|
superHD(250, '超清'),
|
||||||
smooth(150, '高清'),
|
smooth(150, '高清'),
|
||||||
flunt(80, '流畅')
|
flunt(80, '流畅'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final int code;
|
final int code;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ enum SourceType {
|
|||||||
extraId: 4,
|
extraId: 4,
|
||||||
playlistSource: PlaylistSource.MEDIA_LIST,
|
playlistSource: PlaylistSource.MEDIA_LIST,
|
||||||
),
|
),
|
||||||
file
|
file,
|
||||||
;
|
;
|
||||||
|
|
||||||
final int? mediaType;
|
final int? mediaType;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ enum SubtitlePrefType {
|
|||||||
off('默认不显示字幕'),
|
off('默认不显示字幕'),
|
||||||
on('优先选择非自动生成(ai)字幕'),
|
on('优先选择非自动生成(ai)字幕'),
|
||||||
withoutAi('跳过自动生成(ai)字幕,选择第一个可用字幕'),
|
withoutAi('跳过自动生成(ai)字幕,选择第一个可用字幕'),
|
||||||
auto('静音时等同第二项,非静音时等同第三项')
|
auto('静音时等同第二项,非静音时等同第三项'),
|
||||||
;
|
;
|
||||||
|
|
||||||
final String desc;
|
final String desc;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user