upgraded Flutter to stable v3.22.0

This commit is contained in:
Thibault Deckers 2024-05-18 22:43:29 +02:00
parent 9a4657379b
commit 56762eea9c
82 changed files with 361 additions and 410 deletions

@ -1 +1 @@
Subproject commit 54e66469a933b60ddf175f858f82eaeb97e48c8d Subproject commit 5dcb86f68f239346676ceb1ed1ea385bd215fba1

View file

@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
## <a id="unreleased"></a>[Unreleased] ## <a id="unreleased"></a>[Unreleased]
### Changed
- upgraded Flutter to stable v3.22.0
### Removed
- support for Android KitKat (API 19)
## <a id="v1.11.1"></a>[v1.11.1] - 2024-05-03 ## <a id="v1.11.1"></a>[v1.11.1] - 2024-05-03
### Added ### Added

View file

@ -66,9 +66,6 @@ android {
defaultConfig { defaultConfig {
applicationId packageName applicationId packageName
// minSdk constraints:
// - Flutter & other plugins: 19 (cf `flutter.minSdkVersion`)
// - google_maps_flutter v2.1.1: 20
minSdk flutter.minSdkVersion minSdk flutter.minSdkVersion
targetSdk 34 targetSdk 34
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
@ -201,7 +198,7 @@ dependencies {
implementation "androidx.appcompat:appcompat:1.6.1" implementation "androidx.appcompat:appcompat:1.6.1"
implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.lifecycle:lifecycle-process:2.7.0' implementation 'androidx.lifecycle:lifecycle-process:2.8.0'
implementation 'androidx.media:media:1.7.0' implementation 'androidx.media:media:1.7.0'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.security:security-crypto:1.1.0-alpha06' implementation 'androidx.security:security-crypto:1.1.0-alpha06'
@ -229,7 +226,7 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2'
kapt 'androidx.annotation:annotation:1.7.1' kapt 'androidx.annotation:annotation:1.8.0'
ksp "com.github.bumptech.glide:ksp:$glide_version" ksp "com.github.bumptech.glide:ksp:$glide_version"
compileOnly rootProject.findProject(':streams_channel') compileOnly rootProject.findProject(':streams_channel')

View file

@ -72,12 +72,10 @@
--> -->
<!-- <!--
allow install on API 19, despite the `minSdk` declared in dependencies: allow install on API 21, despite the `minSdk` declared in dependencies:
- the Security library is from API 21
- FFmpegKit for Flutter is from API 24 (when not LTS) - FFmpegKit for Flutter is from API 24 (when not LTS)
- Google Maps is from API 20
--> -->
<uses-sdk tools:overrideLibrary="androidx.security, com.arthenica.ffmpegkit.flutter, io.flutter.plugins.googlemaps" /> <uses-sdk tools:overrideLibrary="com.arthenica.ffmpegkit.flutter" />
<!-- from Android 11, we should define <queries> to make other apps visible to this app --> <!-- from Android 11, we should define <queries> to make other apps visible to this app -->
<queries> <queries>
@ -321,8 +319,8 @@
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
<!-- as of Flutter v3.19.0 (stable), <!-- as of Flutter v3.22.0 (stable),
Impeller fails to render videos & platform views, has poor performance --> Impeller fails to render videos (via `ffmpegkit`), and has random glitches -->
<meta-data <meta-data
android:name="io.flutter.embedding.android.EnableImpeller" android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false" /> android:value="false" />

View file

@ -161,13 +161,12 @@ class AnalysisWorker(context: Context, parameters: WorkerParameters) : Coroutine
applicationContext.getString(R.string.analysis_notification_action_stop), applicationContext.getString(R.string.analysis_notification_action_stop),
WorkManager.getInstance(applicationContext).createCancelPendingIntent(id) WorkManager.getInstance(applicationContext).createCancelPendingIntent(id)
).build() ).build()
val icon = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) R.drawable.ic_notification else R.mipmap.ic_launcher_round
val contentTitle = title ?: applicationContext.getText(R.string.analysis_notification_default_title) val contentTitle = title ?: applicationContext.getText(R.string.analysis_notification_default_title)
val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL) val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL)
.setContentTitle(contentTitle) .setContentTitle(contentTitle)
.setTicker(contentTitle) .setTicker(contentTitle)
.setContentText(message) .setContentText(message)
.setSmallIcon(icon) .setSmallIcon(R.drawable.ic_notification)
.setOngoing(true) .setOngoing(true)
.setContentIntent(openAppIntent) .setContentIntent(openAppIntent)
.addAction(stopAction) .addAction(stopAction)

View file

@ -4,8 +4,6 @@ import android.appwidget.AppWidgetManager
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import deckers.thibault.aves.model.FieldMap import deckers.thibault.aves.model.FieldMap
import deckers.thibault.aves.utils.FlutterUtils
import deckers.thibault.aves.utils.FlutterUtils.enableSoftwareRendering
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
@ -13,9 +11,6 @@ class HomeWidgetSettingsActivity : MainActivity() {
private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
if (FlutterUtils.isSoftwareRenderingRequired()) {
intent.enableSoftwareRendering()
}
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// cancel if user does not complete widget setup // cancel if user does not complete widget setup

View file

@ -17,13 +17,37 @@ import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import app.loup.streams_channel.StreamsChannel import app.loup.streams_channel.StreamsChannel
import deckers.thibault.aves.channel.AvesByteSendingMethodCodec import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
import deckers.thibault.aves.channel.calls.* import deckers.thibault.aves.channel.calls.AccessibilityHandler
import deckers.thibault.aves.channel.calls.AnalysisHandler
import deckers.thibault.aves.channel.calls.AppAdapterHandler
import deckers.thibault.aves.channel.calls.DebugHandler
import deckers.thibault.aves.channel.calls.DeviceHandler
import deckers.thibault.aves.channel.calls.EmbeddedDataHandler
import deckers.thibault.aves.channel.calls.GeocodingHandler
import deckers.thibault.aves.channel.calls.GlobalSearchHandler
import deckers.thibault.aves.channel.calls.HomeWidgetHandler
import deckers.thibault.aves.channel.calls.MediaEditHandler
import deckers.thibault.aves.channel.calls.MediaFetchBytesHandler
import deckers.thibault.aves.channel.calls.MediaFetchObjectHandler
import deckers.thibault.aves.channel.calls.MediaSessionHandler
import deckers.thibault.aves.channel.calls.MediaStoreHandler
import deckers.thibault.aves.channel.calls.MetadataEditHandler
import deckers.thibault.aves.channel.calls.MetadataFetchHandler
import deckers.thibault.aves.channel.calls.SecurityHandler
import deckers.thibault.aves.channel.calls.StorageHandler
import deckers.thibault.aves.channel.calls.window.ActivityWindowHandler import deckers.thibault.aves.channel.calls.window.ActivityWindowHandler
import deckers.thibault.aves.channel.calls.window.WindowHandler import deckers.thibault.aves.channel.calls.window.WindowHandler
import deckers.thibault.aves.channel.streams.* import deckers.thibault.aves.channel.streams.ActivityResultStreamHandler
import deckers.thibault.aves.channel.streams.AnalysisStreamHandler
import deckers.thibault.aves.channel.streams.ErrorStreamHandler
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
import deckers.thibault.aves.channel.streams.ImageOpStreamHandler
import deckers.thibault.aves.channel.streams.IntentStreamHandler
import deckers.thibault.aves.channel.streams.MediaCommandStreamHandler
import deckers.thibault.aves.channel.streams.MediaStoreChangeStreamHandler
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
import deckers.thibault.aves.channel.streams.SettingsChangeStreamHandler
import deckers.thibault.aves.model.FieldMap import deckers.thibault.aves.model.FieldMap
import deckers.thibault.aves.utils.FlutterUtils.enableSoftwareRendering
import deckers.thibault.aves.utils.FlutterUtils.isSoftwareRenderingRequired
import deckers.thibault.aves.utils.LogUtils import deckers.thibault.aves.utils.LogUtils
import deckers.thibault.aves.utils.getParcelableExtraCompat import deckers.thibault.aves.utils.getParcelableExtraCompat
import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.android.FlutterFragmentActivity
@ -52,13 +76,6 @@ open class MainActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.i(LOG_TAG, "onCreate intent=$intent") Log.i(LOG_TAG, "onCreate intent=$intent")
if (isSoftwareRenderingRequired()) {
intent.enableSoftwareRendering()
// running the app from Android Studio automatically adds to the intent the `start-paused` flag
// so the IDE can connect to the app, but launching on KitKat emulators fails because of a timeout
intent.removeExtra("start-paused")
}
intent.extras?.takeUnless { it.isEmpty }?.let { intent.extras?.takeUnless { it.isEmpty }?.let {
Log.i(LOG_TAG, "onCreate intent extras=$it") Log.i(LOG_TAG, "onCreate intent extras=$it")
} }
@ -168,11 +185,9 @@ open class MainActivity : FlutterFragmentActivity() {
// as of Flutter v3.0.1, the window `viewInsets` and `viewPadding` // as of Flutter v3.0.1, the window `viewInsets` and `viewPadding`
// are incorrect on startup in some environments (e.g. API 29 emulator), // are incorrect on startup in some environments (e.g. API 29 emulator),
// so we manually request to apply the insets to update the window metrics // so we manually request to apply the insets to update the window metrics
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { Handler(Looper.getMainLooper()).postDelayed({
Handler(Looper.getMainLooper()).postDelayed({ window.decorView.requestApplyInsets()
window.decorView.requestApplyInsets() }, 100)
}, 100)
}
} }
override fun onStop() { override fun onStop() {

View file

@ -34,7 +34,7 @@ class SearchSuggestionsProvider : ContentProvider() {
val columns = arrayOf( val columns = arrayOf(
SearchManager.SUGGEST_COLUMN_INTENT_DATA, SearchManager.SUGGEST_COLUMN_INTENT_DATA,
SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) SearchManager.SUGGEST_COLUMN_CONTENT_TYPE else "mimeType", SearchManager.SUGGEST_COLUMN_CONTENT_TYPE,
SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2, SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_ICON_1, SearchManager.SUGGEST_COLUMN_ICON_1,

View file

@ -2,21 +2,26 @@ package deckers.thibault.aves
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import app.loup.streams_channel.StreamsChannel import app.loup.streams_channel.StreamsChannel
import deckers.thibault.aves.channel.AvesByteSendingMethodCodec import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
import deckers.thibault.aves.channel.calls.* import deckers.thibault.aves.channel.calls.AccessibilityHandler
import deckers.thibault.aves.channel.calls.DeviceHandler
import deckers.thibault.aves.channel.calls.EmbeddedDataHandler
import deckers.thibault.aves.channel.calls.MediaFetchBytesHandler
import deckers.thibault.aves.channel.calls.MediaFetchObjectHandler
import deckers.thibault.aves.channel.calls.MediaSessionHandler
import deckers.thibault.aves.channel.calls.MetadataFetchHandler
import deckers.thibault.aves.channel.calls.StorageHandler
import deckers.thibault.aves.channel.calls.WallpaperHandler
import deckers.thibault.aves.channel.calls.window.ActivityWindowHandler import deckers.thibault.aves.channel.calls.window.ActivityWindowHandler
import deckers.thibault.aves.channel.calls.window.WindowHandler import deckers.thibault.aves.channel.calls.window.WindowHandler
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
import deckers.thibault.aves.channel.streams.MediaCommandStreamHandler import deckers.thibault.aves.channel.streams.MediaCommandStreamHandler
import deckers.thibault.aves.model.FieldMap import deckers.thibault.aves.model.FieldMap
import deckers.thibault.aves.utils.FlutterUtils
import deckers.thibault.aves.utils.FlutterUtils.enableSoftwareRendering
import deckers.thibault.aves.utils.LogUtils import deckers.thibault.aves.utils.LogUtils
import deckers.thibault.aves.utils.getParcelableExtraCompat import deckers.thibault.aves.utils.getParcelableExtraCompat
import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.android.FlutterFragmentActivity
@ -30,9 +35,6 @@ class WallpaperActivity : FlutterFragmentActivity() {
private lateinit var mediaSessionHandler: MediaSessionHandler private lateinit var mediaSessionHandler: MediaSessionHandler
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
if (FlutterUtils.isSoftwareRenderingRequired()) {
intent.enableSoftwareRendering()
}
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Log.i(LOG_TAG, "onCreate intent=$intent") Log.i(LOG_TAG, "onCreate intent=$intent")
@ -83,11 +85,9 @@ class WallpaperActivity : FlutterFragmentActivity() {
// as of Flutter v3.0.1, the window `viewInsets` and `viewPadding` // as of Flutter v3.0.1, the window `viewInsets` and `viewPadding`
// are incorrect on startup in some environments (e.g. API 29 emulator), // are incorrect on startup in some environments (e.g. API 29 emulator),
// so we manually request to apply the insets to update the window metrics // so we manually request to apply the insets to update the window metrics
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { Handler(Looper.getMainLooper()).postDelayed({
Handler(Looper.getMainLooper()).postDelayed({ window.decorView.requestApplyInsets()
window.decorView.requestApplyInsets() }, 100)
}, 100)
}
} }
override fun onDestroy() { override fun onDestroy() {

View file

@ -1,7 +1,7 @@
package deckers.thibault.aves.channel.calls package deckers.thibault.aves.channel.calls
import android.content.Context import android.content.Context
import androidx.core.app.ComponentActivity import androidx.activity.ComponentActivity
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder

View file

@ -79,15 +79,9 @@ class DebugHandler(private val context: Context) : MethodCallHandler {
"obbDir" to context.obbDir, "obbDir" to context.obbDir,
"externalCacheDir" to context.externalCacheDir, "externalCacheDir" to context.externalCacheDir,
"externalFilesDir" to context.getExternalFilesDir(null), "externalFilesDir" to context.getExternalFilesDir(null),
"codeCacheDir" to context.codeCacheDir,
"noBackupFilesDir" to context.noBackupFilesDir,
).apply { ).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
putAll(
hashMapOf(
"codeCacheDir" to context.codeCacheDir,
"noBackupFilesDir" to context.noBackupFilesDir,
)
)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
put("dataDir", context.dataDir) put("dataDir", context.dataDir)
} }
@ -108,8 +102,6 @@ class DebugHandler(private val context: Context) : MethodCallHandler {
} }
private fun getCodecs(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { private fun getCodecs(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
val codecs = ArrayList<FieldMap>()
fun getFields(info: MediaCodecInfo): FieldMap { fun getFields(info: MediaCodecInfo): FieldMap {
val fields: FieldMap = hashMapOf( val fields: FieldMap = hashMapOf(
"name" to info.name, "name" to info.name,
@ -126,18 +118,7 @@ class DebugHandler(private val context: Context) : MethodCallHandler {
return fields return fields
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val codecs = MediaCodecList(MediaCodecList.REGULAR_CODECS).codecInfos.map(::getFields).toList()
codecs.addAll(MediaCodecList(MediaCodecList.REGULAR_CODECS).codecInfos.map(::getFields))
} else {
@Suppress("deprecation")
val count = MediaCodecList.getCodecCount()
for (i in 0 until count) {
@Suppress("deprecation")
val info = MediaCodecList.getCodecInfoAt(i)
codecs.add(getFields(info))
}
}
result.success(codecs) result.success(codecs)
} }

View file

@ -69,16 +69,11 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
} }
private fun getLocales(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { private fun getLocales(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
fun toMap(locale: Locale): FieldMap { fun toMap(locale: Locale): FieldMap = hashMapOf(
val fields: HashMap<String, Any?> = hashMapOf( "language" to locale.language,
"language" to locale.language, "country" to locale.country,
"country" to locale.country, "script" to locale.script,
) )
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
fields["script"] = locale.script
}
return fields
}
val locales = ArrayList<FieldMap>() val locales = ArrayList<FieldMap>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@ -106,11 +101,7 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
} }
private fun isSystemFilePickerEnabled(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { private fun isSystemFilePickerEnabled(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
val enabled = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val enabled = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).resolveActivity(context.packageManager) != null
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).resolveActivity(context.packageManager) != null
} else {
false
}
result.success(enabled) result.success(enabled)
} }

View file

@ -47,9 +47,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
private fun getDataUsage(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { private fun getDataUsage(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
var internalCache = getFolderSize(context.cacheDir) var internalCache = getFolderSize(context.cacheDir)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { internalCache += getFolderSize(context.codeCacheDir)
internalCache += getFolderSize(context.codeCacheDir)
}
val externalCache = context.externalCacheDirs.map(::getFolderSize).sum() val externalCache = context.externalCacheDirs.map(::getFolderSize).sum()
val dataDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) context.dataDir else File(context.applicationInfo.dataDir) val dataDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) context.dataDir else File(context.applicationInfo.dataDir)
@ -105,12 +103,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
val volumeFile = File(volumePath) val volumeFile = File(volumePath)
try { try {
val isPrimary = volumePath == primaryVolumePath val isPrimary = volumePath == primaryVolumePath
val isRemovable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val isRemovable = Environment.isExternalStorageRemovable(volumeFile)
Environment.isExternalStorageRemovable(volumeFile)
} else {
// random guess
!isPrimary
}
volumes.add( volumes.add(
hashMapOf( hashMapOf(
"path" to volumePath, "path" to volumePath,
@ -202,11 +195,6 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
return return
} }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
result.error("revokeDirectoryAccess-unsupported", "volume access is not allowed before Android Lollipop", null)
return
}
val success = PermissionManager.revokeDirectoryAccess(context, path) val success = PermissionManager.revokeDirectoryAccess(context, path)
result.success(success) result.success(success)
} }

View file

@ -61,11 +61,6 @@ class ActivityResultStreamHandler(private val activity: Activity, arguments: Any
return return
} }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
error("requestDirectoryAccess-unsupported", "directory access is not allowed before Android Lollipop", null)
return
}
PermissionManager.requestDirectoryAccess(activity, ensureTrailingSeparator(path), { PermissionManager.requestDirectoryAccess(activity, ensureTrailingSeparator(path), {
success(true) success(true)
endOfStream() endOfStream()

View file

@ -4,14 +4,18 @@ import android.util.Log
import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface
import com.drew.lang.Rational import com.drew.lang.Rational
import com.drew.metadata.Directory import com.drew.metadata.Directory
import com.drew.metadata.exif.* import com.drew.metadata.exif.ExifDirectoryBase
import com.drew.metadata.exif.ExifIFD0Directory
import com.drew.metadata.exif.ExifThumbnailDirectory
import com.drew.metadata.exif.GpsDirectory
import com.drew.metadata.exif.PanasonicRawIFD0Directory
import com.drew.metadata.exif.makernotes.OlympusCameraSettingsMakernoteDirectory import com.drew.metadata.exif.makernotes.OlympusCameraSettingsMakernoteDirectory
import com.drew.metadata.exif.makernotes.OlympusImageProcessingMakernoteDirectory import com.drew.metadata.exif.makernotes.OlympusImageProcessingMakernoteDirectory
import com.drew.metadata.exif.makernotes.OlympusMakernoteDirectory import com.drew.metadata.exif.makernotes.OlympusMakernoteDirectory
import deckers.thibault.aves.utils.LogUtils import deckers.thibault.aves.utils.LogUtils
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.Locale
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.floor import kotlin.math.floor
import kotlin.math.roundToLong import kotlin.math.roundToLong
@ -22,7 +26,7 @@ object ExifInterfaceHelper {
val GPS_DATE_FORMAT = SimpleDateFormat("yyyy:MM:dd", Locale.ROOT) val GPS_DATE_FORMAT = SimpleDateFormat("yyyy:MM:dd", Locale.ROOT)
val GPS_TIME_FORMAT = SimpleDateFormat("HH:mm:ss", Locale.ROOT) val GPS_TIME_FORMAT = SimpleDateFormat("HH:mm:ss", Locale.ROOT)
private const val precisionErrorTolerance = 1e-10 private const val PRECISION_ERROR_TOLERANCE = 1e-10
// ExifInterface always states it has the following attributes // ExifInterface always states it has the following attributes
// and returns "0" instead of "null" when they are actually missing // and returns "0" instead of "null" when they are actually missing
@ -220,7 +224,7 @@ object ExifInterfaceHelper {
// initialize metadata-extractor directories that we will fill // initialize metadata-extractor directories that we will fill
// by tags converted from the ExifInterface attributes // by tags converted from the ExifInterface attributes
// so that we can rely on metadata-extractor descriptions // so that we can rely on metadata-extractor descriptions
val dirs = DirType.values().associateWith { it.createDirectory() } val dirs = DirType.entries.associateWith { it.createDirectory() }
// exclude Exif directory when it only includes image size // exclude Exif directory when it only includes image size
val isUselessExif = fun(it: Map<String, String>): Boolean { val isUselessExif = fun(it: Map<String, String>): Boolean {
@ -308,7 +312,7 @@ object ExifInterfaceHelper {
val numerator = 1L val numerator = 1L
val f = numerator / d val f = numerator / d
val denominator = f.roundToLong() val denominator = f.roundToLong()
if (abs(f - denominator) < precisionErrorTolerance) { if (abs(f - denominator) < PRECISION_ERROR_TOLERANCE) {
return Rational(numerator, denominator) return Rational(numerator, denominator)
} }
} }

View file

@ -60,7 +60,7 @@ object Helper {
private val PNG_RAW_PROFILE_PATTERN = Regex("^\\n(.*?)\\n\\s*(\\d+)\\n(.*)", RegexOption.DOT_MATCHES_ALL) private val PNG_RAW_PROFILE_PATTERN = Regex("^\\n(.*?)\\n\\s*(\\d+)\\n(.*)", RegexOption.DOT_MATCHES_ALL)
// providing the stream length is risky, as it may crash if it is incorrect // providing the stream length is risky, as it may crash if it is incorrect
private const val safeReadStreamLength = -1L private const val SAFE_READ_STREAM_LENGTH = -1L
fun readMimeType(input: InputStream): String? { fun readMimeType(input: InputStream): String? {
val bufferedInputStream = if (input is BufferedInputStream) input else BufferedInputStream(input) val bufferedInputStream = if (input is BufferedInputStream) input else BufferedInputStream(input)
@ -84,7 +84,7 @@ object Helper {
FileType.Orf, FileType.Orf,
FileType.Rw2 -> safeReadTiff(inputStream) FileType.Rw2 -> safeReadTiff(inputStream)
else -> ImageMetadataReader.readMetadata(inputStream, safeReadStreamLength, fileType) else -> ImageMetadataReader.readMetadata(inputStream, SAFE_READ_STREAM_LENGTH, fileType)
} }
metadata.addDirectory(FileTypeDirectory(fileType)) metadata.addDirectory(FileTypeDirectory(fileType))
@ -116,7 +116,7 @@ object Helper {
@Throws(IOException::class, TiffProcessingException::class) @Throws(IOException::class, TiffProcessingException::class)
fun safeReadTiff(input: InputStream): com.drew.metadata.Metadata { fun safeReadTiff(input: InputStream): com.drew.metadata.Metadata {
val reader = RandomAccessStreamReader(input, RandomAccessStreamReader.DEFAULT_CHUNK_LENGTH, safeReadStreamLength) val reader = RandomAccessStreamReader(input, RandomAccessStreamReader.DEFAULT_CHUNK_LENGTH, SAFE_READ_STREAM_LENGTH)
val metadata = com.drew.metadata.Metadata() val metadata = com.drew.metadata.Metadata()
val handler = SafeExifTiffHandler(metadata, null, 0) val handler = SafeExifTiffHandler(metadata, null, 0)
TiffReader().processTiff(reader, handler, 0) TiffReader().processTiff(reader, handler, 0)
@ -294,9 +294,7 @@ object Helper {
if (!modelTiePoints && !modelTransformation) return false if (!modelTiePoints && !modelTransformation) return false
val modelPixelScale = this.containsTag(ExifGeoTiffTags.TAG_MODEL_PIXEL_SCALE) val modelPixelScale = this.containsTag(ExifGeoTiffTags.TAG_MODEL_PIXEL_SCALE)
if ((modelTransformation && modelPixelScale) || (modelPixelScale && !modelTiePoints)) return false return !((modelTransformation && modelPixelScale) || (modelPixelScale && !modelTiePoints))
return true
} }
// TODO TLAD use `GeoTiffDirectory` from the Java version of `metadata-extractor` when available // TODO TLAD use `GeoTiffDirectory` from the Java version of `metadata-extractor` when available

View file

@ -31,6 +31,7 @@ import deckers.thibault.aves.utils.LogUtils
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.Locale
import java.util.zip.InflaterInputStream import java.util.zip.InflaterInputStream
import java.util.zip.ZipException import java.util.zip.ZipException
@ -42,7 +43,7 @@ object SafePngMetadataReader {
private val LOG_TAG = LogUtils.createTag<SafePngMetadataReader>() private val LOG_TAG = LogUtils.createTag<SafePngMetadataReader>()
// arbitrary size to detect chunks that may yield an OOM // arbitrary size to detect chunks that may yield an OOM
private const val chunkSizeDangerThreshold = SafeXmpReader.SEGMENT_TYPE_SIZE_DANGER_THRESHOLD private const val CHUNK_SIZE_DANGER_THRESHOLD = SafeXmpReader.SEGMENT_TYPE_SIZE_DANGER_THRESHOLD
private val latin1Encoding = Charsets.ISO_8859_1 private val latin1Encoding = Charsets.ISO_8859_1
private val utf8Encoding = Charsets.UTF_8 private val utf8Encoding = Charsets.UTF_8
@ -85,7 +86,7 @@ object SafePngMetadataReader {
val bytes = chunk.bytes val bytes = chunk.bytes
// TLAD insert start // TLAD insert start
if (bytes.size > chunkSizeDangerThreshold) { if (bytes.size > CHUNK_SIZE_DANGER_THRESHOLD) {
Log.w(LOG_TAG, "PNG chunk $chunkType is too large, with a size of ${bytes.size} B") Log.w(LOG_TAG, "PNG chunk $chunkType is too large, with a size of ${bytes.size} B")
return return
} }
@ -290,11 +291,12 @@ object SafePngMetadataReader {
val second = reader.uInt8.toInt() val second = reader.uInt8.toInt()
val directory = PngDirectory(PngChunkType.tIME) val directory = PngDirectory(PngChunkType.tIME)
if (DateUtil.isValidDate(year, month - 1, day) && DateUtil.isValidTime(hour, minute, second)) { if (DateUtil.isValidDate(year, month - 1, day) && DateUtil.isValidTime(hour, minute, second)) {
val dateString = String.format("%04d:%02d:%02d %02d:%02d:%02d", year, month, day, hour, minute, second) val dateString = String.format(Locale.ROOT, "%04d:%02d:%02d %02d:%02d:%02d", year, month, day, hour, minute, second)
directory.setString(PngDirectory.TAG_LAST_MODIFICATION_TIME, dateString) directory.setString(PngDirectory.TAG_LAST_MODIFICATION_TIME, dateString)
} else { } else {
directory.addError( directory.addError(
String.format( String.format(
Locale.ROOT,
"PNG tIME data describes an invalid date/time: year=%d month=%d day=%d hour=%d minute=%d second=%d", "PNG tIME data describes an invalid date/time: year=%d month=%d day=%d hour=%d minute=%d second=%d",
year, month, day, hour, minute, second year, month, day, hour, minute, second
) )

View file

@ -16,6 +16,7 @@ import com.drew.metadata.xmp.XmpDirectory
import com.drew.metadata.xmp.XmpReader import com.drew.metadata.xmp.XmpReader
import deckers.thibault.aves.utils.LogUtils import deckers.thibault.aves.utils.LogUtils
import java.io.IOException import java.io.IOException
import java.util.Locale
class SafeXmpReader : XmpReader() { class SafeXmpReader : XmpReader() {
// adapted from `XmpReader` to detect and skip large extended XMP // adapted from `XmpReader` to detect and skip large extended XMP
@ -133,7 +134,7 @@ class SafeXmpReader : XmpReader() {
System.arraycopy(segmentBytes, totalOffset, extendedXMPBuffer, chunkOffset, segmentLength - totalOffset) System.arraycopy(segmentBytes, totalOffset, extendedXMPBuffer, chunkOffset, segmentLength - totalOffset)
} else { } else {
val directory = XmpDirectory() val directory = XmpDirectory()
directory.addError(String.format("Inconsistent length for the Extended XMP buffer: %d instead of %d", fullLength, extendedXMPBuffer.size)) directory.addError(String.format(Locale.ROOT, "Inconsistent length for the Extended XMP buffer: %d instead of %d", fullLength, extendedXMPBuffer.size))
metadata.addDirectory(directory) metadata.addDirectory(directory)
} }
} }

View file

@ -16,8 +16,6 @@ class MpfDirectory : Directory() {
return _tagNameMap return _tagNameMap
} }
fun getNumberOfImages() = getInt(TAG_NUMBER_OF_IMAGES)
companion object { companion object {
const val TAG_MPF_VERSION = 0xb000 const val TAG_MPF_VERSION = 0xb000
const val TAG_NUMBER_OF_IMAGES = 0xb001 const val TAG_NUMBER_OF_IMAGES = 0xb001

View file

@ -15,7 +15,7 @@ class AvesEntry(map: FieldMap) {
val trashed = map["trashed"] as Boolean val trashed = map["trashed"] as Boolean
val trashPath = map["trashPath"] as String? val trashPath = map["trashPath"] as String?
val isRotated: Boolean private val isRotated: Boolean
get() = rotationDegrees % 180 == 90 get() = rotationDegrees % 180 == 90
val displayWidth: Int val displayWidth: Int

View file

@ -1,8 +1,6 @@
package deckers.thibault.aves.utils package deckers.thibault.aves.utils
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
@ -71,29 +69,4 @@ object FlutterUtils {
r.run() r.run()
} }
} }
fun Intent.enableSoftwareRendering() {
putExtra("enable-software-rendering", true)
Log.i(LOG_TAG, "Enable software rendering")
}
fun isSoftwareRenderingRequired() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT && isEmulator
private val isEmulator: Boolean
get() = (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
|| Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.HARDWARE.contains("goldfish")
|| Build.HARDWARE.contains("ranchu")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.MANUFACTURER.contains("Genymotion")
|| Build.PRODUCT.contains("sdk_google")
|| Build.PRODUCT.contains("google_sdk")
|| Build.PRODUCT.contains("sdk")
|| Build.PRODUCT.contains("sdk_x86")
|| Build.PRODUCT.contains("vbox86p")
|| Build.PRODUCT.contains("emulator")
|| Build.PRODUCT.contains("simulator"))
} }

View file

@ -28,7 +28,6 @@ object PermissionManager {
Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_PICTURES,
) )
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
suspend fun requestDirectoryAccess(activity: Activity, path: String, onGranted: (uri: Uri) -> Unit, onDenied: () -> Unit) { suspend fun requestDirectoryAccess(activity: Activity, path: String, onGranted: (uri: Uri) -> Unit, onDenied: () -> Unit) {
Log.i(LOG_TAG, "request user to select and grant access permission to path=$path") Log.i(LOG_TAG, "request user to select and grant access permission to path=$path")
@ -151,7 +150,6 @@ object PermissionManager {
} }
} }
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun revokeDirectoryAccess(context: Context, path: String): Boolean { fun revokeDirectoryAccess(context: Context, path: String): Boolean {
return StorageUtils.convertDirPathToTreeDocumentUri(context, path)?.let { return StorageUtils.convertDirPathToTreeDocumentUri(context, path)?.let {
releaseUriPermission(context, it) releaseUriPermission(context, it)
@ -162,11 +160,9 @@ object PermissionManager {
// returns paths matching directory URIs granted by the user // returns paths matching directory URIs granted by the user
fun getGrantedDirs(context: Context): Set<String> { fun getGrantedDirs(context: Context): Set<String> {
val grantedDirs = HashSet<String>() val grantedDirs = HashSet<String>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { for (uriPermission in context.contentResolver.persistedUriPermissions) {
for (uriPermission in context.contentResolver.persistedUriPermissions) { val dirPath = StorageUtils.convertTreeDocumentUriToDirPath(context, uriPermission.uri)
val dirPath = StorageUtils.convertTreeDocumentUriToDirPath(context, uriPermission.uri) dirPath?.let { grantedDirs.add(it) }
dirPath?.let { grantedDirs.add(it) }
}
} }
return grantedDirs return grantedDirs
} }
@ -216,19 +212,6 @@ object PermissionManager {
) )
}) })
} }
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT_WATCH
) {
// removable storage requires access permission, at the file level
// without directory access, we consider the whole volume restricted
val primaryVolume = StorageUtils.getPrimaryVolumePath(context)
val nonPrimaryVolumes = StorageUtils.getVolumePaths(context).filter { it != primaryVolume }
dirs.addAll(nonPrimaryVolumes.map {
hashMapOf(
"volumePath" to it,
"relativeDir" to "",
)
})
} }
return dirs return dirs
} }
@ -236,7 +219,6 @@ object PermissionManager {
// As of Android 11, `MediaStore.getDocumentUri` fails if any of the persisted // As of Android 11, `MediaStore.getDocumentUri` fails if any of the persisted
// URI permissions we hold points to a folder that no longer exists, // URI permissions we hold points to a folder that no longer exists,
// so we should remove these obsolete URIs before proceeding. // so we should remove these obsolete URIs before proceeding.
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun sanitizePersistedUriPermissions(context: Context) { fun sanitizePersistedUriPermissions(context: Context) {
try { try {
for (uriPermission in context.contentResolver.persistedUriPermissions) { for (uriPermission in context.contentResolver.persistedUriPermissions) {
@ -252,7 +234,6 @@ object PermissionManager {
} }
} }
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun releaseUriPermission(context: Context, it: Uri) { private fun releaseUriPermission(context: Context, it: Uri) {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
context.contentResolver.releasePersistableUriPermission(it, flags) context.contentResolver.releasePersistableUriPermission(it, flags)

View file

@ -16,7 +16,6 @@ import android.provider.DocumentsContract
import android.provider.MediaStore import android.provider.MediaStore
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi
import com.commonsware.cwac.document.DocumentFileCompat import com.commonsware.cwac.document.DocumentFileCompat
import deckers.thibault.aves.model.provider.ImageProvider import deckers.thibault.aves.model.provider.ImageProvider
import deckers.thibault.aves.utils.FileUtils.transferFrom import deckers.thibault.aves.utils.FileUtils.transferFrom
@ -29,7 +28,7 @@ import java.io.FileInputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.util.* import java.util.Locale
import java.util.regex.Pattern import java.util.regex.Pattern
object StorageUtils { object StorageUtils {
@ -381,7 +380,6 @@ object StorageUtils {
// e.g. // e.g.
// /storage/emulated/0/ -> content://com.android.externalstorage.documents/tree/primary%3A // /storage/emulated/0/ -> content://com.android.externalstorage.documents/tree/primary%3A
// /storage/10F9-3F13/Pictures/ -> content://com.android.externalstorage.documents/tree/10F9-3F13%3APictures // /storage/10F9-3F13/Pictures/ -> content://com.android.externalstorage.documents/tree/10F9-3F13%3APictures
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun convertDirPathToTreeDocumentUri(context: Context, dirPath: String): Uri? { fun convertDirPathToTreeDocumentUri(context: Context, dirPath: String): Uri? {
val uuid = getVolumeUuidForDocumentUri(context, dirPath) val uuid = getVolumeUuidForDocumentUri(context, dirPath)
if (uuid != null) { if (uuid != null) {
@ -446,7 +444,7 @@ object StorageUtils {
fun getDocumentFile(context: Context, anyPath: String, mediaUri: Uri): DocumentFileCompat? { fun getDocumentFile(context: Context, anyPath: String, mediaUri: Uri): DocumentFileCompat? {
try { try {
if (requireAccessPermission(context, anyPath) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (requireAccessPermission(context, anyPath)) {
// need a document URI (not a media content URI) to open a `DocumentFile` output stream // need a document URI (not a media content URI) to open a `DocumentFile` output stream
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isMediaStoreContentUri(mediaUri)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isMediaStoreContentUri(mediaUri)) {
// cleanest API to get it // cleanest API to get it
@ -485,7 +483,7 @@ object StorageUtils {
fun createDirectoryDocIfAbsent(context: Context, dirPath: String): DocumentFileCompat? { fun createDirectoryDocIfAbsent(context: Context, dirPath: String): DocumentFileCompat? {
try { try {
val cleanDirPath = ensureTrailingSeparator(dirPath) val cleanDirPath = ensureTrailingSeparator(dirPath)
return if (requireAccessPermission(context, cleanDirPath) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return if (requireAccessPermission(context, cleanDirPath)) {
val grantedDir = getGrantedDirForPath(context, cleanDirPath) ?: return null val grantedDir = getGrantedDirForPath(context, cleanDirPath) ?: return null
val rootTreeDocumentUri = convertDirPathToTreeDocumentUri(context, grantedDir) ?: return null val rootTreeDocumentUri = convertDirPathToTreeDocumentUri(context, grantedDir) ?: return null
var parentFile: DocumentFileCompat? = DocumentFileCompat.fromTreeUri(context, rootTreeDocumentUri) ?: return null var parentFile: DocumentFileCompat? = DocumentFileCompat.fromTreeUri(context, rootTreeDocumentUri) ?: return null

View file

@ -26,5 +26,5 @@ android {
} }
dependencies { dependencies {
implementation 'androidx.annotation:annotation:1.7.1' implementation 'androidx.annotation:annotation:1.8.0'
} }

View file

@ -1,4 +1,3 @@
import 'package:aves/widgets/aves_app.dart'; import 'package:aves/widgets/aves_app.dart';
import 'package:aves_utils/aves_utils.dart'; import 'package:aves_utils/aves_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -26,16 +25,21 @@ class Themes {
} }
} }
static Color _schemeCardLayer(ColorScheme colors) => ElevationOverlay.applySurfaceTint(colors.surface, colors.surfaceTint, 1); static bool _isDarkTheme(ColorScheme colors) => colors.brightness == Brightness.dark && colors.surface != Colors.black;
static Color firstLayerColor(BuildContext context) => _schemeFirstLayer(Theme.of(context).colorScheme);
static Color _schemeFirstLayer(ColorScheme colors) => _isDarkTheme(colors) ? colors.surfaceContainer : colors.surface;
static Color _schemeCardLayer(ColorScheme colors) => _isDarkTheme(colors) ? _schemeSecondLayer(colors) : colors.surfaceContainerLow;
static Color secondLayerColor(BuildContext context) => _schemeSecondLayer(Theme.of(context).colorScheme); static Color secondLayerColor(BuildContext context) => _schemeSecondLayer(Theme.of(context).colorScheme);
// `DialogTheme` M3 defaults use `6.0` elevation static Color _schemeSecondLayer(ColorScheme colors) => _isDarkTheme(colors) ? colors.surfaceContainerHigh : colors.surfaceContainer;
static Color _schemeSecondLayer(ColorScheme colors) => ElevationOverlay.applySurfaceTint(colors.surface, colors.surfaceTint, 6);
static Color thirdLayerColor(BuildContext context) => _schemeThirdLayer(Theme.of(context).colorScheme); static Color thirdLayerColor(BuildContext context) => _schemeThirdLayer(Theme.of(context).colorScheme);
static Color _schemeThirdLayer(ColorScheme colors) => ElevationOverlay.applySurfaceTint(colors.surface, colors.surfaceTint, 12); static Color _schemeThirdLayer(ColorScheme colors) => _isDarkTheme(colors) ? colors.surfaceContainerHighest : colors.surfaceContainerHigh;
static Color _unselectedWidgetColor(ColorScheme colors) => colors.onSurface.withOpacity(0.6); static Color _unselectedWidgetColor(ColorScheme colors) => colors.onSurface.withOpacity(0.6);
@ -55,14 +59,15 @@ class Themes {
colorScheme: colors, colorScheme: colors,
dividerColor: colors.outlineVariant, dividerColor: colors.outlineVariant,
indicatorColor: colors.primary, indicatorColor: colors.primary,
scaffoldBackgroundColor: colors.background, scaffoldBackgroundColor: _schemeFirstLayer(colors),
// TYPOGRAPHY & ICONOGRAPHY // TYPOGRAPHY & ICONOGRAPHY
typography: _typography, typography: _typography,
// COMPONENT THEMES // COMPONENT THEMES
checkboxTheme: _checkboxTheme(colors), checkboxTheme: _checkboxTheme(colors),
drawerTheme: _drawerTheme(colors),
floatingActionButtonTheme: _floatingActionButtonTheme(colors), floatingActionButtonTheme: _floatingActionButtonTheme(colors),
navigationRailTheme: NavigationRailThemeData( navigationRailTheme: NavigationRailThemeData(
backgroundColor: colors.background, backgroundColor: _schemeFirstLayer(colors),
selectedIconTheme: IconThemeData(color: colors.primary), selectedIconTheme: IconThemeData(color: colors.primary),
unselectedIconTheme: IconThemeData(color: _unselectedWidgetColor(colors)), unselectedIconTheme: IconThemeData(color: _unselectedWidgetColor(colors)),
selectedLabelTextStyle: TextStyle(color: colors.primary), selectedLabelTextStyle: TextStyle(color: colors.primary),
@ -78,16 +83,21 @@ class Themes {
side: BorderSide(width: 2.0, color: _unselectedWidgetColor(colors)), side: BorderSide(width: 2.0, color: _unselectedWidgetColor(colors)),
); );
static DrawerThemeData _drawerTheme(ColorScheme colors) => DrawerThemeData(
backgroundColor: _schemeSecondLayer(colors),
);
static const _listTileTheme = ListTileThemeData( static const _listTileTheme = ListTileThemeData(
contentPadding: EdgeInsets.symmetric(horizontal: 16), contentPadding: EdgeInsets.symmetric(horizontal: 16),
); );
static PopupMenuThemeData _popupMenuTheme(ColorScheme colors, TextTheme textTheme) { static PopupMenuThemeData _popupMenuTheme(ColorScheme colors, TextTheme textTheme) {
return PopupMenuThemeData( return PopupMenuThemeData(
labelTextStyle: MaterialStateProperty.resolveWith((states) { color: _schemeSecondLayer(colors),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
// adapted from M3 defaults // adapted from M3 defaults
final TextStyle style = textTheme.labelLarge!; final TextStyle style = textTheme.labelLarge!;
if (states.contains(MaterialState.disabled)) { if (states.contains(WidgetState.disabled)) {
return style.apply(color: colors.onSurface.withOpacity(0.38)); return style.apply(color: colors.onSurface.withOpacity(0.38));
} }
return style.apply(color: colors.onSurface); return style.apply(color: colors.onSurface);
@ -105,23 +115,23 @@ class Themes {
// adapted from M3 defaults // adapted from M3 defaults
static RadioThemeData _radioTheme(ColorScheme colors) => RadioThemeData( static RadioThemeData _radioTheme(ColorScheme colors) => RadioThemeData(
fillColor: MaterialStateProperty.resolveWith<Color>((states) { fillColor: WidgetStateProperty.resolveWith<Color>((states) {
if (states.contains(MaterialState.selected)) { if (states.contains(WidgetState.selected)) {
if (states.contains(MaterialState.disabled)) { if (states.contains(WidgetState.disabled)) {
return colors.onSurface.withOpacity(0.38); return colors.onSurface.withOpacity(0.38);
} }
return colors.primary; return colors.primary;
} }
if (states.contains(MaterialState.disabled)) { if (states.contains(WidgetState.disabled)) {
return colors.onSurface.withOpacity(0.38); return colors.onSurface.withOpacity(0.38);
} }
if (states.contains(MaterialState.pressed)) { if (states.contains(WidgetState.pressed)) {
return colors.onSurface; return colors.onSurface;
} }
if (states.contains(MaterialState.hovered)) { if (states.contains(WidgetState.hovered)) {
return colors.onSurface; return colors.onSurface;
} }
if (states.contains(MaterialState.focused)) { if (states.contains(WidgetState.focused)) {
return colors.onSurface; return colors.onSurface;
} }
return _unselectedWidgetColor(colors); return _unselectedWidgetColor(colors);
@ -166,13 +176,15 @@ class Themes {
textTheme: textTheme, textTheme: textTheme,
// COMPONENT THEMES // COMPONENT THEMES
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
backgroundColor: _schemeFirstLayer(colors),
// `foregroundColor` is used by icons // `foregroundColor` is used by icons
foregroundColor: _lightActionIconColor, foregroundColor: _lightActionIconColor,
// `titleTextStyle.color` is used by text // `titleTextStyle.color` is used by text
titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor), titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor),
systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, colors.background) : null, systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, _schemeFirstLayer(colors)) : null,
), ),
dialogTheme: DialogTheme( dialogTheme: DialogTheme(
backgroundColor: _schemeSecondLayer(colors),
titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor), titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor),
), ),
listTileTheme: _listTileTheme.copyWith( listTileTheme: _listTileTheme.copyWith(
@ -217,13 +229,15 @@ class Themes {
textTheme: textTheme, textTheme: textTheme,
// COMPONENT THEMES // COMPONENT THEMES
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
backgroundColor: _schemeFirstLayer(colors),
// `foregroundColor` is used by icons // `foregroundColor` is used by icons
foregroundColor: _darkTitleColor, foregroundColor: _darkTitleColor,
// `titleTextStyle.color` is used by text // `titleTextStyle.color` is used by text
titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor), titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor),
systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, colors.background) : null, systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, _schemeFirstLayer(colors)) : null,
), ),
dialogTheme: DialogTheme( dialogTheme: DialogTheme(
backgroundColor: _schemeSecondLayer(colors),
titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor), titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor),
), ),
listTileTheme: _listTileTheme, listTileTheme: _listTileTheme,
@ -251,13 +265,8 @@ class Themes {
static ThemeData blackTheme(Color accentColor, bool deviceInitialized) { static ThemeData blackTheme(Color accentColor, bool deviceInitialized) {
final colors = _darkColorScheme(accentColor).copyWith( final colors = _darkColorScheme(accentColor).copyWith(
background: Colors.black, surface: Colors.black,
);
final baseTheme = _baseDarkTheme(colors, deviceInitialized);
return baseTheme.copyWith(
appBarTheme: baseTheme.appBarTheme.copyWith(
backgroundColor: colors.background,
),
); );
return _baseDarkTheme(colors, deviceInitialized);
} }
} }

View file

@ -1,4 +1,5 @@
import 'package:aves/model/device.dart'; import 'package:aves/model/device.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/about/app_ref.dart'; import 'package:aves/widgets/about/app_ref.dart';
import 'package:aves/widgets/about/credits.dart'; import 'package:aves/widgets/about/credits.dart';
import 'package:aves/widgets/about/translators.dart'; import 'package:aves/widgets/about/translators.dart';
@ -192,7 +193,7 @@ class _ContentState extends State<_Content> {
return Theme( return Theme(
data: theme.copyWith( data: theme.copyWith(
listTileTheme: listTileTheme.copyWith( listTileTheme: listTileTheme.copyWith(
tileColor: theme.colorScheme.background, tileColor: Themes.firstLayerColor(context),
), ),
), ),
child: const TvLicensePage(), child: const TvLicensePage(),

View file

@ -2,6 +2,7 @@ import 'package:aves/app_flavor.dart';
import 'package:aves/model/app/dependencies.dart'; import 'package:aves/model/app/dependencies.dart';
import 'package:aves/ref/brand_colors.dart'; import 'package:aves/ref/brand_colors.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/about/title.dart'; import 'package:aves/widgets/about/title.dart';
import 'package:aves/widgets/common/basic/link_chip.dart'; import 'package:aves/widgets/common/basic/link_chip.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
@ -87,8 +88,8 @@ class _LicensesState extends State<Licenses> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => Theme( builder: (context) => Theme(
data: Theme.of(context).copyWith( data: Theme.of(context).copyWith(
// as of Flutter v3.7.8, `cardColor` is used as a background color by `LicensePage` // as of Flutter v3.22.0, `cardColor` is used as a background color by `LicensePage`
cardColor: Theme.of(context).colorScheme.background, cardColor: Themes.firstLayerColor(context),
), ),
child: const LicensePage(), child: const LicensePage(),
), ),

View file

@ -31,7 +31,7 @@ class AboutTranslators extends StatelessWidget {
static Widget buildBody(BuildContext context) { static Widget buildBody(BuildContext context) {
return _RandomTextSpanHighlighter( return _RandomTextSpanHighlighter(
spans: Contributors.translators.map((v) => v.name).toList(), spans: Contributors.translators.map((v) => v.name).toList(),
color: Theme.of(context).colorScheme.onBackground, color: Theme.of(context).colorScheme.onSurface,
); );
} }
} }

View file

@ -1,5 +1,6 @@
import 'dart:developer' show Flow, Timeline; import 'dart:developer' show Flow, Timeline;
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/basic/scaffold.dart';
import 'package:aves/widgets/common/behaviour/intents.dart'; import 'package:aves/widgets/common/behaviour/intents.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -77,7 +78,7 @@ class _TvLicensePageState extends State<TvLicensePage> {
final isSelected = index == selectedIndex; final isSelected = index == selectedIndex;
final theme = Theme.of(context); final theme = Theme.of(context);
return Ink( return Ink(
color: isSelected ? theme.highlightColor : theme.colorScheme.background, color: isSelected ? theme.highlightColor : Themes.firstLayerColor(context),
child: ListTile( child: ListTile(
title: Text(packageName), title: Text(packageName),
subtitle: Text(MaterialLocalizations.of(context).licensesPackageDetailText(bindings.length)), subtitle: Text(MaterialLocalizations.of(context).licensesPackageDetailText(bindings.length)),
@ -298,7 +299,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> {
), ),
body: Center( body: Center(
child: Material( child: Material(
color: theme.colorScheme.background, color: Themes.firstLayerColor(context),
elevation: 4.0, elevation: 4.0,
child: Container( child: Container(
constraints: BoxConstraints.loose(const Size.fromWidth(600.0)), constraints: BoxConstraints.loose(const Size.fromWidth(600.0)),
@ -328,7 +329,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> {
SliverAppBar( SliverAppBar(
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
pinned: true, pinned: true,
backgroundColor: theme.colorScheme.background, backgroundColor: Themes.firstLayerColor(context),
title: _PackageLicensePageTitle( title: _PackageLicensePageTitle(
title: title, title: title,
subtitle: subtitle, subtitle: subtitle,

View file

@ -96,12 +96,12 @@ class AvesApp extends StatefulWidget {
State<AvesApp> createState() => _AvesAppState(); State<AvesApp> createState() => _AvesAppState();
static void setSystemUIStyle(ThemeData theme) { static void setSystemUIStyle(ThemeData theme) {
final style = systemUIStyleForBrightness(theme.brightness, theme.colorScheme.background); final style = systemUIStyleForBrightness(theme.brightness, theme.colorScheme.surfaceContainer);
SystemChrome.setSystemUIOverlayStyle(style); SystemChrome.setSystemUIOverlayStyle(style);
} }
static SystemUiOverlayStyle systemUIStyleForBrightness(Brightness themeBrightness, Color backgroundColor) { static SystemUiOverlayStyle systemUIStyleForBrightness(Brightness themeBrightness, Color backgroundColor) {
final barBrightness = themeBrightness == Brightness.light ? Brightness.dark : Brightness.light; final barBrightness = themeBrightness == Brightness.dark ? Brightness.light : Brightness.dark;
const statusBarColor = Colors.transparent; const statusBarColor = Colors.transparent;
// as of Flutter v3.3.0-0.2.pre, setting `SystemUiOverlayStyle` (whether manually or automatically because of `AppBar`) // as of Flutter v3.3.0-0.2.pre, setting `SystemUiOverlayStyle` (whether manually or automatically because of `AppBar`)
// prevents the canvas from drawing behind the nav bar on Android <10 (API <29), // prevents the canvas from drawing behind the nav bar on Android <10 (API <29),

View file

@ -87,7 +87,7 @@ mixin FeedbackMixin {
action: action != null action: action != null
? TextButton( ? TextButton(
style: ButtonStyle( style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(snackBarTheme.actionTextColor), foregroundColor: WidgetStateProperty.all(snackBarTheme.actionTextColor),
), ),
onPressed: () { onPressed: () {
notificationOverlayEntry?.dismiss(); notificationOverlayEntry?.dismiss();

View file

@ -132,12 +132,10 @@ class _OverlaySnackBarState extends State<OverlaySnackBar> {
primary: colorScheme.onPrimary, primary: colorScheme.onPrimary,
secondary: buttonColor, secondary: buttonColor,
surface: colorScheme.onSurface, surface: colorScheme.onSurface,
background: defaults.backgroundColor!,
error: colorScheme.onError, error: colorScheme.onError,
onPrimary: colorScheme.primary, onPrimary: colorScheme.primary,
onSecondary: colorScheme.secondary, onSecondary: colorScheme.secondary,
onSurface: colorScheme.surface, onSurface: colorScheme.surface,
onBackground: colorScheme.background,
onError: colorScheme.error, onError: colorScheme.error,
brightness: brightness, brightness: brightness,
), ),
@ -363,17 +361,17 @@ class _SnackbarDefaultsM3 extends SnackBarThemeData {
Color get backgroundColor => _colors.inverseSurface; Color get backgroundColor => _colors.inverseSurface;
@override @override
Color get actionTextColor => MaterialStateColor.resolveWith((states) { Color get actionTextColor => WidgetStateColor.resolveWith((states) {
if (states.contains(MaterialState.disabled)) { if (states.contains(WidgetState.disabled)) {
return _colors.inversePrimary; return _colors.inversePrimary;
} }
if (states.contains(MaterialState.pressed)) { if (states.contains(WidgetState.pressed)) {
return _colors.inversePrimary; return _colors.inversePrimary;
} }
if (states.contains(MaterialState.hovered)) { if (states.contains(WidgetState.hovered)) {
return _colors.inversePrimary; return _colors.inversePrimary;
} }
if (states.contains(MaterialState.focused)) { if (states.contains(WidgetState.focused)) {
return _colors.inversePrimary; return _colors.inversePrimary;
} }
return _colors.inversePrimary; return _colors.inversePrimary;

View file

@ -38,7 +38,7 @@ class MarkdownContainer extends StatelessWidget {
child = Theme( child = Theme(
data: Theme.of(context).copyWith( data: Theme.of(context).copyWith(
scrollbarTheme: ScrollbarThemeData( scrollbarTheme: ScrollbarThemeData(
thumbVisibility: MaterialStateProperty.all(true), thumbVisibility: WidgetStateProperty.all(true),
radius: const Radius.circular(16), radius: const Radius.circular(16),
crossAxisMargin: 6, crossAxisMargin: 6,
mainAxisMargin: 16, mainAxisMargin: 16,

View file

@ -46,8 +46,8 @@ class _PopupMenuExpansionPanelState<T> extends State<PopupMenuExpansionPanel<T>>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final style = PopupMenuTheme.of(context).labelTextStyle!.resolve( final style = PopupMenuTheme.of(context).labelTextStyle!.resolve(
<MaterialState>{ <WidgetState>{
if (!widget.enabled) MaterialState.disabled, if (!widget.enabled) WidgetState.disabled,
}, },
)!; )!;
final animationDuration = context.select<DurationsData, Duration>((v) => v.expansionTileAnimation); final animationDuration = context.select<DurationsData, Duration>((v) => v.expansionTileAnimation);

View file

@ -103,7 +103,7 @@ class _WheelSelectorState<T> extends State<WheelSelector<T>> {
child: Theme( child: Theme(
data: Theme.of(context).copyWith( data: Theme.of(context).copyWith(
scrollbarTheme: ScrollbarThemeData( scrollbarTheme: ScrollbarThemeData(
thumbVisibility: MaterialStateProperty.all(false), thumbVisibility: WidgetStateProperty.all(false),
), ),
), ),
child: ListWheelScrollView( child: ListWheelScrollView(

View file

@ -51,7 +51,7 @@ class MosaicScaleOverlay extends StatelessWidget {
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
_buildBar(extentMax, colorScheme.onBackground.withOpacity(.2)), _buildBar(extentMax, colorScheme.onSurface.withOpacity(.2)),
_buildBar(scaledSize.width, colorScheme.primary), _buildBar(scaledSize.width, colorScheme.primary),
], ],
), ),

View file

@ -1,5 +1,6 @@
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/aves_app.dart'; import 'package:aves/widgets/aves_app.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart'; import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/insets.dart'; import 'package:aves/widgets/common/basic/insets.dart';
@ -227,7 +228,7 @@ class _AvesFloatingBarState extends State<AvesFloatingBar> with RouteAware {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final backgroundColor = theme.appBarTheme.backgroundColor ?? theme.colorScheme.surface; final backgroundColor = theme.appBarTheme.backgroundColor ?? Themes.firstLayerColor(context);
return ValueListenableBuilder<bool>( return ValueListenableBuilder<bool>(
valueListenable: _isBlurAllowedNotifier, valueListenable: _isBlurAllowedNotifier,
builder: (context, isBlurAllowed, child) { builder: (context, isBlurAllowed, child) {

View file

@ -15,7 +15,7 @@ class AvesCaption extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final subtitleStyle = theme.textTheme.bodySmall!; final subtitleStyle = theme.textTheme.bodySmall!;
final subtitleChangeShadowColor = theme.colorScheme.onBackground; final subtitleChangeShadowColor = theme.colorScheme.onSurface;
return ChangeHighlightText( return ChangeHighlightText(
// provide key to refresh on theme brightness change // provide key to refresh on theme brightness change
key: ValueKey(subtitleChangeShadowColor), key: ValueKey(subtitleChangeShadowColor),

View file

@ -1,4 +1,5 @@
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/common/identity/highlight_title.dart'; import 'package:aves/widgets/common/identity/highlight_title.dart';
import 'package:expansion_tile_card/expansion_tile_card.dart'; import 'package:expansion_tile_card/expansion_tile_card.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -56,7 +57,7 @@ class AvesExpansionTile extends StatelessWidget {
expandable: enabled, expandable: enabled,
initiallyExpanded: initiallyExpanded, initiallyExpanded: initiallyExpanded,
finalPadding: const EdgeInsets.symmetric(vertical: 6.0), finalPadding: const EdgeInsets.symmetric(vertical: 6.0),
baseColor: theme.colorScheme.background, baseColor: Themes.firstLayerColor(context),
expandedTextColor: colorScheme.onSurface, expandedTextColor: colorScheme.onSurface,
duration: animationDuration, duration: animationDuration,
shadowColor: theme.shadowColor, shadowColor: theme.shadowColor,

View file

@ -12,6 +12,7 @@ import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/view/view.dart'; import 'package:aves/view/view.dart';
import 'package:aves/widgets/collection/filter_bar.dart'; import 'package:aves/widgets/collection/filter_bar.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart'; import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
@ -48,6 +49,7 @@ class AvesFilterChip extends StatefulWidget {
final CollectionFilter filter; final CollectionFilter filter;
final bool showText, showGenericIcon, useFilterColor; final bool showText, showGenericIcon, useFilterColor;
final AvesFilterDecoration? decoration; final AvesFilterDecoration? decoration;
final Color? background;
final String? banner; final String? banner;
final Widget? leadingOverride, details; final Widget? leadingOverride, details;
final double padding; final double padding;
@ -71,6 +73,7 @@ class AvesFilterChip extends StatefulWidget {
this.showGenericIcon = true, this.showGenericIcon = true,
this.useFilterColor = true, this.useFilterColor = true,
this.decoration, this.decoration,
this.background,
this.banner, this.banner,
this.leadingOverride, this.leadingOverride,
this.details, this.details,
@ -233,7 +236,7 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final decoration = widget.decoration; final decoration = widget.decoration;
final chipBackground = Theme.of(context).colorScheme.background; final chipBackground = widget.background ?? Themes.firstLayerColor(context);
final onTap = widget.onTap != null final onTap = widget.onTap != null
? () { ? () {

View file

@ -16,13 +16,13 @@ class AvesOutlinedButton extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final style = ButtonStyle( final style = ButtonStyle(
side: MaterialStateProperty.resolveWith<BorderSide>((states) { side: WidgetStateProperty.resolveWith<BorderSide>((states) {
return BorderSide( return BorderSide(
color: states.contains(MaterialState.disabled) ? theme.disabledColor : theme.colorScheme.primary, color: states.contains(WidgetState.disabled) ? theme.disabledColor : theme.colorScheme.primary,
); );
}), }),
foregroundColor: MaterialStateProperty.resolveWith<Color>((states) { foregroundColor: WidgetStateProperty.resolveWith<Color>((states) {
return states.contains(MaterialState.disabled) ? theme.disabledColor : theme.colorScheme.onBackground; return states.contains(WidgetState.disabled) ? theme.disabledColor : theme.colorScheme.onSurface;
}), }),
); );
return icon != null return icon != null

View file

@ -161,7 +161,7 @@ class OverlayTextButton extends StatelessWidget {
}); });
static const _borderRadius = 123.0; static const _borderRadius = 123.0;
static final _minSize = MaterialStateProperty.all<Size>(const Size(kMinInteractiveDimension, kMinInteractiveDimension)); static final _minSize = WidgetStateProperty.all<Size>(const Size(kMinInteractiveDimension, kMinInteractiveDimension));
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -173,12 +173,12 @@ class OverlayTextButton extends StatelessWidget {
child: OutlinedButton( child: OutlinedButton(
onPressed: onPressed, onPressed: onPressed,
style: ButtonStyle( style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Themes.overlayBackgroundColor(brightness: theme.brightness, blurred: blurred)), backgroundColor: WidgetStateProperty.all<Color>(Themes.overlayBackgroundColor(brightness: theme.brightness, blurred: blurred)),
foregroundColor: MaterialStateProperty.all<Color>(theme.colorScheme.onSurface), foregroundColor: WidgetStateProperty.all<Color>(theme.colorScheme.onSurface),
overlayColor: theme.isDark ? MaterialStateProperty.all<Color>(Colors.white.withOpacity(0.12)) : null, overlayColor: theme.isDark ? WidgetStateProperty.all<Color>(Colors.white.withOpacity(0.12)) : null,
minimumSize: _minSize, minimumSize: _minSize,
side: MaterialStateProperty.all<BorderSide>(AvesBorder.curvedSide(context)), side: WidgetStateProperty.all<BorderSide>(AvesBorder.curvedSide(context)),
shape: MaterialStateProperty.all<OutlinedBorder>(const RoundedRectangleBorder( shape: WidgetStateProperty.all<OutlinedBorder>(const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(_borderRadius)), borderRadius: BorderRadius.all(Radius.circular(_borderRadius)),
)), )),
), ),

View file

@ -1,6 +1,7 @@
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/common/basic/text/outlined.dart'; import 'package:aves/widgets/common/basic/text/outlined.dart';
import 'package:aves/widgets/common/extensions/theme.dart'; import 'package:aves/widgets/common/extensions/theme.dart';
import 'package:aves/widgets/common/fx/highlight_decoration.dart'; import 'package:aves/widgets/common/fx/highlight_decoration.dart';
@ -61,7 +62,7 @@ class HighlightTitle extends StatelessWidget {
style: style, style: style,
), ),
], ],
outlineColor: Theme.of(context).colorScheme.background, outlineColor: Themes.firstLayerColor(context),
softWrap: false, softWrap: false,
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
maxLines: 1, maxLines: 1,

View file

@ -60,49 +60,43 @@ class _OverlayCoordinateFilterChipState extends State<OverlayCoordinateFilterChi
Widget build(BuildContext context) { Widget build(BuildContext context) {
final blurred = settings.enableBlurEffect; final blurred = settings.enableBlurEffect;
final theme = Theme.of(context); final theme = Theme.of(context);
return Theme( return Align(
data: theme.copyWith( alignment: Alignment.topLeft,
colorScheme: theme.colorScheme.copyWith( child: Selector<MapThemeData, Animation<double>>(
background: Themes.overlayBackgroundColor(brightness: theme.brightness, blurred: blurred), selector: (context, v) => v.scale,
builder: (context, scale, child) => SizeTransition(
sizeFactor: scale,
axisAlignment: 1,
child: FadeTransition(
opacity: scale,
child: child,
),
), ),
), child: ValueListenableBuilder<ZoomedBounds?>(
child: Align( valueListenable: _idleBoundsNotifier,
alignment: Alignment.topLeft, builder: (context, bounds, child) {
child: Selector<MapThemeData, Animation<double>>( if (bounds == null) return const SizedBox();
selector: (context, v) => v.scale, final filter = CoordinateFilter(
builder: (context, scale, child) => SizeTransition( bounds.sw,
sizeFactor: scale, bounds.ne,
axisAlignment: 1, // more stable format when bounds change
child: FadeTransition( minuteSecondPadding: true,
opacity: scale, );
child: child, return Padding(
), padding: EdgeInsets.all(widget.padding),
), child: BlurredRRect.all(
child: ValueListenableBuilder<ZoomedBounds?>( enabled: blurred,
valueListenable: _idleBoundsNotifier, borderRadius: AvesFilterChip.defaultRadius,
builder: (context, bounds, child) { child: AvesFilterChip(
if (bounds == null) return const SizedBox(); filter: filter,
final filter = CoordinateFilter( useFilterColor: false,
bounds.sw, background: Themes.overlayBackgroundColor(brightness: theme.brightness, blurred: blurred),
bounds.ne, maxWidth: double.infinity,
// more stable format when bounds change onTap: (filter) => FilterSelectedNotification(CoordinateFilter(bounds.sw, bounds.ne)).dispatch(context),
minuteSecondPadding: true,
);
return Padding(
padding: EdgeInsets.all(widget.padding),
child: BlurredRRect.all(
enabled: blurred,
borderRadius: AvesFilterChip.defaultRadius,
child: AvesFilterChip(
filter: filter,
useFilterColor: false,
maxWidth: double.infinity,
onTap: (filter) => FilterSelectedNotification(CoordinateFilter(bounds.sw, bounds.ne)).dispatch(context),
),
), ),
); ),
}, );
), },
), ),
), ),
); );

View file

@ -67,7 +67,7 @@ class _ErrorThumbnailState extends State<ErrorThumbnail> {
} }
return Container( return Container(
alignment: Alignment.center, alignment: Alignment.center,
color: Theme.of(context).colorScheme.background, color: Themes.firstLayerColor(context),
width: extent, width: extent,
height: extent, height: extent,
child: child, child: child,

View file

@ -20,23 +20,39 @@ class _DebugColorSectionState extends State<DebugColorSection> with AutomaticKee
('onPrimary', scheme.onPrimary), ('onPrimary', scheme.onPrimary),
('primaryContainer', scheme.primaryContainer), ('primaryContainer', scheme.primaryContainer),
('onPrimaryContainer', scheme.onPrimaryContainer), ('onPrimaryContainer', scheme.onPrimaryContainer),
('primaryFixed', scheme.primaryFixed),
('primaryFixedDim', scheme.primaryFixedDim),
('onPrimaryFixed', scheme.onPrimaryFixed),
('onPrimaryFixedVariant', scheme.onPrimaryFixedVariant),
('secondary', scheme.secondary), ('secondary', scheme.secondary),
('onSecondary', scheme.onSecondary), ('onSecondary', scheme.onSecondary),
('secondaryContainer', scheme.secondaryContainer), ('secondaryContainer', scheme.secondaryContainer),
('onSecondaryContainer', scheme.onSecondaryContainer), ('onSecondaryContainer', scheme.onSecondaryContainer),
('secondaryFixed', scheme.secondaryFixed),
('secondaryFixedDim', scheme.secondaryFixedDim),
('onSecondaryFixed', scheme.onSecondaryFixed),
('onSecondaryFixedVariant', scheme.onSecondaryFixedVariant),
('tertiary', scheme.tertiary), ('tertiary', scheme.tertiary),
('onTertiary', scheme.onTertiary), ('onTertiary', scheme.onTertiary),
('tertiaryContainer', scheme.tertiaryContainer), ('tertiaryContainer', scheme.tertiaryContainer),
('onTertiaryContainer', scheme.onTertiaryContainer), ('onTertiaryContainer', scheme.onTertiaryContainer),
('tertiaryFixed', scheme.tertiaryFixed),
('tertiaryFixedDim', scheme.tertiaryFixedDim),
('onTertiaryFixed', scheme.onTertiaryFixed),
('onTertiaryFixedVariant', scheme.onTertiaryFixedVariant),
('error', scheme.error), ('error', scheme.error),
('onError', scheme.onError), ('onError', scheme.onError),
('errorContainer', scheme.errorContainer), ('errorContainer', scheme.errorContainer),
('onErrorContainer', scheme.onErrorContainer), ('onErrorContainer', scheme.onErrorContainer),
('background', scheme.background),
('onBackground', scheme.onBackground),
('surface', scheme.surface), ('surface', scheme.surface),
('onSurface', scheme.onSurface), ('onSurface', scheme.onSurface),
('surfaceVariant', scheme.surfaceVariant), ('surfaceDim', scheme.surfaceDim),
('surfaceBright', scheme.surfaceBright),
('surfaceContainerLowest', scheme.surfaceContainerLowest),
('surfaceContainerLow', scheme.surfaceContainerLow),
('surfaceContainer', scheme.surfaceContainer),
('surfaceContainerHigh', scheme.surfaceContainerHigh),
('surfaceContainerHighest', scheme.surfaceContainerHighest),
('onSurfaceVariant', scheme.onSurfaceVariant), ('onSurfaceVariant', scheme.onSurfaceVariant),
('outline', scheme.outline), ('outline', scheme.outline),
('outlineVariant', scheme.outlineVariant), ('outlineVariant', scheme.outlineVariant),

View file

@ -74,7 +74,7 @@ class AvesDialog extends StatelessWidget {
child = Theme( child = Theme(
data: Theme.of(context).copyWith( data: Theme.of(context).copyWith(
scrollbarTheme: ScrollbarThemeData( scrollbarTheme: ScrollbarThemeData(
thumbVisibility: MaterialStateProperty.all(true), thumbVisibility: WidgetStateProperty.all(true),
radius: const Radius.circular(16), radius: const Radius.circular(16),
crossAxisMargin: 4, crossAxisMargin: 4,
// adapt margin when corner is around content itself, not outside for the title // adapt margin when corner is around content itself, not outside for the title

View file

@ -99,7 +99,7 @@ class _ConvertEntryDialogState extends State<ConvertEntryDialog> {
const contentHorizontalPadding = EdgeInsets.symmetric(horizontal: AvesDialog.defaultHorizontalContentPadding); const contentHorizontalPadding = EdgeInsets.symmetric(horizontal: AvesDialog.defaultHorizontalContentPadding);
final colorScheme = Theme.of(context).colorScheme; final colorScheme = Theme.of(context).colorScheme;
final trailingStyle = TextStyle(color: colorScheme.onSurfaceVariant); final trailingStyle = TextStyle(color: colorScheme.onSurfaceVariant);
final trailingChangeShadowColor = colorScheme.onBackground; final trailingChangeShadowColor = colorScheme.onSurface;
// used by the drop down to match input decoration // used by the drop down to match input decoration
final textFieldDecorationBorder = Border( final textFieldDecorationBorder = Border(

View file

@ -2,6 +2,7 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/ref/brand_colors.dart'; import 'package:aves/ref/brand_colors.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/view/view.dart'; import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/basic/text/outlined.dart'; import 'package:aves/widgets/common/basic/text/outlined.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
@ -113,7 +114,7 @@ class _RemoveEntryMetadataDialogState extends State<RemoveEntryMetadataDialog> {
), ),
), ),
], ],
outlineColor: Theme.of(context).colorScheme.background, outlineColor: Themes.firstLayerColor(context),
); );
if (context.select<Settings, bool>((v) => v.themeColorMode == AvesThemeColorMode.polychrome)) { if (context.select<Settings, bool>((v) => v.themeColorMode == AvesThemeColorMode.polychrome)) {
final colors = context.watch<AvesColorsData>(); final colors = context.watch<AvesColorsData>();

View file

@ -36,7 +36,7 @@ class _PatternDialogState extends State<PatternDialog> {
child: PatternLock( child: PatternLock(
relativePadding: .4, relativePadding: .4,
selectedColor: colorScheme.primary, selectedColor: colorScheme.primary,
notSelectedColor: colorScheme.onBackground, notSelectedColor: colorScheme.onSurface,
pointRadius: 8, pointRadius: 8,
fillPoints: true, fillPoints: true,
onInputComplete: (input) => _submit(input.join()), onInputComplete: (input) => _submit(input.join()),

View file

@ -50,8 +50,8 @@ class _PinDialogState extends State<PinDialog> {
autoFocus: true, autoFocus: true,
autoDismissKeyboard: !widget.needConfirmation || _confirming, autoDismissKeyboard: !widget.needConfirmation || _confirming,
pinTheme: PinTheme( pinTheme: PinTheme(
activeColor: colorScheme.onBackground, activeColor: colorScheme.onSurface,
inactiveColor: colorScheme.onBackground, inactiveColor: colorScheme.onSurface,
selectedColor: colorScheme.primary, selectedColor: colorScheme.primary,
selectedFillColor: colorScheme.primary, selectedFillColor: colorScheme.primary,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),

View file

@ -124,7 +124,7 @@ class _AppDrawerState extends State<AppDrawer> {
final onPrimary = colorScheme.onPrimary; final onPrimary = colorScheme.onPrimary;
final drawerButtonStyle = ButtonStyle( final drawerButtonStyle = ButtonStyle(
padding: MaterialStateProperty.all(const EdgeInsetsDirectional.only(start: 12, end: 16)), padding: WidgetStateProperty.all(const EdgeInsetsDirectional.only(start: 12, end: 16)),
); );
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
@ -163,9 +163,9 @@ class _AppDrawerState extends State<AppDrawer> {
OutlinedButtonTheme( OutlinedButtonTheme(
data: OutlinedButtonThemeData( data: OutlinedButtonThemeData(
style: ButtonStyle( style: ButtonStyle(
foregroundColor: MaterialStateProperty.all<Color>(onPrimary), foregroundColor: WidgetStateProperty.all<Color>(onPrimary),
overlayColor: MaterialStateProperty.all<Color>(onPrimary.withOpacity(.12)), overlayColor: WidgetStateProperty.all<Color>(onPrimary.withOpacity(.12)),
side: MaterialStateProperty.all<BorderSide>(BorderSide(width: 1, color: onPrimary.withOpacity(.24))), side: WidgetStateProperty.all<BorderSide>(BorderSide(width: 1, color: onPrimary.withOpacity(.24))),
), ),
), ),
child: Wrap( child: Wrap(

View file

@ -275,7 +275,7 @@ class _QuickActionEditorBodyState<T extends Object> extends State<QuickActionEdi
effect: WormEffect( effect: WormEffect(
dotWidth: 8, dotWidth: 8,
dotHeight: 8, dotHeight: 8,
dotColor: colorScheme.onBackground.withOpacity(.2), dotColor: colorScheme.onSurface.withOpacity(.2),
activeDotColor: colorScheme.primary, activeDotColor: colorScheme.primary,
), ),
), ),

View file

@ -1,5 +1,6 @@
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/styles.dart'; import 'package:aves/theme/styles.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/common/extensions/theme.dart'; import 'package:aves/widgets/common/extensions/theme.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:decorated_icon/decorated_icon.dart'; import 'package:decorated_icon/decorated_icon.dart';
@ -17,12 +18,10 @@ class SettingsTileLeading extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return AnimatedContainer( return AnimatedContainer(
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: colorScheme.background, color: Themes.firstLayerColor(context),
border: Border.fromBorderSide(BorderSide( border: Border.fromBorderSide(BorderSide(
color: color, color: color,
width: AvesFilterChip.outlineWidth, width: AvesFilterChip.outlineWidth,
@ -34,7 +33,7 @@ class SettingsTileLeading extends StatelessWidget {
icon, icon,
size: 18, size: 18,
color: DefaultTextStyle.of(context).style.color, color: DefaultTextStyle.of(context).style.color,
shadows: theme.isDark ? AStyles.embossShadows : null, shadows: Theme.of(context).isDark ? AStyles.embossShadows : null,
), ),
); );
} }

View file

@ -80,7 +80,6 @@ class _ContentState extends State<_Content> {
valueListenable: _indexNotifier, valueListenable: _indexNotifier,
builder: (context, selectedIndex, child) { builder: (context, selectedIndex, child) {
final rail = NavigationRail( final rail = NavigationRail(
backgroundColor: Theme.of(context).colorScheme.background,
extended: true, extended: true,
destinations: sections destinations: sections
.map((section) => NavigationRailDestination( .map((section) => NavigationRailDestination(

View file

@ -5,6 +5,7 @@ import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/sort.dart'; import 'package:aves/model/entry/sort.dart';
import 'package:aves/model/filters/date.dart'; import 'package:aves/model/filters/date.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/utils/time_utils.dart'; import 'package:aves/utils/time_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/fx/transitions.dart'; import 'package:aves/widgets/common/fx/transitions.dart';
@ -196,11 +197,11 @@ class _HistogramState extends State<Histogram> with AutomaticKeepAliveClientMixi
final colorScheme = Theme.of(context).colorScheme; final colorScheme = Theme.of(context).colorScheme;
final accentColor = colorScheme.primary; final accentColor = colorScheme.primary;
final axisColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onBackground : Colors.transparent); final axisColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onSurface : Colors.transparent);
final measureLineColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onBackground.withOpacity(.1) : Colors.transparent); final measureLineColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onSurface.withOpacity(.1) : Colors.transparent);
final histogramLineColor = charts.ColorUtil.fromDartColor(drawLine ? accentColor : Colors.white); final histogramLineColor = charts.ColorUtil.fromDartColor(drawLine ? accentColor : Colors.white);
final histogramPointStrikeColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onSurface : Colors.transparent); final histogramPointStrikeColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onSurface : Colors.transparent);
final histogramPointFillColor = charts.ColorUtil.fromDartColor(colorScheme.background); final histogramPointFillColor = charts.ColorUtil.fromDartColor(Themes.firstLayerColor(context));
final series = [ final series = [
if (drawLine || drawArea) if (drawLine || drawArea)

View file

@ -1,4 +1,5 @@
import 'package:aves/theme/styles.dart'; import 'package:aves/theme/styles.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/common/basic/text/outlined.dart'; import 'package:aves/widgets/common/basic/text/outlined.dart';
import 'package:aves/widgets/common/extensions/theme.dart'; import 'package:aves/widgets/common/extensions/theme.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -15,17 +16,16 @@ class LinearPercentIndicatorText extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context);
return OutlinedText( return OutlinedText(
textSpans: [ textSpans: [
TextSpan( TextSpan(
text: percentFormat.format(percent), text: percentFormat.format(percent),
style: TextStyle( style: TextStyle(
shadows: theme.isDark ? AStyles.embossShadows : null, shadows: Theme.of(context).isDark ? AStyles.embossShadows : null,
), ),
) )
], ],
outlineColor: theme.colorScheme.background, outlineColor: Themes.firstLayerColor(context),
); );
} }
} }

View file

@ -13,6 +13,7 @@ import 'package:aves/widgets/common/app_bar/sliver_app_bar_title.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart'; import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/search/route.dart';
import 'package:aves/widgets/viewer/action/entry_info_action_delegate.dart'; import 'package:aves/widgets/viewer/action/entry_info_action_delegate.dart';
import 'package:aves/widgets/viewer/info/info_search.dart'; import 'package:aves/widgets/viewer/info/info_search.dart';
import 'package:aves/widgets/viewer/info/metadata/metadata_dir.dart'; import 'package:aves/widgets/viewer/info/metadata/metadata_dir.dart';
@ -112,14 +113,15 @@ class InfoAppBar extends StatelessWidget {
void _goToSearch(BuildContext context) { void _goToSearch(BuildContext context) {
final isSelecting = context.read<Selection<AvesEntry>?>()?.isSelecting ?? false; final isSelecting = context.read<Selection<AvesEntry>?>()?.isSelecting ?? false;
showSearch( Navigator.maybeOf(context)?.push(
context: context, SearchPageRoute(
delegate: InfoSearchDelegate( delegate: InfoSearchDelegate(
searchFieldLabel: context.l10n.viewerInfoSearchFieldLabel, searchFieldLabel: context.l10n.viewerInfoSearchFieldLabel,
searchFieldStyle: Themes.searchFieldStyle(context), searchFieldStyle: Themes.searchFieldStyle(context),
entry: entry, entry: entry,
metadataNotifier: metadataNotifier, metadataNotifier: metadataNotifier,
isSelecting: isSelecting, isSelecting: isSelecting,
),
), ),
); );
} }

View file

@ -3,28 +3,30 @@ import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/empty.dart'; import 'package:aves/widgets/common/identity/empty.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/common/search/delegate.dart';
import 'package:aves/widgets/viewer/info/embedded/embedded_data_opener.dart'; import 'package:aves/widgets/viewer/info/embedded/embedded_data_opener.dart';
import 'package:aves/widgets/viewer/info/metadata/metadata_dir.dart'; import 'package:aves/widgets/viewer/info/metadata/metadata_dir.dart';
import 'package:aves/widgets/viewer/info/metadata/metadata_dir_tile.dart'; import 'package:aves/widgets/viewer/info/metadata/metadata_dir_tile.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class InfoSearchDelegate extends SearchDelegate { class InfoSearchDelegate extends AvesSearchDelegate {
final AvesEntry entry; final AvesEntry entry;
final ValueNotifier<Map<String, MetadataDirectory>> metadataNotifier; final ValueNotifier<Map<String, MetadataDirectory>> metadataNotifier;
final bool isSelecting; final bool isSelecting;
Map<String, MetadataDirectory> get metadata => metadataNotifier.value; Map<String, MetadataDirectory> get metadata => metadataNotifier.value;
static const pageRouteName = '/info/search';
InfoSearchDelegate({ InfoSearchDelegate({
required String searchFieldLabel, required super.searchFieldLabel,
required TextStyle searchFieldStyle, required super.searchFieldStyle,
required this.entry, required this.entry,
required this.metadataNotifier, required this.metadataNotifier,
required this.isSelecting, required this.isSelecting,
}) : super( }) : super(
searchFieldLabel: searchFieldLabel, routeName: pageRouteName,
searchFieldStyle: searchFieldStyle,
); );
@override @override

View file

@ -64,7 +64,6 @@ class _TvMetadataPageState extends State<TvMetadataPage> {
if (selectedDir == null) return const SizedBox(); if (selectedDir == null) return const SizedBox();
final rail = NavigationRail( final rail = NavigationRail(
backgroundColor: Theme.of(context).colorScheme.background,
extended: true, extended: true,
destinations: titles.mapIndexed((i, title) { destinations: titles.mapIndexed((i, title) {
final dir = metadata[titles[i]]!; final dir = metadata[titles[i]]!;

View file

@ -65,10 +65,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
nested: nested:
dependency: transitive dependency: transitive
description: description:

View file

@ -153,10 +153,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
mgrs_dart: mgrs_dart:
dependency: transitive dependency: transitive
description: description:

View file

@ -58,10 +58,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View file

@ -48,6 +48,6 @@ android {
} }
defaultConfig { defaultConfig {
minSdk 19 minSdk 21
} }
} }

View file

@ -50,10 +50,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
plugin_platform_interface: plugin_platform_interface:
dependency: "direct main" dependency: "direct main"
description: description:

View file

@ -50,10 +50,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
path: path:
dependency: transitive dependency: transitive
description: description:

View file

@ -57,10 +57,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
path: path:
dependency: transitive dependency: transitive
description: description:

View file

@ -131,26 +131,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.0" version: "10.0.4"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "3.0.3"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "3.0.1"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -179,10 +179,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -248,10 +248,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.1" version: "0.7.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -264,10 +264,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "13.0.0" version: "14.2.1"
web: web:
dependency: transitive dependency: transitive
description: description:
@ -278,4 +278,4 @@ packages:
version: "0.5.1" version: "0.5.1"
sdks: sdks:
dart: ">=3.3.0 <4.0.0" dart: ">=3.3.0 <4.0.0"
flutter: ">=3.3.0" flutter: ">=3.18.0-18.0.pre.54"

View file

@ -48,6 +48,6 @@ android {
} }
defaultConfig { defaultConfig {
minSdk 19 minSdk 21
} }
} }

View file

@ -50,10 +50,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
plugin_platform_interface: plugin_platform_interface:
dependency: "direct main" dependency: "direct main"
description: description:

View file

@ -160,10 +160,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
mgrs_dart: mgrs_dart:
dependency: transitive dependency: transitive
description: description:

View file

@ -323,10 +323,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
mgrs_dart: mgrs_dart:
dependency: transitive dependency: transitive
description: description:
@ -464,10 +464,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.0" version: "5.5.1"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:
@ -485,5 +485,5 @@ packages:
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
sdks: sdks:
dart: ">=3.3.0 <4.0.0" dart: ">=3.4.0 <4.0.0"
flutter: ">=3.19.0" flutter: ">=3.19.0"

View file

@ -199,10 +199,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
mgrs_dart: mgrs_dart:
dependency: transitive dependency: transitive
description: description:

View file

@ -167,10 +167,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
mgrs_dart: mgrs_dart:
dependency: transitive dependency: transitive
description: description:

View file

@ -50,10 +50,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View file

@ -50,10 +50,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View file

@ -72,10 +72,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View file

@ -96,10 +96,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:

View file

@ -88,10 +88,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View file

@ -212,10 +212,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
package_info_plus: package_info_plus:
dependency: transitive dependency: transitive
description: description:
@ -433,10 +433,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.0" version: "5.5.1"
xml: xml:
dependency: transitive dependency: transitive
description: description:
@ -446,5 +446,5 @@ packages:
source: hosted source: hosted
version: "6.5.0" version: "6.5.0"
sdks: sdks:
dart: ">=3.3.0 <4.0.0" dart: ">=3.4.0 <4.0.0"
flutter: ">=3.19.0" flutter: ">=3.19.0"

View file

@ -177,7 +177,7 @@ packages:
description: description:
path: charts_common path: charts_common
ref: aves ref: aves
resolved-ref: "6c44dc05289c1a5dbf0b027dbba083669843072c" resolved-ref: ba7833d74e17ff497d3f4fb48f362d2dfdf3c6e3
url: "https://github.com/deckerst/flutter_google_charts.git" url: "https://github.com/deckerst/flutter_google_charts.git"
source: git source: git
version: "0.12.0" version: "0.12.0"
@ -186,7 +186,7 @@ packages:
description: description:
path: charts_flutter path: charts_flutter
ref: aves ref: aves
resolved-ref: "6c44dc05289c1a5dbf0b027dbba083669843072c" resolved-ref: ba7833d74e17ff497d3f4fb48f362d2dfdf3c6e3
url: "https://github.com/deckerst/flutter_google_charts.git" url: "https://github.com/deckerst/flutter_google_charts.git"
source: git source: git
version: "0.12.0" version: "0.12.0"
@ -436,18 +436,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flex_color_picker name: flex_color_picker
sha256: "5c846437069fb7afdd7ade6bf37e628a71d2ab0787095ddcb1253bf9345d5f3a" sha256: "31b27677d8d8400e4cff5edb3f189f606dd964d608779b6ae1b7ddad37ea48c6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.4.1" version: "3.5.0"
flex_seed_scheme: flex_seed_scheme:
dependency: transitive dependency: transitive
description: description:
name: flex_seed_scheme name: flex_seed_scheme
sha256: "4cee2f1d07259f77e8b36f4ec5f35499d19e74e17c7dce5b819554914082bc01" sha256: fb66cdb8ca89084e79efcad2bc2d9deb144666875116f08cdd8d9f8238c8b3ab
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "2.0.0"
floating: floating:
dependency: "direct main" dependency: "direct main"
description: description:
@ -511,7 +511,7 @@ packages:
description: description:
path: "." path: "."
ref: HEAD ref: HEAD
resolved-ref: "2956bcff219761aa90a1c95ad2d90b0050ae208f" resolved-ref: f35b88327b98a1494426cf40ff7c30281cc7f937
url: "https://github.com/deckerst/flutter_localization_nn.git" url: "https://github.com/deckerst/flutter_localization_nn.git"
source: git source: git
version: "0.0.1" version: "0.0.1"
@ -566,10 +566,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: frontend_server_client name: frontend_server_client
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.0" version: "4.0.0"
fuchsia_remote_debug_protocol: fuchsia_remote_debug_protocol:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -715,10 +715,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.19.0"
io: io:
dependency: transitive dependency: transitive
description: description:
@ -755,26 +755,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: leak_tracker name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.0" version: "10.0.4"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "3.0.3"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "3.0.1"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -915,10 +915,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
mgrs_dart: mgrs_dart:
dependency: transitive dependency: transitive
description: description:
@ -1093,10 +1093,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: pdf_widget_wrapper name: pdf_widget_wrapper
sha256: "9c3ca36e5000c9682d52bbdc486867ba7c5ee4403d1a5d6d03ed72157753377b" sha256: c930860d987213a3d58c7ec3b7ecf8085c3897f773e8dc23da9cae60a5d6d0f5
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.3" version: "1.0.4"
percent_indicator: percent_indicator:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1531,26 +1531,26 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: test name: test
sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.24.9" version: "1.25.2"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.1" version: "0.7.0"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.9" version: "0.6.0"
transparent_image: transparent_image:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1675,10 +1675,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "13.0.0" version: "14.2.1"
volume_controller: volume_controller:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1747,10 +1747,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.0" version: "5.5.1"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:
@ -1792,5 +1792,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.3.0 <4.0.0" dart: ">=3.4.0 <4.0.0"
flutter: ">=3.19.6" flutter: ">=3.22.0"

View file

@ -13,7 +13,7 @@ publish_to: none
environment: environment:
# this project bundles Flutter SDK via `flutter_wrapper` # this project bundles Flutter SDK via `flutter_wrapper`
# cf https://github.com/passsy/flutter_wrapper # cf https://github.com/passsy/flutter_wrapper
flutter: 3.19.6 flutter: 3.22.0
sdk: '>=3.3.0 <4.0.0' sdk: '>=3.3.0 <4.0.0'
# use `scripts/apply_flavor_{flavor}.sh` to set the right dependencies for the flavor # use `scripts/apply_flavor_{flavor}.sh` to set the right dependencies for the flavor