From f6628cf2fe05f01082cb10422f887f3dd0bca537 Mon Sep 17 00:00:00 2001 From: dom Date: Wed, 13 May 2026 09:36:54 +0800 Subject: [PATCH] flutter 3.44 pre Signed-off-by: dom --- .fvmrc | 3 - android/app/build.gradle.kts | 3 +- .../main/java/io/flutter/SystemChrome.java | 532 ++++++++++++++++++ .../com/example/piliplus/MainActivity.kt | 17 + android/gradle.properties | 4 +- lib/common/widgets/custom_icon.dart | 189 +++++-- .../widgets/flutter/chat_list_view.dart | 9 +- .../widgets/flutter/layout_builder.dart | 2 +- lib/common/widgets/flutter/list_tile.dart | 96 +++- .../widgets/flutter/page/page_view.dart | 86 ++- .../widgets/flutter/page/scrollable.dart | 12 +- lib/common/widgets/flutter/popup_menu.dart | 10 +- .../widgets/flutter/refresh_indicator.dart | 2 +- .../widgets/flutter/text/paragraph.dart | 20 +- .../adaptive_text_selection_toolbar.dart | 3 +- .../adaptive_text_selection_toolbar.dart | 3 +- .../spell_check_suggestions_toolbar.dart | 3 +- .../text_field/cupertino/text_field.dart | 13 + .../widgets/flutter/text_field/editable.dart | 2 +- .../flutter/text_field/editable_text.dart | 224 ++++---- .../spell_check_suggestions_toolbar.dart | 6 +- .../text_field/system_context_menu.dart | 3 +- .../flutter/text_field/text_field.dart | 13 + .../widgets/flutter/vertical_slider.dart | 225 +++++--- .../sliver/sliver_floating_header.dart | 9 +- lib/main.dart | 2 +- lib/plugin/pl_player/utils/fullscreen.dart | 34 +- lib/scripts/bottom_sheet_ios_flutter.patch | 28 +- lib/scripts/patch.ps1 | 15 +- pubspec.lock | 33 +- pubspec.yaml | 10 +- 31 files changed, 1284 insertions(+), 327 deletions(-) delete mode 100644 .fvmrc create mode 100644 android/app/src/main/java/io/flutter/SystemChrome.java diff --git a/.fvmrc b/.fvmrc deleted file mode 100644 index d80cf5773..000000000 --- a/.fvmrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "flutter": "3.41.9" -} \ No newline at end of file diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 8128334d6..507ea44c8 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -3,7 +3,6 @@ import org.jetbrains.kotlin.konan.properties.Properties plugins { id("com.android.application") - id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") } @@ -20,7 +19,7 @@ android { kotlin { compilerOptions { - jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 } } diff --git a/android/app/src/main/java/io/flutter/SystemChrome.java b/android/app/src/main/java/io/flutter/SystemChrome.java new file mode 100644 index 000000000..2060122d2 --- /dev/null +++ b/android/app/src/main/java/io/flutter/SystemChrome.java @@ -0,0 +1,532 @@ +package io.flutter; + +import android.app.Activity; +import android.os.Build; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.core.view.WindowInsetsControllerCompat; + +import org.json.JSONArray; +import org.json.JSONException; + +import java.util.ArrayList; +import java.util.List; + +/// from Flutter +public class SystemChrome { + public static void onMethodCall(Activity activity, String methodName, Object arguments) { + switch (methodName) { + case "SystemChrome.setEnabledSystemUIMode": + try { + SystemUiMode mode = decodeSystemUiMode((String) arguments); + setSystemChromeEnabledSystemUIMode(activity, mode); + } catch (JSONException | NoSuchFieldException exception) { + } + break; + case "SystemChrome.setEnabledSystemUIOverlays": + try { + List overlays = decodeSystemUiOverlays((JSONArray) arguments); + setSystemChromeEnabledSystemUIOverlays(activity, overlays); + } catch (JSONException | NoSuchFieldException exception) { + } + break; + } + } + + + @NonNull + private static SystemUiMode decodeSystemUiMode(@NonNull String encodedSystemUiMode) + throws JSONException, NoSuchFieldException { + SystemUiMode mode = SystemUiMode.fromValue(encodedSystemUiMode); + switch (mode) { + case LEAN_BACK: + return SystemUiMode.LEAN_BACK; + case IMMERSIVE: + return SystemUiMode.IMMERSIVE; + case IMMERSIVE_STICKY: + return SystemUiMode.IMMERSIVE_STICKY; + case EDGE_TO_EDGE: + return SystemUiMode.EDGE_TO_EDGE; + } + + // Execution should never ever get this far, but if it does, we default to edge to edge. + return SystemUiMode.EDGE_TO_EDGE; + } + + public static final int DEFAULT_SYSTEM_UI = + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + + private static void setSystemChromeEnabledSystemUIOverlays(Activity activity, List overlaysToShow) { + // Start by assuming we want to hide all system overlays (like an immersive + // game). + int enabledOverlays = + DEFAULT_SYSTEM_UI + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + + if (overlaysToShow.isEmpty()) { + enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + } + + // Re-add any desired system overlays. + for (int i = 0; i < overlaysToShow.size(); ++i) { + SystemUiOverlay overlayToShow = overlaysToShow.get(i); + switch (overlayToShow) { + case TOP_OVERLAYS: + enabledOverlays &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; + break; + case BOTTOM_OVERLAYS: + enabledOverlays &= ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + enabledOverlays &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + break; + } + } + + mEnabledOverlays = enabledOverlays; + updateSystemUiOverlays(activity); + } + + @NonNull + private static List decodeSystemUiOverlays(@NonNull JSONArray encodedSystemUiOverlay) + throws JSONException, NoSuchFieldException { + List overlays = new ArrayList<>(); + for (int i = 0; i < encodedSystemUiOverlay.length(); ++i) { + String encodedOverlay = encodedSystemUiOverlay.getString(i); + SystemUiOverlay overlay = SystemUiOverlay.fromValue(encodedOverlay); + switch (overlay) { + case TOP_OVERLAYS: + overlays.add(SystemUiOverlay.TOP_OVERLAYS); + break; + case BOTTOM_OVERLAYS: + overlays.add(SystemUiOverlay.BOTTOM_OVERLAYS); + break; + } + } + return overlays; + } + + + public enum SystemUiOverlay { + TOP_OVERLAYS("SystemUiOverlay.top"), + BOTTOM_OVERLAYS("SystemUiOverlay.bottom"); + + @NonNull + static SystemUiOverlay fromValue(@NonNull String encodedName) throws NoSuchFieldException { + for (SystemUiOverlay overlay : SystemUiOverlay.values()) { + if (overlay.encodedName.equals(encodedName)) { + return overlay; + } + } + throw new NoSuchFieldException("No such SystemUiOverlay: " + encodedName); + } + + @NonNull + private String encodedName; + + SystemUiOverlay(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } + + + private static int mEnabledOverlays; + private static SystemChromeStyle currentTheme; + + + /** + * The set of Android system fullscreen modes as perceived by the Flutter application. + */ + public enum SystemUiMode { + LEAN_BACK("SystemUiMode.leanBack"), + IMMERSIVE("SystemUiMode.immersive"), + IMMERSIVE_STICKY("SystemUiMode.immersiveSticky"), + EDGE_TO_EDGE("SystemUiMode.edgeToEdge"); + + /** + * Returns the SystemUiMode for the provided encoded value. @throws NoSuchFieldException if any + * of the given encoded overlay names are invalid. + */ + @NonNull + static SystemUiMode fromValue(@NonNull String encodedName) throws NoSuchFieldException { + for (SystemUiMode mode : SystemUiMode.values()) { + if (mode.encodedName.equals(encodedName)) { + return mode; + } + } + throw new NoSuchFieldException("No such SystemUiMode: " + encodedName); + } + + @NonNull + private String encodedName; + + /** + * Returns the encoded {@link SystemUiMode} + */ + SystemUiMode(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } + + private static void setSystemChromeEnabledSystemUIMode(Activity activity, SystemUiMode systemUiMode) { + int enabledOverlays; + + if (systemUiMode == SystemUiMode.LEAN_BACK) { + // LEAN BACK + // Available starting at Android SDK 4.1 (API 16). + // + // If the Flutter Android app targets Android SDK 15 (API 35), then the Android + // system will ignore this value unless the app also follows the opt out + // instructions found in + // https://docs.flutter.dev/release/breaking-changes/default-systemuimode-edge-to-edge. + // + // If the Flutter Android app targets Android SDK 16 (API 36) or later, then the Android + // system will ignore this value. + // + // Should not show overlays, tap to reveal overlays, needs onChange callback + // When the overlays come in on tap, the app does not receive the gesture and does not know + // the system overlay has changed. The overlays cannot be dismissed, so adding the callback + // support will allow users to restore the system ui and dismiss the overlays. + // Not compatible with top/bottom overlays enabled. + enabledOverlays = + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN; + } else if (systemUiMode == SystemUiMode.IMMERSIVE) { + // IMMERSIVE + // Available starting at Android SDK 4.4 (API 19). + // + // If the Flutter Android app targets Android SDK 15 (API 35), then the Android + // system will ignore this value unless the app also follows the opt out + // instructions found in + // https://docs.flutter.dev/release/breaking-changes/default-systemuimode-edge-to-edge. + // + // If the Flutter Android app targets Android SDK 16 (API 36) or later, then the Android + // system will ignore this value. + // + // Should not show overlays, swipe from edges to reveal overlays, needs onChange callback + // When the overlays come in on swipe, the app does not receive the gesture and does not know + // the system overlay has changed. The overlays cannot be dismissed, so adding callback + // support will allow users to restore the system ui and dismiss the overlays. + // Not compatible with top/bottom overlays enabled. + enabledOverlays = + View.SYSTEM_UI_FLAG_IMMERSIVE + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN; + } else if (systemUiMode == SystemUiMode.IMMERSIVE_STICKY) { + // STICKY IMMERSIVE + // Available starting at Android SDK 4.4 (API 19). + // + // If the Flutter Android app targets Android SDK 15 (API 35), then the Android + // system will ignore this value unless the app also follows the opt out + // instructions found in + // https://docs.flutter.dev/release/breaking-changes/default-systemuimode-edge-to-edge. + // + // If the Flutter Android app targets Android SDK 16 (API 36) or later, then the Android + // system will ignore this value. + // + // Should not show overlays, swipe from edges to reveal overlays. The app will also receive + // the swipe gesture. The overlays cannot be dismissed, so adding callback support will + // allow users to restore the system ui and dismiss the overlays. + // Not compatible with top/bottom overlays enabled. + enabledOverlays = + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN; + } else if (systemUiMode == SystemUiMode.EDGE_TO_EDGE + && Build.VERSION.SDK_INT >= API_LEVELS.API_29) { + // EDGE TO EDGE + // + // Available starting at Android SDK 10 (API 29). + // + // If the Flutter app targets Android SDK 15 (API 35) or later (Flutter does this by default), + // then this mode is used by default. + // + // SDK 29 and up will apply a translucent body scrim behind 2/3 button navigation bars + // to ensure contrast with buttons on the nav and status bars, unless the contrast is not + // enforced in the overlay styling. + enabledOverlays = + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + } else { + // When none of the conditions are matched, return without updating the system UI overlays. + return; + } + + mEnabledOverlays = enabledOverlays; + updateSystemUiOverlays(activity); + } + + + public static void updateSystemUiOverlays(Activity activity) { + activity.getWindow().getDecorView().setSystemUiVisibility(mEnabledOverlays); + if (currentTheme != null) { + setSystemChromeSystemUIOverlayStyle(activity, currentTheme); + } + } + + + private static void setSystemChromeSystemUIOverlayStyle(Activity activity, SystemChromeStyle systemChromeStyle) { + Window window = activity.getWindow(); + View view = window.getDecorView(); + WindowInsetsControllerCompat windowInsetsControllerCompat = + new WindowInsetsControllerCompat(window, view); + + if (Build.VERSION.SDK_INT < API_LEVELS.API_30) { + // Flag set to specify that this window is responsible for drawing the background for the + // system bars. Must be set for all operations on API < 30 (Android SDK < 11) excluding + // enforcing + // system bar contrasts. Deprecated in API 30. + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + + // Flag set to dismiss any requests for translucent system bars to be provided in lieu of what + // is specified by systemChromeStyle. Must be set for all operations on API < 30 operations + // excluding enforcing system bar contrasts. Deprecated in API 30. + window.clearFlags( + WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + } + + // SYSTEM STATUS BAR ------------------------------------------------------------------- + // You can't change the color of the system status bar until SDK 21, and you can't change the + // color of the status icons until SDK 23. We only allow both starting at 23 to ensure buttons + // and icons can be visible when changing the background color. + // If transparent, SDK 29 and higher may apply a translucent scrim behind the bar to ensure + // proper contrast. This can be overridden with + // SystemChromeStyle.systemStatusBarContrastEnforced. + if (systemChromeStyle.statusBarIconBrightness != null) { + switch (systemChromeStyle.statusBarIconBrightness) { + case DARK: + // Dark status bar icon brightness. + // Light status bar appearance. + windowInsetsControllerCompat.setAppearanceLightStatusBars(true); + break; + case LIGHT: + // Light status bar icon brightness. + // Dark status bar appearance. + windowInsetsControllerCompat.setAppearanceLightStatusBars(false); + break; + } + } + + if (systemChromeStyle.statusBarColor != null) { + // setStatusBarColor has no effect on Android 15 and above, meaning calls to this method will + // have no effect on those versions. + // Consider using the + // [WindowInsetsController](https://developer.android.com/reference/android/view/WindowInsetsController) + // or other Android 15+ APIs for system UI styling. + if (Build.VERSION.SDK_INT < API_LEVELS.API_35) { + window.setStatusBarColor(systemChromeStyle.statusBarColor); + } + } + // You can't override the enforced contrast for a transparent status bar until SDK 29. + // This overrides the translucent scrim that may be placed behind the bar on SDK 29+ to ensure + // contrast is appropriate when using full screen layout modes like Edge to Edge. + if (systemChromeStyle.systemStatusBarContrastEnforced != null + && Build.VERSION.SDK_INT >= API_LEVELS.API_29) { + window.setStatusBarContrastEnforced(systemChromeStyle.systemStatusBarContrastEnforced); + } + + // SYSTEM NAVIGATION BAR -------------------------------------------------------------- + // You can't change the color of the system navigation bar until SDK 21, and you can't change + // the color of the navigation buttons until SDK 26. We only allow both starting at 26 to + // ensure buttons can be visible when changing the background color. + // If transparent, SDK 29 and higher may apply a translucent scrim behind 2/3 button navigation + // bars to ensure proper contrast. This can be overridden with + // SystemChromeStyle.systemNavigationBarContrastEnforced. + if (Build.VERSION.SDK_INT >= API_LEVELS.API_26) { + if (systemChromeStyle.systemNavigationBarIconBrightness != null) { + switch (systemChromeStyle.systemNavigationBarIconBrightness) { + case DARK: + // Dark navigation bar icon brightness. + // Light navigation bar appearance. + windowInsetsControllerCompat.setAppearanceLightNavigationBars(true); + break; + case LIGHT: + // Light navigation bar icon brightness. + // Dark navigation bar appearance. + windowInsetsControllerCompat.setAppearanceLightNavigationBars(false); + break; + } + } + + if (systemChromeStyle.systemNavigationBarColor != null) { + // setNavigationBarColor has no effect on Android 15 and above, meaning calls to this method + // will have no effect on those versions. + // Consider using the + // [WindowInsetsController](https://developer.android.com/reference/android/view/WindowInsetsController) + // or other Android 15+ APIs for system UI styling. + if (Build.VERSION.SDK_INT < API_LEVELS.API_35) { + window.setNavigationBarColor(systemChromeStyle.systemNavigationBarColor); + } + } + } + // You can't change the color of the navigation bar divider color until SDK 28. + + // setNavigationBarDividerColor has no effect on Android 15 and above, meaning calls to this + // method will have no effect on those versions. + // Consider using the + // [WindowInsetsController](https://developer.android.com/reference/android/view/WindowInsetsController) + // or other Android 15+ APIs for system UI styling. + if (systemChromeStyle.systemNavigationBarDividerColor != null + && Build.VERSION.SDK_INT >= API_LEVELS.API_28 + && Build.VERSION.SDK_INT < API_LEVELS.API_35) { + window.setNavigationBarDividerColor(systemChromeStyle.systemNavigationBarDividerColor); + } + + // You can't override the enforced contrast for a transparent navigation bar until SDK 29. + // This overrides the translucent scrim that may be placed behind 2/3 button navigation bars on + // SDK 29+ to ensure contrast is appropriate when using full screen layout modes like + // Edge to Edge. + if (systemChromeStyle.systemNavigationBarContrastEnforced != null + && Build.VERSION.SDK_INT >= API_LEVELS.API_29) { + window.setNavigationBarContrastEnforced( + systemChromeStyle.systemNavigationBarContrastEnforced); + } + + currentTheme = systemChromeStyle; + } + + + public static class SystemChromeStyle { + // TODO(mattcarroll): add color annotation + @Nullable + public final Integer statusBarColor; + @Nullable + public final Brightness statusBarIconBrightness; + @Nullable + public final Boolean systemStatusBarContrastEnforced; + // TODO(mattcarroll): add color annotation + @Nullable + public final Integer systemNavigationBarColor; + @Nullable + public final Brightness systemNavigationBarIconBrightness; + // TODO(mattcarroll): add color annotation + @Nullable + public final Integer systemNavigationBarDividerColor; + @Nullable + public final Boolean systemNavigationBarContrastEnforced; + + public SystemChromeStyle( + @Nullable Integer statusBarColor, + @Nullable Brightness statusBarIconBrightness, + @Nullable Boolean systemStatusBarContrastEnforced, + @Nullable Integer systemNavigationBarColor, + @Nullable Brightness systemNavigationBarIconBrightness, + @Nullable Integer systemNavigationBarDividerColor, + @Nullable Boolean systemNavigationBarContrastEnforced) { + this.statusBarColor = statusBarColor; + this.statusBarIconBrightness = statusBarIconBrightness; + this.systemStatusBarContrastEnforced = systemStatusBarContrastEnforced; + this.systemNavigationBarColor = systemNavigationBarColor; + this.systemNavigationBarIconBrightness = systemNavigationBarIconBrightness; + this.systemNavigationBarDividerColor = systemNavigationBarDividerColor; + this.systemNavigationBarContrastEnforced = systemNavigationBarContrastEnforced; + } + } + + public enum Brightness { + LIGHT("Brightness.light"), + DARK("Brightness.dark"); + + @NonNull + static Brightness fromValue(@NonNull String encodedName) throws NoSuchFieldException { + for (Brightness brightness : Brightness.values()) { + if (brightness.encodedName.equals(encodedName)) { + return brightness; + } + } + throw new NoSuchFieldException("No such Brightness: " + encodedName); + } + + @NonNull + private String encodedName; + + Brightness(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } + + public static class API_LEVELS { + @VisibleForTesting + public static final int FLUTTER_MIN = 24; + /** + * Android 5.0 (Lollipop) + */ + public static final int API_21 = 21; + /** + * Android 5.1 (Lollipop MR1) + */ + public static final int API_22 = 22; + /** + * Android 6.0 (Marshmallow) + */ + public static final int API_23 = 23; + /** + * Android 7.0 (Nougat) + */ + public static final int API_24 = 24; + /** + * Android 7.1 (Nougat MR1) + */ + public static final int API_25 = 25; + /** + * Android 8.0 (Oreo) + */ + public static final int API_26 = 26; + /** + * Android 8.1 (Oreo MR1) + */ + public static final int API_27 = 27; + /** + * Android 9 (Pie) + */ + public static final int API_28 = 28; + /** + * Android 10 (Q) + */ + public static final int API_29 = 29; + /** + * Android 11 (R) + */ + public static final int API_30 = 30; + /** + * Android 12 (S) + */ + public static final int API_31 = 31; + /** + * Android 12L (Sv2) + */ + public static final int API_32 = 32; + /** + * Android 13 (Tiramisu) + */ + public static final int API_33 = 33; + /** + * Android 14 (Upside Down Cake) + */ + public static final int API_34 = 34; + /** + * Android 15 (Vanilla Ice Cream) + */ + public static final int API_35 = 35; + /** + * Android 16 + */ + public static final int API_36 = 36; + } +} diff --git a/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt b/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt index f90359fdb..2d2ad8995 100644 --- a/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt @@ -16,6 +16,7 @@ import androidx.core.net.toUri import com.ryanheise.audioservice.AudioServiceActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel +import io.flutter.SystemChrome class MainActivity : AudioServiceActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { @@ -120,6 +121,22 @@ class MainActivity : AudioServiceActivity() { result.success(Build.VERSION.SDK_INT) } + "SystemChrome.setEnabledSystemUIMode" -> { + SystemChrome.onMethodCall( + this, + "SystemChrome.setEnabledSystemUIMode", + call.argument("arguments") + ) + } + + "SystemChrome.setEnabledSystemUIOverlays" -> { + SystemChrome.onMethodCall( + this, + "SystemChrome.setEnabledSystemUIOverlays", + call.argument("arguments") + ) + } + else -> result.notImplemented() } } diff --git a/android/gradle.properties b/android/gradle.properties index f3dfe3348..2f0b4cadd 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,5 @@ org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true +android.builtInKotlin=false +android.newDsl=false diff --git a/lib/common/widgets/custom_icon.dart b/lib/common/widgets/custom_icon.dart index e7c17d079..341f7ff05 100644 --- a/lib/common/widgets/custom_icon.dart +++ b/lib/common/widgets/custom_icon.dart @@ -3,45 +3,152 @@ import 'package:flutter/widgets.dart'; class CustomIcons { - static const IconData ai_circle = _CustomIconData(0xe800); - static const IconData coin = _CustomIconData(0xe801); - static const IconData dm_off = _CustomIconData(0xe802); - static const IconData dm_on = _CustomIconData(0xe803); - static const IconData dm_settings = _CustomIconData(0xe804); - static const IconData dyn = _CustomIconData(0xe805); - static const IconData fav = _CustomIconData(0xe806); - static const IconData flip_rotate_90 = _CustomIconData(0xe807); - static const IconData identifier_circle = _CustomIconData(0xe808); - static const IconData live_reserve = _CustomIconData(0xe809); - static const IconData open_in_full_rotate_45 = _CustomIconData(0xe80a); - static const IconData player_dm_tip_back = _CustomIconData(0xe80b); - static const IconData player_dm_tip_copy = _CustomIconData(0xe80c); - static const IconData player_dm_tip_like = _CustomIconData(0xe80d); - static const IconData player_dm_tip_like_solid = _CustomIconData(0xe80e); - static const IconData player_dm_tip_recall = _CustomIconData(0xe80f); - static const IconData repeat_rounded_rotate_90 = _CustomIconData(0xe810); - static const IconData share = _CustomIconData(0xe811); - static const IconData share_line = _CustomIconData(0xe812); - static const IconData share_node = _CustomIconData(0xe813); - static const IconData shield_play_arrow = _CustomIconData(0xe814); - static const IconData shield_published = _CustomIconData(0xe815); - static const IconData shield_reply = _CustomIconData(0xe816); - static const IconData shopping_bag_not_interested = _CustomIconData(0xe817); - static const IconData splitscreen_rotate_90 = _CustomIconData(0xe818); - static const IconData star_favorite_line = _CustomIconData(0xe819); - static const IconData star_favorite_solid = _CustomIconData(0xe81a); - static const IconData thumbs_down = _CustomIconData(0xe81b); - static const IconData thumbs_down_outline = _CustomIconData(0xe81c); - static const IconData thumbs_up = _CustomIconData(0xe81d); - static const IconData thumbs_up_fill = _CustomIconData(0xe81e); - static const IconData thumbs_up_line = _CustomIconData(0xe81f); - static const IconData thumbs_up_outline = _CustomIconData(0xe820); - static const IconData topic_tag = _CustomIconData(0xe821); - static const IconData touch_app_rotate_270 = _CustomIconData(0xe822); - static const IconData view_headline_rotate_90 = _CustomIconData(0xe823); - static const IconData watch_later = _CustomIconData(0xe824); -} - -class _CustomIconData extends IconData { - const _CustomIconData(super.codePoint) : super(fontFamily: 'custom_icon'); + static const IconData ai_circle = IconData( + 0xe800, + fontFamily: 'custom_icon', + ); + static const IconData coin = IconData( + 0xe801, + fontFamily: 'custom_icon', + ); + static const IconData dm_off = IconData( + 0xe802, + fontFamily: 'custom_icon', + ); + static const IconData dm_on = IconData( + 0xe803, + fontFamily: 'custom_icon', + ); + static const IconData dm_settings = IconData( + 0xe804, + fontFamily: 'custom_icon', + ); + static const IconData dyn = IconData( + 0xe805, + fontFamily: 'custom_icon', + ); + static const IconData fav = IconData( + 0xe806, + fontFamily: 'custom_icon', + ); + static const IconData flip_rotate_90 = IconData( + 0xe807, + fontFamily: 'custom_icon', + ); + static const IconData identifier_circle = IconData( + 0xe808, + fontFamily: 'custom_icon', + ); + static const IconData live_reserve = IconData( + 0xe809, + fontFamily: 'custom_icon', + ); + static const IconData open_in_full_rotate_45 = IconData( + 0xe80a, + fontFamily: 'custom_icon', + ); + static const IconData player_dm_tip_back = IconData( + 0xe80b, + fontFamily: 'custom_icon', + ); + static const IconData player_dm_tip_copy = IconData( + 0xe80c, + fontFamily: 'custom_icon', + ); + static const IconData player_dm_tip_like = IconData( + 0xe80d, + fontFamily: 'custom_icon', + ); + static const IconData player_dm_tip_like_solid = IconData( + 0xe80e, + fontFamily: 'custom_icon', + ); + static const IconData player_dm_tip_recall = IconData( + 0xe80f, + fontFamily: 'custom_icon', + ); + static const IconData repeat_rounded_rotate_90 = IconData( + 0xe810, + fontFamily: 'custom_icon', + ); + static const IconData share = IconData( + 0xe811, + fontFamily: 'custom_icon', + ); + static const IconData share_line = IconData( + 0xe812, + fontFamily: 'custom_icon', + ); + static const IconData share_node = IconData( + 0xe813, + fontFamily: 'custom_icon', + ); + static const IconData shield_play_arrow = IconData( + 0xe814, + fontFamily: 'custom_icon', + ); + static const IconData shield_published = IconData( + 0xe815, + fontFamily: 'custom_icon', + ); + static const IconData shield_reply = IconData( + 0xe816, + fontFamily: 'custom_icon', + ); + static const IconData shopping_bag_not_interested = IconData( + 0xe817, + fontFamily: 'custom_icon', + ); + static const IconData splitscreen_rotate_90 = IconData( + 0xe818, + fontFamily: 'custom_icon', + ); + static const IconData star_favorite_line = IconData( + 0xe819, + fontFamily: 'custom_icon', + ); + static const IconData star_favorite_solid = IconData( + 0xe81a, + fontFamily: 'custom_icon', + ); + static const IconData thumbs_down = IconData( + 0xe81b, + fontFamily: 'custom_icon', + ); + static const IconData thumbs_down_outline = IconData( + 0xe81c, + fontFamily: 'custom_icon', + ); + static const IconData thumbs_up = IconData( + 0xe81d, + fontFamily: 'custom_icon', + ); + static const IconData thumbs_up_fill = IconData( + 0xe81e, + fontFamily: 'custom_icon', + ); + static const IconData thumbs_up_line = IconData( + 0xe81f, + fontFamily: 'custom_icon', + ); + static const IconData thumbs_up_outline = IconData( + 0xe820, + fontFamily: 'custom_icon', + ); + static const IconData topic_tag = IconData( + 0xe821, + fontFamily: 'custom_icon', + ); + static const IconData touch_app_rotate_270 = IconData( + 0xe822, + fontFamily: 'custom_icon', + ); + static const IconData view_headline_rotate_90 = IconData( + 0xe823, + fontFamily: 'custom_icon', + ); + static const IconData watch_later = IconData( + 0xe824, + fontFamily: 'custom_icon', + ); } diff --git a/lib/common/widgets/flutter/chat_list_view.dart b/lib/common/widgets/flutter/chat_list_view.dart index 540aaef0c..196b50406 100644 --- a/lib/common/widgets/flutter/chat_list_view.dart +++ b/lib/common/widgets/flutter/chat_list_view.dart @@ -5,8 +5,8 @@ import 'dart:math' as math; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; +import 'package:flutter/material.dart' hide ListView; +import 'package:flutter/rendering.dart' hide RenderSliverList; class ChatListView extends BoxScrollView { ChatListView.separated({ @@ -32,7 +32,12 @@ class ChatListView extends BoxScrollView { bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, bool addSemanticIndexes = true, + @Deprecated( + 'Use scrollCacheExtent instead. ' + 'This feature was deprecated after v3.41.0-0.0.pre.', + ) super.cacheExtent, + super.scrollCacheExtent, super.dragStartBehavior, super.keyboardDismissBehavior, super.restorationId, diff --git a/lib/common/widgets/flutter/layout_builder.dart b/lib/common/widgets/flutter/layout_builder.dart index af7a7a735..48bb2b50e 100644 --- a/lib/common/widgets/flutter/layout_builder.dart +++ b/lib/common/widgets/flutter/layout_builder.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/widgets.dart' hide LayoutBuilder; /// An abstract superclass for widgets that defer their building until layout. /// diff --git a/lib/common/widgets/flutter/list_tile.dart b/lib/common/widgets/flutter/list_tile.dart index 048fe1400..4478336c9 100644 --- a/lib/common/widgets/flutter/list_tile.dart +++ b/lib/common/widgets/flutter/list_tile.dart @@ -742,19 +742,6 @@ class ListTile extends StatelessWidget { return dense ?? tileTheme.dense ?? theme.listTileTheme.dense ?? false; } - Color _tileBackgroundColor( - ThemeData theme, - ListTileThemeData tileTheme, - ListTileThemeData defaults, - ) { - final Color? color = selected - ? selectedTileColor ?? - tileTheme.selectedTileColor ?? - theme.listTileTheme.selectedTileColor - : tileColor ?? tileTheme.tileColor ?? theme.listTileTheme.tileColor; - return color ?? defaults.tileColor!; - } - @override Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); @@ -769,6 +756,25 @@ class ListTile extends StatelessWidget { final ListTileThemeData defaults = theme.useMaterial3 ? _LisTileDefaultsM3(context) : _LisTileDefaultsM2(context, listTileStyle); + + final Color backgroundColor = + tileColor ?? + tileTheme.tileColor ?? + theme.listTileTheme.tileColor ?? + defaults.tileColor!; + final Color selectedBackgroundColor = + selectedTileColor ?? + tileTheme.selectedTileColor ?? + theme.listTileTheme.selectedTileColor ?? + defaults.tileColor!; + final effectiveTileColor = selected + ? selectedBackgroundColor + : backgroundColor; + final bool hasOpaqueBackground = + backgroundColor.alpha > 0 || selectedBackgroundColor.alpha > 0; + if (onTap != null || onLongPress != null || hasOpaqueBackground) { + assert(_debugCheckBackgroundIsHidden(context)); + } final Set states = { if (!enabled) WidgetState.disabled, if (selected) WidgetState.selected, @@ -1015,7 +1021,7 @@ class ListTile extends StatelessWidget { child: Ink( decoration: ShapeDecoration( shape: shape ?? tileTheme.shape ?? const Border(), - color: _tileBackgroundColor(theme, tileTheme, defaults), + color: effectiveTileColor, ), child: child, ), @@ -1189,6 +1195,68 @@ class ListTile extends StatelessWidget { ), ); } + + bool _debugCheckBackgroundIsHidden(BuildContext context) { + assert(() { + final Widget? intermediateWidget = _findIntermediateWidget(context); + if (intermediateWidget != null) { + FlutterError.reportError( + FlutterErrorDetails( + exception: FlutterError.fromParts([ + ErrorSummary( + 'ListTile background color or ink splashes may be invisible.', + ), + ErrorDescription( + 'The ListTile is wrapped in a ${intermediateWidget.runtimeType} that has a background color. ' + 'Because ListTile paints its background and ink splashes on the nearest Material ancestor, ' + 'this ${intermediateWidget.runtimeType} will hide those effects.', + ), + ErrorHint( + 'To fix this, wrap the ListTile in its own Material widget, ' + 'or remove the background color from the intermediate ${intermediateWidget.runtimeType}.', + ), + ]), + informationCollector: () => [ + DiagnosticsProperty( + 'ListTile', + this, + expandableValue: true, + ), + DiagnosticsProperty( + '${intermediateWidget.runtimeType}', + intermediateWidget, + expandableValue: true, + ), + ], + ), + ); + } + return true; + }()); + return true; + } + + Widget? _findIntermediateWidget(BuildContext context) { + Widget? intermediateWidget; + (context as Element).visitAncestorElements((Element ancestor) { + if (ancestor.widget is Material) { + return false; + } + final Widget widget = ancestor.widget; + final Color? color = switch (widget) { + ColoredBox(:final Color color) => color, + DecoratedBox(decoration: BoxDecoration(:final Color? color)) => color, + DecoratedBox(decoration: ShapeDecoration(:final Color? color)) => color, + _ => null, + }; + if (color != null && color.a > 0) { + intermediateWidget = widget; + return false; + } + return true; + }); + return intermediateWidget; + } } class _IndividualOverrides extends WidgetStateProperty { diff --git a/lib/common/widgets/flutter/page/page_view.dart b/lib/common/widgets/flutter/page/page_view.dart index b61ef905d..9950c7624 100644 --- a/lib/common/widgets/flutter/page/page_view.dart +++ b/lib/common/widgets/flutter/page/page_view.dart @@ -99,13 +99,24 @@ class PageView List children = const [], this.dragStartBehavior = DragStartBehavior.start, this.allowImplicitScrolling = false, + ScrollCacheExtent? scrollCacheExtent, this.restorationId, this.clipBehavior = Clip.hardEdge, this.hitTestBehavior = HitTestBehavior.opaque, this.scrollBehavior, this.padEnds = true, required this.horizontalDragGestureRecognizer, - }) : childrenDelegate = SliverChildListDelegate(children); + }) : assert( + scrollCacheExtent == null || + (scrollCacheExtent.value > 0.0) == allowImplicitScrolling, + 'scrollCacheExtent and allowImplicitScrolling must be consistent: ' + 'scrollCacheExtent must be greater than 0.0 when allowImplicitScrolling is true, ' + 'and must be 0.0 when allowImplicitScrolling is false.', + ), + scrollCacheExtent = + scrollCacheExtent ?? + ScrollCacheExtent.viewport(allowImplicitScrolling ? 1.0 : 0.0), + childrenDelegate = SliverChildListDelegate(children); final GestureRecognizerFactoryConstructor horizontalDragGestureRecognizer; @@ -147,13 +158,24 @@ class PageView int? itemCount, this.dragStartBehavior = DragStartBehavior.start, this.allowImplicitScrolling = false, + ScrollCacheExtent? scrollCacheExtent, this.restorationId, this.clipBehavior = Clip.hardEdge, this.hitTestBehavior = HitTestBehavior.opaque, this.scrollBehavior, this.padEnds = true, required this.horizontalDragGestureRecognizer, - }) : childrenDelegate = SliverChildBuilderDelegate( + }) : assert( + scrollCacheExtent == null || + (scrollCacheExtent.value > 0.0) == allowImplicitScrolling, + 'scrollCacheExtent and allowImplicitScrolling must be consistent: ' + 'scrollCacheExtent must be greater than 0.0 when allowImplicitScrolling is true, ' + 'and must be 0.0 when allowImplicitScrolling is false.', + ), + scrollCacheExtent = + scrollCacheExtent ?? + ScrollCacheExtent.viewport(allowImplicitScrolling ? 1.0 : 0.0), + childrenDelegate = SliverChildBuilderDelegate( itemBuilder, findChildIndexCallback: findChildIndexCallback, childCount: itemCount, @@ -170,7 +192,7 @@ class PageView /// {@end-tool} /// /// {@macro flutter.widgets.PageView.allowImplicitScrolling} - const PageView.custom({ + PageView.custom({ super.key, this.scrollDirection = Axis.horizontal, this.reverse = false, @@ -181,14 +203,25 @@ class PageView required this.childrenDelegate, this.dragStartBehavior = DragStartBehavior.start, this.allowImplicitScrolling = false, + ScrollCacheExtent? scrollCacheExtent, this.restorationId, this.clipBehavior = Clip.hardEdge, this.hitTestBehavior = HitTestBehavior.opaque, this.scrollBehavior, this.padEnds = true, required this.horizontalDragGestureRecognizer, - }); + }) : assert( + scrollCacheExtent == null || + (scrollCacheExtent.value > 0.0) == allowImplicitScrolling, + 'scrollCacheExtent and allowImplicitScrolling must be consistent: ' + 'scrollCacheExtent must be greater than 0.0 when allowImplicitScrolling is true, ' + 'and must be 0.0 when allowImplicitScrolling is false.', + ), + scrollCacheExtent = + scrollCacheExtent ?? + ScrollCacheExtent.viewport(allowImplicitScrolling ? 1.0 : 0.0); + /// {@template flutter.widgets.PageView.allowImplicitScrolling} /// Controls whether the widget's pages will respond to /// [RenderObject.showOnScreen], which will allow for implicit accessibility /// scrolling. @@ -200,8 +233,37 @@ class PageView /// With this flag set to true, when accessibility focus reaches the end of /// the current page and user attempts to move it to the next element, focus /// will traverse to the next page in the page view. + /// {@endtemplate} final bool allowImplicitScrolling; + /// {@macro flutter.rendering.RenderViewportBase.scrollCacheExtent} + /// + /// In [PageView], the default [scrollCacheExtent] uses + /// [ScrollCacheExtent.viewport], where the value represents the number of + /// viewport lengths to cache beyond the visible area. + /// + /// When [PageController.viewportFraction] is 1.0 (the default), this is + /// equivalent to the number of pages. For example, + /// `ScrollCacheExtent.viewport(2.0)` caches 2 pages before and after the + /// visible page. + /// + /// When [PageController.viewportFraction] is less than 1.0, multiple pages + /// may be visible in a single viewport, so `ScrollCacheExtent.viewport(1.0)` + /// may cache more than one additional page in each direction. + /// + /// [ScrollCacheExtent.pixels] can also be used to specify the cache extent + /// in logical pixels instead of viewport sizes. + /// + /// If [scrollCacheExtent] is specified, its value must be consistent with + /// [allowImplicitScrolling]: the value must be greater than 0.0 when + /// [allowImplicitScrolling] is true, and must be 0.0 when + /// [allowImplicitScrolling] is false. + /// + /// Defaults to `ScrollCacheExtent.viewport(1.0)` if + /// [allowImplicitScrolling] is true, and `ScrollCacheExtent.viewport(0.0)` if + /// [allowImplicitScrolling] is false. + final ScrollCacheExtent scrollCacheExtent; + /// {@macro flutter.widgets.scrollable.restorationId} final String? restorationId; @@ -392,11 +454,7 @@ class _PageViewState ScrollConfiguration.of(context).copyWith(scrollbars: false), viewportBuilder: (BuildContext context, ViewportOffset position) { return Viewport( - // TODO(dnfield): we should provide a way to set cacheExtent - // independent of implicit scrolling: - // https://github.com/flutter/flutter/issues/45632 - cacheExtent: widget.allowImplicitScrolling ? 1.0 : 0.0, - cacheExtentStyle: CacheExtentStyle.viewport, + scrollCacheExtent: widget.scrollCacheExtent, axisDirection: axisDirection, offset: position, clipBehavior: widget.clipBehavior, @@ -405,6 +463,7 @@ class _PageViewState viewportFraction: _controller.viewportFraction, delegate: widget.childrenDelegate, padEnds: widget.padEnds, + allowImplicitScrolling: widget.allowImplicitScrolling, ), ], ); @@ -447,6 +506,15 @@ class _PageViewState value: widget.allowImplicitScrolling, ifTrue: 'allow implicit scrolling', ), + ) + ..add( + DiagnosticsProperty( + 'scrollCacheExtent', + widget.scrollCacheExtent, + defaultValue: ScrollCacheExtent.viewport( + widget.allowImplicitScrolling ? 1.0 : 0.0, + ), + ), ); } } diff --git a/lib/common/widgets/flutter/page/scrollable.dart b/lib/common/widgets/flutter/page/scrollable.dart index f1876e5f3..57cc886c6 100644 --- a/lib/common/widgets/flutter/page/scrollable.dart +++ b/lib/common/widgets/flutter/page/scrollable.dart @@ -948,9 +948,6 @@ class ScrollableState void _receivedPointerSignal(PointerSignalEvent event) { if (event is PointerScrollEvent && _position != null) { if (_physics != null && !_physics!.shouldAcceptUserOffset(position)) { - // The handler won't use the `event`, so allow the platform to trigger - // any default native actions. - event.respond(allowPlatformDefault: true); return; } final double delta = _pointerSignalEventDelta(event); @@ -965,9 +962,6 @@ class ScrollableState ); return; } - // The `event` won't result in a scroll, so allow the platform to trigger - // any default native actions. - event.respond(allowPlatformDefault: true); } else if (event is PointerScrollInertiaCancelEvent) { position.pointerScroll(0); // Don't use the pointer signal resolver, all hit-tested scrollables should stop. @@ -976,12 +970,16 @@ class ScrollableState void _handlePointerScroll(PointerEvent event) { assert(event is PointerScrollEvent); - final double delta = _pointerSignalEventDelta(event as PointerScrollEvent); + final scrollEvent = event as PointerScrollEvent; + final double delta = _pointerSignalEventDelta(scrollEvent); final double targetScrollOffset = _targetScrollOffsetForPointerScroll( delta, ); if (delta != 0.0 && targetScrollOffset != position.pixels) { position.pointerScroll(delta); + // Tell engine this scrollable handled the event. + // This prevents parent page from scrolling when nested scrollables exist. + scrollEvent.respond(allowPlatformDefault: false); } } diff --git a/lib/common/widgets/flutter/popup_menu.dart b/lib/common/widgets/flutter/popup_menu.dart index 1e05da9cc..c20d230f7 100644 --- a/lib/common/widgets/flutter/popup_menu.dart +++ b/lib/common/widgets/flutter/popup_menu.dart @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library; - -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide PopupMenuItem; class CustomPopupMenuItem extends PopupMenuEntry { const CustomPopupMenuItem({ @@ -114,7 +112,7 @@ class _CustomPopupMenuDividerState extends State { // dart format off class _PopupMenuDefaultsM3 extends PopupMenuThemeData { _PopupMenuDefaultsM3(this.context) - : super(elevation: 3.0); + : super(elevation: 3.0); final BuildContext context; late final ThemeData _theme = Theme.of(context); @@ -123,8 +121,8 @@ class _PopupMenuDefaultsM3 extends PopupMenuThemeData { @override WidgetStateProperty? get labelTextStyle { return WidgetStateProperty.resolveWith((Set states) { - // TODO(quncheng): Update this hard-coded value to use the latest tokens. - final TextStyle style = _textTheme.labelLarge!; + // TODO(quncheng): Update this hard-coded value to use the latest tokens. + final TextStyle style = _textTheme.labelLarge!; if (states.contains(WidgetState.disabled)) { return style.apply(color: _colors.onSurface.withValues(alpha: 0.38)); } diff --git a/lib/common/widgets/flutter/refresh_indicator.dart b/lib/common/widgets/flutter/refresh_indicator.dart index 1cdb260c0..d10a52690 100644 --- a/lib/common/widgets/flutter/refresh_indicator.dart +++ b/lib/common/widgets/flutter/refresh_indicator.dart @@ -517,7 +517,7 @@ class RefreshIndicatorState extends State left: 0.0, right: 0.0, child: SizeTransition( - axisAlignment: 1.0, + alignment: .bottomStart, sizeFactor: _positionFactor, // This is what brings it down. child: Padding( padding: EdgeInsets.only(top: displacement), diff --git a/lib/common/widgets/flutter/text/paragraph.dart b/lib/common/widgets/flutter/text/paragraph.dart index ff60f8c77..d27d1ee2a 100644 --- a/lib/common/widgets/flutter/text/paragraph.dart +++ b/lib/common/widgets/flutter/text/paragraph.dart @@ -47,6 +47,7 @@ const String _kEllipsis = '\u2026'; class _UnspecifiedTextScaler extends TextScaler { const _UnspecifiedTextScaler(); + @override Never get textScaleFactor => throw UnimplementedError(); @@ -130,6 +131,7 @@ class RenderParagraph extends RenderBox // TODO(abarth): Make computing the min/max intrinsic width/height a // non-destructive operation. TextPainter? _textIntrinsicsCache; + TextPainter get _textIntrinsics { return (_textIntrinsicsCache ??= TextPainter()) ..text = _textPainter.text @@ -150,6 +152,7 @@ class RenderParagraph extends RenderBox /// The text to display. InlineSpan get text => _textPainter.text!; + set text(({InlineSpan text, Color primary}) params) { final value = params.text; _primary = params.primary; @@ -223,6 +226,7 @@ class RenderParagraph extends RenderBox /// The [SelectionRegistrar] this paragraph will be, or is, registered to. SelectionRegistrar? get registrar => _registrar; SelectionRegistrar? _registrar; + set registrar(SelectionRegistrar? value) { if (value == _registrar) { return; @@ -324,6 +328,7 @@ class RenderParagraph extends RenderBox /// How the text should be aligned horizontally. TextAlign get textAlign => _textPainter.textAlign; + set textAlign(TextAlign value) { if (_textPainter.textAlign == value) { return; @@ -344,6 +349,7 @@ class RenderParagraph extends RenderBox /// context, the English phrase will be on the right and the Hebrew phrase on /// its left. TextDirection get textDirection => _textPainter.textDirection!; + set textDirection(TextDirection value) { if (_textPainter.textDirection == value) { return; @@ -361,6 +367,7 @@ class RenderParagraph extends RenderBox /// effects. bool get softWrap => _softWrap; bool _softWrap; + set softWrap(bool value) { if (_softWrap == value) { return; @@ -372,6 +379,7 @@ class RenderParagraph extends RenderBox /// How visual overflow should be handled. TextOverflow get overflow => _overflow; TextOverflow _overflow; + set overflow(TextOverflow value) { if (_overflow == value) { return; @@ -394,6 +402,7 @@ class RenderParagraph extends RenderBox 'This feature was deprecated after v3.12.0-2.0.pre.', ) double get textScaleFactor => _textPainter.textScaleFactor; + @Deprecated( 'Use textScaler instead. ' 'Use of textScaleFactor was deprecated in preparation for the upcoming nonlinear text scaling support. ' @@ -405,6 +414,7 @@ class RenderParagraph extends RenderBox /// {@macro flutter.painting.textPainter.textScaler} TextScaler get textScaler => _textPainter.textScaler; + set textScaler(TextScaler value) { if (_textPainter.textScaler == value) { return; @@ -468,6 +478,7 @@ class RenderParagraph extends RenderBox /// {@macro flutter.painting.textPainter.textWidthBasis} TextWidthBasis get textWidthBasis => _textPainter.textWidthBasis; + set textWidthBasis(TextWidthBasis value) { if (_textPainter.textWidthBasis == value) { return; @@ -480,6 +491,7 @@ class RenderParagraph extends RenderBox /// {@macro dart.ui.textHeightBehavior} ui.TextHeightBehavior? get textHeightBehavior => _textPainter.textHeightBehavior; + set textHeightBehavior(ui.TextHeightBehavior? value) { if (_textPainter.textHeightBehavior == value) { return; @@ -494,6 +506,7 @@ class RenderParagraph extends RenderBox /// Ignored if the text is not selectable (e.g. if [registrar] is null). Color? get selectionColor => _selectionColor; Color? _selectionColor; + set selectionColor(Color? value) { if (_selectionColor == value) { return; @@ -1370,6 +1383,7 @@ class _SelectableFragment @override SelectionGeometry get value => _selectionGeometry; late SelectionGeometry _selectionGeometry; + void _updateSelectionGeometry() { final SelectionGeometry newValue = _getSelectionGeometry(); @@ -2328,6 +2342,7 @@ class _SelectableFragment PlaceholderSpan.placeholderCodeUnit, ); static final int _placeholderLength = _placeholderCharacter.length; + // This method handles updating the start edge by a text boundary that may // not be contained within this selectable fragment. It is possible // that a boundary spans multiple selectable fragments when the text contains @@ -3702,12 +3717,13 @@ class _SelectableFragment } List? _cachedBoundingBoxes; + @override List get boundingBoxes { if (_cachedBoundingBoxes == null) { final List boxes = paragraph.getBoxesForSelection( TextSelection(baseOffset: range.start, extentOffset: range.end), - boxHeightStyle: ui.BoxHeightStyle.max, + boxHeightStyle: .max, ); if (boxes.isNotEmpty) { _cachedBoundingBoxes = []; @@ -3729,10 +3745,12 @@ class _SelectableFragment } Rect? _cachedRect; + Rect get _rect { if (_cachedRect == null) { final List boxes = paragraph.getBoxesForSelection( TextSelection(baseOffset: range.start, extentOffset: range.end), + boxHeightStyle: .max, ); if (boxes.isNotEmpty) { Rect result = boxes.first.toRect(); diff --git a/lib/common/widgets/flutter/text_field/adaptive_text_selection_toolbar.dart b/lib/common/widgets/flutter/text_field/adaptive_text_selection_toolbar.dart index 059145edf..3150fd91c 100644 --- a/lib/common/widgets/flutter/text_field/adaptive_text_selection_toolbar.dart +++ b/lib/common/widgets/flutter/text_field/adaptive_text_selection_toolbar.dart @@ -11,7 +11,8 @@ library; import 'package:PiliPlus/common/widgets/flutter/text_field/editable_text.dart'; import 'package:flutter/cupertino.dart' hide EditableText, EditableTextState; -import 'package:flutter/material.dart' hide EditableText, EditableTextState; +import 'package:flutter/material.dart' + hide EditableText, EditableTextState, AdaptiveTextSelectionToolbar; import 'package:flutter/rendering.dart'; /// The default context menu for text selection for the current platform. diff --git a/lib/common/widgets/flutter/text_field/cupertino/adaptive_text_selection_toolbar.dart b/lib/common/widgets/flutter/text_field/cupertino/adaptive_text_selection_toolbar.dart index e8e988b51..6a375ee08 100644 --- a/lib/common/widgets/flutter/text_field/cupertino/adaptive_text_selection_toolbar.dart +++ b/lib/common/widgets/flutter/text_field/cupertino/adaptive_text_selection_toolbar.dart @@ -6,7 +6,8 @@ library; import 'package:PiliPlus/common/widgets/flutter/text_field/editable_text.dart'; -import 'package:flutter/cupertino.dart' hide EditableText, EditableTextState; +import 'package:flutter/cupertino.dart' + hide EditableText, EditableTextState, CupertinoAdaptiveTextSelectionToolbar; import 'package:flutter/foundation.dart' show defaultTargetPlatform; import 'package:flutter/rendering.dart'; diff --git a/lib/common/widgets/flutter/text_field/cupertino/spell_check_suggestions_toolbar.dart b/lib/common/widgets/flutter/text_field/cupertino/spell_check_suggestions_toolbar.dart index 429b5bdb5..f71055eb1 100644 --- a/lib/common/widgets/flutter/text_field/cupertino/spell_check_suggestions_toolbar.dart +++ b/lib/common/widgets/flutter/text_field/cupertino/spell_check_suggestions_toolbar.dart @@ -6,7 +6,8 @@ library; import 'package:PiliPlus/common/widgets/flutter/text_field/editable_text.dart'; -import 'package:flutter/cupertino.dart' hide EditableText, EditableTextState; +import 'package:flutter/cupertino.dart' + hide EditableText, EditableTextState, CupertinoSpellCheckSuggestionsToolbar; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart' show SelectionChangedCause, SuggestionSpan; diff --git a/lib/common/widgets/flutter/text_field/cupertino/text_field.dart b/lib/common/widgets/flutter/text_field/cupertino/text_field.dart index 54d04c6d8..5996c707b 100644 --- a/lib/common/widgets/flutter/text_field/cupertino/text_field.dart +++ b/lib/common/widgets/flutter/text_field/cupertino/text_field.dart @@ -294,6 +294,7 @@ class CupertinoRichTextField extends StatefulWidget { this.stylusHandwritingEnabled = EditableText.defaultStylusHandwritingEnabled, this.enableIMEPersonalizedLearning = true, + this.enableInlinePrediction, this.contextMenuBuilder = _defaultContextMenuBuilder, this.spellCheckConfiguration, this.magnifierConfiguration, @@ -439,6 +440,7 @@ class CupertinoRichTextField extends StatefulWidget { this.scribbleEnabled = true, this.stylusHandwritingEnabled = true, this.enableIMEPersonalizedLearning = true, + this.enableInlinePrediction, this.contextMenuBuilder = _defaultContextMenuBuilder, this.spellCheckConfiguration, this.magnifierConfiguration, @@ -799,6 +801,9 @@ class CupertinoRichTextField extends StatefulWidget { /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning} final bool enableIMEPersonalizedLearning; + /// {@macro flutter.services.TextInputConfiguration.enableInlinePrediction} + final bool? enableInlinePrediction; + /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} final ContentInsertionConfiguration? contentInsertionConfiguration; @@ -1116,6 +1121,13 @@ class CupertinoRichTextField extends StatefulWidget { defaultValue: true, ), ) + ..add( + DiagnosticsProperty( + 'enableInlinePrediction', + enableInlinePrediction, + defaultValue: null, + ), + ) ..add( DiagnosticsProperty( 'spellCheckConfiguration', @@ -1738,6 +1750,7 @@ class _CupertinoRichTextFieldState extends State scribbleEnabled: widget.scribbleEnabled, stylusHandwritingEnabled: widget.stylusHandwritingEnabled, enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning, + enableInlinePrediction: widget.enableInlinePrediction, contentInsertionConfiguration: widget.contentInsertionConfiguration, contextMenuBuilder: widget.contextMenuBuilder, spellCheckConfiguration: spellCheckConfiguration, diff --git a/lib/common/widgets/flutter/text_field/editable.dart b/lib/common/widgets/flutter/text_field/editable.dart index 6772e4bf6..c1ab543f2 100644 --- a/lib/common/widgets/flutter/text_field/editable.dart +++ b/lib/common/widgets/flutter/text_field/editable.dart @@ -19,7 +19,7 @@ import 'package:PiliPlus/common/widgets/flutter/text_field/controller.dart'; import 'package:characters/characters.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; -import 'package:flutter/rendering.dart'; +import 'package:flutter/rendering.dart' hide RenderEditable; import 'package:flutter/services.dart'; const double _kCaretGap = 1.0; // pixels diff --git a/lib/common/widgets/flutter/text_field/editable_text.dart b/lib/common/widgets/flutter/text_field/editable_text.dart index 3915d9b44..0beb97348 100644 --- a/lib/common/widgets/flutter/text_field/editable_text.dart +++ b/lib/common/widgets/flutter/text_field/editable_text.dart @@ -394,7 +394,7 @@ class _DiscreteKeyFrameSimulation extends Simulation { /// ### Customizing User Input Accessibility Announcements /// /// To customize user input accessibility announcements triggered by text -/// changes, use [SemanticsService.announce] to make the desired +/// changes, use [SemanticsService.sendAnnouncement] to make the desired /// accessibility announcement. /// /// On iOS, the on-screen keyboard may announce the most recent input @@ -525,6 +525,7 @@ class EditableText extends StatefulWidget { this.spellCheckConfiguration, this.magnifierConfiguration = TextMagnifierConfiguration.disabled, this.hintLocales, + this.enableInlinePrediction, }) : assert(obscuringCharacter.length == 1), autocorrect = autocorrect ?? _inferAutocorrect(autofillHints: autofillHints), @@ -1683,6 +1684,9 @@ class EditableText extends StatefulWidget { /// {@macro flutter.services.TextInputConfiguration.hintLocales} final List? hintLocales; + /// {@macro flutter.services.TextInputConfiguration.enableInlinePrediction} + final bool? enableInlinePrediction; + /// The default value for [selectionHeightStyle]. /// /// On web platforms, this defaults to [ui.BoxHeightStyle.max]. @@ -2159,6 +2163,13 @@ class EditableText extends StatefulWidget { defaultValue: true, ), ) + ..add( + DiagnosticsProperty( + 'enableInlinePrediction', + enableInlinePrediction, + defaultValue: null, + ), + ) ..add( DiagnosticsProperty( 'enableInteractiveSelection', @@ -2494,7 +2505,9 @@ class EditableTextState extends State final String text = widget.controller.getSelectionText(selection) ?? selection.textInside(textEditingValue.text); - Clipboard.setData(ClipboardData(text: text)); + Clipboard.setData( + ClipboardData(text: text), + ).catchError(_reportClipboardError('while copying selection to clipboard')); if (cause == SelectionChangedCause.toolbar) { bringIntoView(textEditingValue.selection.extent); hideToolbar(false); @@ -2536,7 +2549,9 @@ class EditableTextState extends State final String text = widget.controller.getSelectionText(selection) ?? selection.textInside(textEditingValue.text); - Clipboard.setData(ClipboardData(text: text)); + Clipboard.setData( + ClipboardData(text: text), + ).catchError(_reportClipboardError('while cutting selection to clipboard')); _replaceText(ReplaceTextIntent(textEditingValue, '', selection, cause)); if (cause == SelectionChangedCause.toolbar) { // Schedule a call to bringIntoView() after renderEditable updates. @@ -2550,6 +2565,19 @@ class EditableTextState extends State clipboardStatus.update(); } + void Function(Object, StackTrace) _reportClipboardError(String context) { + return (Object exception, StackTrace stack) { + FlutterError.reportError( + FlutterErrorDetails( + exception: exception, + stack: stack, + library: 'widgets library', + context: ErrorDescription(context), + ), + ); + }; + } + bool get _allowPaste { return !widget.readOnly && textEditingValue.selection.isValid; } @@ -2616,6 +2644,21 @@ class EditableTextState extends State } } + Future _pasteTextWithReporting(SelectionChangedCause cause) async { + try { + await pasteText(cause); + } catch (error, stack) { + FlutterError.reportError( + FlutterErrorDetails( + exception: error, + stack: stack, + library: 'widgets', + context: ErrorDescription('while pasting text to EditableText'), + ), + ); + } + } + /// Select the entire text value. @override void selectAll(SelectionChangedCause cause) { @@ -2723,7 +2766,19 @@ class EditableTextState extends State return; } if (_hasInputConnection) { - LiveText.startLiveTextInput(); + LiveText.startLiveTextInput().then( + (_) {}, + onError: (Object error, StackTrace stack) { + FlutterError.reportError( + FlutterErrorDetails( + exception: error, + stack: stack, + library: 'widgets library', + context: ErrorDescription('while starting Live Text input'), + ), + ); + }, + ); } if (cause == SelectionChangedCause.toolbar) { hideToolbar(); @@ -2846,8 +2901,8 @@ class EditableTextState extends State ), if (toolbarOptions.paste && pasteEnabled) ContextMenuButtonItem( - onPressed: () { - pasteText(SelectionChangedCause.toolbar); + onPressed: () async { + await _pasteTextWithReporting(SelectionChangedCause.toolbar); }, type: ContextMenuButtonType.paste, ), @@ -2972,7 +3027,7 @@ class EditableTextState extends State ? () => cutSelection(SelectionChangedCause.toolbar) : null, onPaste: pasteEnabled - ? () => pasteText(SelectionChangedCause.toolbar) + ? () => _pasteTextWithReporting(SelectionChangedCause.toolbar) : null, onSelectAll: selectAllEnabled ? () => selectAll(SelectionChangedCause.toolbar) @@ -3118,6 +3173,14 @@ class EditableTextState extends State _effectiveAutofillClient.textInputConfiguration, ); } + // The style may have changed due to dependency changes + // (e.g. MediaQuery.boldTextOf, MediaQuery.textScalerOf, etc.). + SchedulerBinding.instance.addPostFrameCallback((Duration _) { + if (!mounted || !_hasInputConnection) { + return; + } + _textInputConnection!.updateStyle(_getTextInputStyle(context)); + }, debugLabel: 'EditableText.updateStyle'); } if (defaultTargetPlatform != TargetPlatform.iOS && @@ -3217,7 +3280,13 @@ class EditableTextState extends State } if (kIsWeb && _hasInputConnection) { - if (oldWidget.readOnly != widget.readOnly) { + final obscureTextChanged = oldWidget.obscureText != widget.obscureText; + if (obscureTextChanged || oldWidget.keyboardType != widget.keyboardType) { + if (obscureTextChanged) { + // When obscureText is toggled, we should reset its state to prevent the last character from being visible between state changes. + _obscureShowCharTicksPending = 0; + _obscureLatestCharIndex = null; + } _textInputConnection!.updateConfig( _effectiveAutofillClient.textInputConfiguration, ); @@ -3240,13 +3309,14 @@ class EditableTextState extends State ? widget.style.merge(const TextStyle(fontWeight: FontWeight.bold)) : widget.style; if (_hasInputConnection) { - _textInputConnection!.setStyle( - fontFamily: _style.fontFamily, - fontSize: _style.fontSize, - fontWeight: _style.fontWeight, - textDirection: _textDirection, - textAlign: widget.textAlign, - ); + // Schedule the style update after layout to ensure preferredLineHeight + // is computed with the new style. + SchedulerBinding.instance.addPostFrameCallback((Duration _) { + if (!mounted || !_hasInputConnection) { + return; + } + _textInputConnection!.updateStyle(_getTextInputStyle(context)); + }, debugLabel: 'EditableText.updateStyle'); } } @@ -3272,6 +3342,27 @@ class EditableTextState extends State } } + TextInputStyle _getTextInputStyle(BuildContext context) { + final double? letterSpacingOverride = + MediaQuery.maybeLetterSpacingOverrideOf(context); + final double? wordSpacingOverride = MediaQuery.maybeWordSpacingOverrideOf( + context, + ); + + return TextInputStyle( + fontFamily: _style.fontFamily, + fontSize: _style.fontSize, + fontWeight: _style.fontWeight, + textDirection: _textDirection, + textAlign: widget.textAlign, + letterSpacing: letterSpacingOverride ?? _style.letterSpacing, + wordSpacing: wordSpacingOverride ?? _style.wordSpacing, + // preferredLineHeight already includes lineHeightScaleFactor from + // _OverridingTextStyleTextSpanUtils.applyTextSpacingOverrides. + lineHeight: renderEditable.preferredLineHeight, + ); + } + @protected @override void dispose() { @@ -3902,13 +3993,7 @@ class EditableTextState extends State _updateSizeAndTransform(); _schedulePeriodicPostFrameCallbacks(); _textInputConnection! - ..setStyle( - fontFamily: _style.fontFamily, - fontSize: _style.fontSize, - fontWeight: _style.fontWeight, - textDirection: _textDirection, - textAlign: widget.textAlign, - ) + ..updateStyle(_getTextInputStyle(context)) ..setEditingState(localValue) ..show(); if (_needsAutofill) { @@ -3974,13 +4059,7 @@ class EditableTextState extends State newConnection ..show() - ..setStyle( - fontFamily: _style.fontFamily, - fontSize: _style.fontSize, - fontWeight: _style.fontWeight, - textDirection: _textDirection, - textAlign: widget.textAlign, - ) + ..updateStyle(_getTextInputStyle(context)) ..setEditingState(_value); _lastKnownRemoteTextEditingValue = _value; } @@ -3996,6 +4075,15 @@ class EditableTextState extends State } } + @override + bool onFocusReceived() { + if (mounted && !_hasFocus && widget.focusNode.canRequestFocus) { + widget.focusNode.requestFocus(); + return true; + } + return false; + } + @override void connectionClosed() { if (_hasInputConnection) { @@ -5248,6 +5336,7 @@ class EditableTextState extends State ? const [] : widget.contentInsertionConfiguration!.allowedMimeTypes, hintLocales: widget.hintLocales, + enableInlinePrediction: widget.enableInlinePrediction, ); } @@ -5300,9 +5389,9 @@ class EditableTextState extends State : pasteEnabled && (widget.selectionControls?.canPaste(this) ?? false)) && (clipboardStatus.value == ClipboardStatus.pasteable) - ? () { - controls?.handlePaste(this); - pasteText(SelectionChangedCause.toolbar); + ? () async { + await controls?.handlePaste(this); + await _pasteTextWithReporting(SelectionChangedCause.toolbar); } : null; } @@ -5529,70 +5618,6 @@ class EditableTextState extends State _scrollController.jumpTo(destination); } - /// Extend the selection down by page if the `forward` parameter is true, or - /// up by page otherwise. - void _extendSelectionByPage(ExtendSelectionByPageIntent intent) { - if (widget.maxLines == 1) { - return; - } - - final TextSelection nextSelection; - final Rect extentRect = renderEditable.getLocalRectForCaret( - _value.selection.extent, - ); - final state = _scrollableKey.currentState as ScrollableState?; - final double increment = ScrollAction.getDirectionalIncrement( - state!, - ScrollIntent( - direction: intent.forward ? AxisDirection.down : AxisDirection.up, - type: ScrollIncrementType.page, - ), - ); - final ScrollPosition position = _scrollController.position; - if (intent.forward) { - if (_value.selection.extentOffset >= _value.text.length) { - return; - } - final nextExtentOffset = Offset( - extentRect.left, - extentRect.top + increment, - ); - final double height = - position.maxScrollExtent + renderEditable.size.height; - final TextPosition nextExtent = - nextExtentOffset.dy + position.pixels >= height - ? TextPosition(offset: _value.text.length) - : renderEditable.getPositionForPoint( - renderEditable.localToGlobal(nextExtentOffset), - ); - nextSelection = _value.selection.copyWith( - extentOffset: nextExtent.offset, - ); - } else { - if (_value.selection.extentOffset <= 0) { - return; - } - final nextExtentOffset = Offset( - extentRect.left, - extentRect.top + increment, - ); - final TextPosition nextExtent = nextExtentOffset.dy + position.pixels <= 0 - ? const TextPosition(offset: 0) - : renderEditable.getPositionForPoint( - renderEditable.localToGlobal(nextExtentOffset), - ); - nextSelection = _value.selection.copyWith( - extentOffset: nextExtent.offset, - ); - } - - bringIntoView(nextSelection.extent); - userUpdateTextEditingValue( - _value.copyWith(selection: nextSelection), - SelectionChangedCause.keyboard, - ); - } - void _updateSelection(UpdateSelectionIntent intent) { assert( intent.newSelection.start <= intent.currentTextEditingValue.text.length, @@ -5730,11 +5755,6 @@ class EditableTextState extends State ignoreNonCollapsedSelection: false, ), ), - ExtendSelectionByPageIntent: _makeOverridable( - CallbackAction( - onInvoke: _extendSelectionByPage, - ), - ), ExtendSelectionToNextWordBoundaryIntent: _makeOverridable( _UpdateTextSelectionAction( this, @@ -6953,7 +6973,7 @@ class _PasteSelectionAction extends ContextAction { return; } - state.pasteText(intent.cause); + state._pasteTextWithReporting(intent.cause); } } diff --git a/lib/common/widgets/flutter/text_field/spell_check_suggestions_toolbar.dart b/lib/common/widgets/flutter/text_field/spell_check_suggestions_toolbar.dart index 549e93149..592f1cefc 100644 --- a/lib/common/widgets/flutter/text_field/spell_check_suggestions_toolbar.dart +++ b/lib/common/widgets/flutter/text_field/spell_check_suggestions_toolbar.dart @@ -6,7 +6,11 @@ import 'package:PiliPlus/common/widgets/flutter/text_field/adaptive_text_selecti import 'package:PiliPlus/common/widgets/flutter/text_field/editable_text.dart'; import 'package:flutter/cupertino.dart' hide EditableText, EditableTextState; import 'package:flutter/material.dart' - hide EditableText, EditableTextState, AdaptiveTextSelectionToolbar; + hide + EditableText, + EditableTextState, + AdaptiveTextSelectionToolbar, + SpellCheckSuggestionsToolbar; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart' show SelectionChangedCause, SuggestionSpan; diff --git a/lib/common/widgets/flutter/text_field/system_context_menu.dart b/lib/common/widgets/flutter/text_field/system_context_menu.dart index ed682f985..f51bd0e9b 100644 --- a/lib/common/widgets/flutter/text_field/system_context_menu.dart +++ b/lib/common/widgets/flutter/text_field/system_context_menu.dart @@ -7,7 +7,8 @@ library; import 'package:PiliPlus/common/widgets/flutter/text_field/editable_text.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart' hide EditableText, EditableTextState; +import 'package:flutter/material.dart' + hide EditableText, EditableTextState, SystemContextMenu; import 'package:flutter/services.dart'; /// Displays the system context menu on top of the Flutter view. diff --git a/lib/common/widgets/flutter/text_field/text_field.dart b/lib/common/widgets/flutter/text_field/text_field.dart index 7cd3ed517..eb3e3b8ef 100644 --- a/lib/common/widgets/flutter/text_field/text_field.dart +++ b/lib/common/widgets/flutter/text_field/text_field.dart @@ -38,6 +38,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart' hide + TextField, EditableText, EditableTextState, EditableTextContextMenuBuilder, @@ -321,6 +322,7 @@ class RichTextField extends StatefulWidget { this.stylusHandwritingEnabled = EditableText.defaultStylusHandwritingEnabled, this.enableIMEPersonalizedLearning = true, + this.enableInlinePrediction, this.contextMenuBuilder = _defaultContextMenuBuilder, this.canRequestFocus = true, this.spellCheckConfiguration, @@ -868,6 +870,9 @@ class RichTextField extends StatefulWidget { /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning} final bool enableIMEPersonalizedLearning; + /// {@macro flutter.services.TextInputConfiguration.enableInlinePrediction} + final bool? enableInlinePrediction; + /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} final ContentInsertionConfiguration? contentInsertionConfiguration; @@ -1213,6 +1218,13 @@ class RichTextField extends StatefulWidget { defaultValue: true, ), ) + ..add( + DiagnosticsProperty( + 'enableInlinePrediction', + enableInlinePrediction, + defaultValue: null, + ), + ) ..add( DiagnosticsProperty( 'spellCheckConfiguration', @@ -1885,6 +1897,7 @@ class RichTextFieldState extends State scribbleEnabled: widget.scribbleEnabled, stylusHandwritingEnabled: widget.stylusHandwritingEnabled, enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning, + enableInlinePrediction: widget.enableInlinePrediction, contentInsertionConfiguration: widget.contentInsertionConfiguration, contextMenuBuilder: widget.contextMenuBuilder, spellCheckConfiguration: spellCheckConfiguration, diff --git a/lib/common/widgets/flutter/vertical_slider.dart b/lib/common/widgets/flutter/vertical_slider.dart index df40eadd6..1cb99503d 100644 --- a/lib/common/widgets/flutter/vertical_slider.dart +++ b/lib/common/widgets/flutter/vertical_slider.dart @@ -8,7 +8,7 @@ import 'dart:math' as math; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide Slider; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart' show timeDilation; import 'package:flutter/services.dart'; @@ -1019,6 +1019,7 @@ class _VerticalSliderState extends State onChangeEnd: _handleDragEnd, state: this, semanticFormatterCallback: widget.semanticFormatterCallback, + onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus, hasFocus: _focused, hovering: _hovering, allowedInteraction: effectiveAllowedInteraction, @@ -1037,22 +1038,17 @@ class _VerticalSliderState extends State child: result, ); - return Semantics( - label: widget.label, - container: true, - slider: true, - onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus, - child: FocusableActionDetector( - actions: _actionMap, - shortcuts: shortcutMap, - focusNode: focusNode, - autofocus: widget.autofocus, - enabled: _enabled, - onShowFocusHighlight: _handleFocusHighlightChanged, - onShowHoverHighlight: _handleHoverChanged, - mouseCursor: effectiveMouseCursor, - child: result, - ), + return FocusableActionDetector( + actions: _actionMap, + shortcuts: shortcutMap, + focusNode: focusNode, + autofocus: widget.autofocus, + enabled: _enabled, + onShowFocusHighlight: _handleFocusHighlightChanged, + onShowHoverHighlight: _handleHoverChanged, + mouseCursor: effectiveMouseCursor, + includeFocusSemantics: false, + child: result, ); } @@ -1111,6 +1107,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { required this.onChangeEnd, required this.state, required this.semanticFormatterCallback, + required this.onDidGainAccessibilityFocus, required this.hasFocus, required this.hovering, required this.allowedInteraction, @@ -1127,6 +1124,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { final ValueChanged? onChangeStart; final ValueChanged? onChangeEnd; final SemanticFormatterCallback? semanticFormatterCallback; + final VoidCallback? onDidGainAccessibilityFocus; final _VerticalSliderState state; final bool hasFocus; final bool hovering; @@ -1148,6 +1146,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { state: state, textDirection: Directionality.of(context), semanticFormatterCallback: semanticFormatterCallback, + onDidGainAccessibilityFocus: onDidGainAccessibilityFocus, platform: Theme.of(context).platform, hasFocus: hasFocus, hovering: hovering, @@ -1173,6 +1172,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { ..onChangeEnd = onChangeEnd ..textDirection = Directionality.of(context) ..semanticFormatterCallback = semanticFormatterCallback + ..onDidGainAccessibilityFocus = onDidGainAccessibilityFocus ..platform = Theme.of(context).platform ..hasFocus = hasFocus ..hovering = hovering @@ -1195,6 +1195,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { required TargetPlatform platform, required ValueChanged? onChanged, required SemanticFormatterCallback? semanticFormatterCallback, + required this.onDidGainAccessibilityFocus, required this.onChangeStart, required this.onChangeEnd, required _VerticalSliderState state, @@ -1292,6 +1293,7 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { late VerticalDragGestureRecognizer _drag; late TapGestureRecognizer _tap; bool _active = false; + VoidCallback? onDidGainAccessibilityFocus; double _currentDragValue = 0.0; Rect? overlayRect; @@ -1808,31 +1810,12 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { sliderTheme: _sliderTheme, isDiscrete: isDiscrete, ); - final double padding = _sliderTheme.trackShape!.isRounded - ? trackRect.width - : 0.0; - final double thumbPosition = isDiscrete - ? trackRect.left + - visualPosition * (trackRect.width - padding) + - padding / 2 - : trackRect.bottom - visualPosition * trackRect.height; - // Apply padding to trackRect.left and trackRect.right if the track height is - // greater than the thumb radius to ensure the thumb is drawn within the track. - final Size thumbPreferredSize = _sliderTheme.thumbShape!.getPreferredSize( - isInteractive, - isDiscrete, - ); - final double thumbPadding = (padding > thumbPreferredSize.width / 2 - ? padding / 2 - : 0); - final thumbCenter = Offset( - trackRect.center.dx, - clampDouble( - thumbPosition, - trackRect.top + thumbPadding, - trackRect.bottom - thumbPadding, - ), + + final Offset thumbCenter = _calcThumbCenter( + trackRect: trackRect, + visualPosition: visualPosition, ); + if (isInteractive) { final Size overlaySize = sliderTheme.overlayShape!.getPreferredSize( isInteractive, @@ -1940,33 +1923,35 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { } } - if (isInteractive && label != null) { - if ((shouldShowValueIndicatorWhenDragged && - !_valueIndicatorAnimation.isDismissed) || - shouldAlwaysShowValueIndicator) { - _state.paintValueIndicator = (PaintingContext context, Offset offset) { - if (attached && _labelPainter.text != null) { - _sliderTheme.valueIndicatorShape!.paint( - context, - offset + thumbCenter, - activationAnimation: shouldAlwaysShowValueIndicator - ? const AlwaysStoppedAnimation(1) - : _valueIndicatorAnimation, - enableAnimation: shouldAlwaysShowValueIndicator - ? const AlwaysStoppedAnimation(1) - : _enableAnimation, - isDiscrete: isDiscrete, - labelPainter: _labelPainter, - parentBox: this, - sliderTheme: _sliderTheme, - textDirection: _textDirection, - value: _value, - textScaleFactor: textScaleFactor, - sizeWithOverflow: screenSize.isEmpty ? size : screenSize, - ); - } - }; - } + if (isInteractive && + label != null && + ((shouldShowValueIndicatorWhenDragged && + !_valueIndicatorAnimation.isDismissed) || + shouldAlwaysShowValueIndicator)) { + _state.paintValueIndicator = (PaintingContext context, Offset offset) { + if (attached && _labelPainter.text != null) { + _sliderTheme.valueIndicatorShape?.paint( + context, + offset + thumbCenter, + activationAnimation: shouldAlwaysShowValueIndicator + ? const AlwaysStoppedAnimation(1) + : _valueIndicatorAnimation, + enableAnimation: shouldAlwaysShowValueIndicator + ? const AlwaysStoppedAnimation(1) + : _enableAnimation, + isDiscrete: isDiscrete, + labelPainter: _labelPainter, + parentBox: this, + sliderTheme: _sliderTheme, + textDirection: _textDirection, + value: _value, + textScaleFactor: textScaleFactor, + sizeWithOverflow: screenSize.isEmpty ? size : screenSize, + ); + } + }; + } else { + _state.paintValueIndicator = null; } _sliderTheme.thumbShape!.paint( @@ -1991,27 +1976,96 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { ); } + /// Calculates the local coordinate center of the [Slider] thumb given its + /// physical placement on the track from 0.0 (left) to 1.0 (right). + /// + /// The [visualPosition] is provided by the caller so semantics can use the + /// raw logical value while paint can use the smoothly animated value. + Offset _calcThumbCenter({ + required Rect trackRect, + required double visualPosition, + }) { + final double padding = _sliderTheme.trackShape!.isRounded + ? trackRect.height + : 0.0; + final double thumbPosition = isDiscrete + ? trackRect.left + + visualPosition * (trackRect.width - padding) + + padding / 2 + : trackRect.left + visualPosition * trackRect.width; + // Apply padding to trackRect.left and trackRect.right if the track height is + // greater than the thumb radius to ensure the thumb is drawn within the track. + final Size thumbPreferredSize = _sliderTheme.thumbShape!.getPreferredSize( + isInteractive, + isDiscrete, + ); + final double thumbPadding = padding > thumbPreferredSize.width / 2 + ? padding / 2 + : 0; + return Offset( + clampDouble( + thumbPosition, + trackRect.top + thumbPadding, + trackRect.bottom - thumbPadding, + ), + trackRect.center.dy, + ); + } + + Offset get _semanticThumbCenter { + final double visualPosition = switch (textDirection) { + TextDirection.rtl => 1.0 - _value, + TextDirection.ltr => _value, + }; + return _calcThumbCenter( + trackRect: _trackRect, + visualPosition: visualPosition, + ); + } + + @override + void assembleSemanticsNode( + SemanticsNode node, + SemanticsConfiguration config, + Iterable children, + ) { + node + ..rect = Rect.fromCenter( + center: _semanticThumbCenter, + width: kMinInteractiveDimension, + height: kMinInteractiveDimension, + ) + ..updateWith(config: config); + } + @override void describeSemanticsConfiguration(SemanticsConfiguration config) { super.describeSemanticsConfiguration(config); - // The Slider widget has its own Focus widget with semantics information, + // The Slider widget has its own Focus widget. + // We mark the Focus widget with "includeFocusSemantics: false" // and we want that semantics node to collect the semantics information here - // so that it's all in the same node: otherwise Talkback sees that the node - // has focusable children, and it won't focus the Slider's Focus widget - // because it thinks the Focus widget's node doesn't have anything to say - // (which it doesn't, but this child does). Aggregating the semantic - // information into one node means that Talkback will recognize that it has - // something to say and focus it when it receives keyboard focus. - // (See https://github.com/flutter/flutter/issues/57038 for context). + // so that it's all in the same node. config - ..isSemanticBoundary = false - ..isEnabled = isInteractive - ..textDirection = textDirection; + ..isSemanticBoundary = true + ..isEnabled = isInteractive; + if (label != null) { + config.label = label!; + } + config + ..isSlider = true + ..isFocusable = isInteractive + ..isFocused = hasFocus; + + if (onDidGainAccessibilityFocus != null) { + config.onDidGainAccessibilityFocus = onDidGainAccessibilityFocus; + } + config.textDirection = textDirection; if (isInteractive) { config ..onIncrease = increaseAction - ..onDecrease = decreaseAction; + ..onDecrease = decreaseAction + ..onFocus = onFocusAction; } if (semanticFormatterCallback != null) { @@ -2036,6 +2090,17 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { double get _semanticActionUnit => divisions != null ? 1.0 / divisions! : _adjustmentUnit; + void onFocusAction() { + if (isInteractive) { + if (!_state.mounted) { + return; + } + if (!hasFocus) { + _state.focusNode.requestFocus(); + } + } + } + void increaseAction() { if (isInteractive) { onChangeStart!(currentValue); diff --git a/lib/common/widgets/sliver/sliver_floating_header.dart b/lib/common/widgets/sliver/sliver_floating_header.dart index 5b87b9e8e..78b2a2cc8 100644 --- a/lib/common/widgets/sliver/sliver_floating_header.dart +++ b/lib/common/widgets/sliver/sliver_floating_header.dart @@ -17,6 +17,7 @@ import 'dart:math' as math; +import 'package:PiliPlus/utils/platform_utils.dart'; import 'package:flutter/foundation.dart' show clampDouble; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart' @@ -36,9 +37,15 @@ class SliverFloatingHeaderWidget extends StatelessWidget { @override Widget build(BuildContext context) { + if (PlatformUtils.isDesktop) { + return _SliverFloatingHeaderWidget( + backgroundColor: backgroundColor, + child: _SliverFloatingHeaderScroll(child: child), + ); + } return _SliverFloatingHeaderWidget( backgroundColor: backgroundColor, - child: _SliverFloatingHeaderScroll(child: child), + child: child, ); } } diff --git a/lib/main.dart b/lib/main.dart index 5e11334ef..8ec2748e6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -130,7 +130,7 @@ void main() async { SmartDialog.config.toast = SmartConfigToast(displayType: .onlyRefresh); if (PlatformUtils.isMobile) { - SystemChrome.setEnabledSystemUIMode(.edgeToEdge); + setEnabledSystemUIMode(.edgeToEdge); SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( systemNavigationBarColor: Colors.transparent, diff --git a/lib/plugin/pl_player/utils/fullscreen.dart b/lib/plugin/pl_player/utils/fullscreen.dart index 15783f449..8104b67e0 100644 --- a/lib/plugin/pl_player/utils/fullscreen.dart +++ b/lib/plugin/pl_player/utils/fullscreen.dart @@ -3,7 +3,12 @@ import 'dart:io' show Platform; import 'package:PiliPlus/utils/device_utils.dart'; import 'package:flutter/services.dart' - show SystemChrome, MethodChannel, SystemUiOverlay, DeviceOrientation; + show + SystemChrome, + MethodChannel, + SystemUiOverlay, + DeviceOrientation, + SystemUiMode; bool _isDesktopFullScreen = false; @@ -69,7 +74,7 @@ Future? hideSystemBar() { return null; } _showSystemBar = false; - return SystemChrome.setEnabledSystemUIMode(.immersiveSticky); + return setEnabledSystemUIMode(.immersiveSticky); } //退出全屏显示 @@ -78,8 +83,31 @@ Future? showSystemBar() { return null; } _showSystemBar = true; - return SystemChrome.setEnabledSystemUIMode( + return setEnabledSystemUIMode( Platform.isAndroid && DeviceUtils.sdkInt < 29 ? .manual : .edgeToEdge, overlays: SystemUiOverlay.values, ); } + +// TODO: remove +Future setEnabledSystemUIMode( + SystemUiMode mode, { + List? overlays, +}) async { + if (mode != SystemUiMode.manual) { + await const MethodChannel('PiliPlus').invokeMethod( + 'SystemChrome.setEnabledSystemUIMode', + {'arguments': mode.toString()}, + ); + } else { + assert(mode == SystemUiMode.manual && overlays != null); + await const MethodChannel('PiliPlus').invokeMethod( + 'SystemChrome.setEnabledSystemUIOverlays', + {'arguments': _stringify(overlays!)}, + ); + } +} + +List _stringify(List list) => [ + for (final dynamic item in list) item.toString(), +]; diff --git a/lib/scripts/bottom_sheet_ios_flutter.patch b/lib/scripts/bottom_sheet_ios_flutter.patch index 02612f9cc..1b44933c0 100644 --- a/lib/scripts/bottom_sheet_ios_flutter.patch +++ b/lib/scripts/bottom_sheet_ios_flutter.patch @@ -1,5 +1,5 @@ diff --git a/packages/flutter/lib/src/cupertino/route.dart b/packages/flutter/lib/src/cupertino/route.dart -index 5ce4479480e..2551c6613ae 100644 +index b6786d2611f..c36901a40bf 100644 --- a/packages/flutter/lib/src/cupertino/route.dart +++ b/packages/flutter/lib/src/cupertino/route.dart @@ -201,7 +201,7 @@ mixin CupertinoRouteTransitionMixin on PageRoute { @@ -69,11 +69,10 @@ index 5ce4479480e..2551c6613ae 100644 } // The popping may have finished inline if already at the target destination. -@@ -1548,3 +1558,18 @@ class CupertinoDialogRoute extends RawDialogRoute { - // transitions. +@@ -1549,6 +1559,20 @@ class CupertinoDialogRoute extends RawDialogRoute { static final Tween _dialogScaleTween = Tween(begin: 1.3, end: 1.0); } -+ + +class _HorizontalDragGestureRecognizer extends HorizontalDragGestureRecognizer { + _HorizontalDragGestureRecognizer({ + super.debugOwner, @@ -81,19 +80,21 @@ index 5ce4479480e..2551c6613ae 100644 + super.allowedButtonsFilter, + }); + -+ + @override + void didStopTrackingLastPointer(int pointer) { + gestureSettings = null; + super.didStopTrackingLastPointer(pointer); + } +} -\ No newline at end of file ++ + /// A [PageTransitionsBuilder] that provides an iOS-style page transition + /// animation. + /// diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart -index d121d10f1d6..92fa155c168 100644 +index 0233bd1da39..a1ce131c786 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart -@@ -2521,6 +2521,7 @@ class ScaffoldState extends State +@@ -2519,6 +2519,7 @@ class ScaffoldState extends State final LocalHistoryEntry? entry = isPersistent ? null : LocalHistoryEntry( @@ -102,7 +103,7 @@ index d121d10f1d6..92fa155c168 100644 if (!removedEntry && _currentBottomSheet?._widget == bottomSheet && !doingDispose) { removeCurrentBottomSheet(); diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart -index 5c4a8982617..6621148781a 100644 +index 6d42beb65c0..44c3da72895 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart @@ -709,7 +709,11 @@ class LocalHistoryEntry { @@ -156,13 +157,16 @@ index 5c4a8982617..6621148781a 100644 } class _DismissModalAction extends DismissAction { -@@ -2839,3 +2860,9 @@ abstract class PopEntry { - return 'PopEntry canPop: ${canPopNotifier.value}, onPopInvoked: $onPopInvokedWithResult'; +@@ -989,6 +1010,12 @@ class _DismissModalAction extends DismissAction { } } -+ + +class _GesturePop { + const _GesturePop(); +} + +const Object gesturePop = _GesturePop(); ++ + enum _ModalRouteAspect { + /// Specifies the aspect corresponding to [ModalRoute.isCurrent]. + isCurrent, diff --git a/lib/scripts/patch.ps1 b/lib/scripts/patch.ps1 index 7a91d1ba9..560e52247 100644 --- a/lib/scripts/patch.ps1 +++ b/lib/scripts/patch.ps1 @@ -2,10 +2,6 @@ param( [string]$platform = "" ) -# TODO: remove -# https://github.com/flutter/flutter/issues/182468 -$ToolTipFix = "56956c33ef102ac0b5fc46b62bd2dd9f50a86616"; - # TODO: remove # https://github.com/flutter/flutter/issues/182281 $NewOverScrollIndicator = "362b1de29974ffc1ed6faa826e1df870d7bec75f"; @@ -20,6 +16,7 @@ $ScrollViewPatch = "lib/scripts/scroll_view.patch" $TextSelectionPatch = "lib/scripts/text_selection.patch" +# https://github.com/bggRGjQaUbCoE/PiliPlus/issues/1947 $NavigatorPatch = "lib/scripts/navigator.patch" $NavigationBarPatch = "lib/scripts/navigation_bar.patch" @@ -63,13 +60,10 @@ switch ($platform.ToLower()) { $patches += $BottomSheetIOSFlutterPatch } "linux" { - $picks += $ToolTipFix } "macos" { - $picks += $ToolTipFix } "windows" { - $picks += $ToolTipFix } default {} } @@ -105,10 +99,3 @@ foreach ($patch in $patches) { Write-Host "$patch applied" } } - -# TODO: remove -if ($platform.ToLower() -eq "android") { - "69e31205362b4e59b7eb89b24797e687b4b67afe" | Set-Content -Path .\bin\internal\engine.version - Remove-Item -Path ".\bin\cache" -Recurse -Force - flutter --version -} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 58f6b26a5..f34078ecb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" + sha256: cd6add6f846f35fb79f3c315296703c1a24f3cfd7f4739d91a74961c1c7e9f1b url: "https://pub.dev" source: hosted - version: "93.0.0" + version: "100.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b + sha256: "6ba98576948803398b69e3a444df24eacdbe12ed699c7014e120ea38552debbf" url: "https://pub.dev" source: hosted - version: "10.0.1" + version: "13.0.0" ansicolor: dependency: transitive description: @@ -360,10 +360,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2" + sha256: "59d53ef8eaed9d288ed9767618e2b31c4fa0383a127db59d5eb2e737a7638a60" url: "https://pub.dev" source: hosted - version: "3.1.7" + version: "3.1.9" dbus: dependency: transitive description: @@ -744,10 +744,11 @@ packages: font_awesome_flutter: dependency: "direct main" description: - name: font_awesome_flutter - sha256: f50ce90dbe26d977415b9540400d6778bef00894aced6358ae578abd92b14b10 - url: "https://pub.dev" - source: hosted + path: "." + ref: "v10.9.0" + resolved-ref: cf4a19ece5cb296d73d90f0d73106f779c85fa26 + url: "https://github.com/bggRGjQaUbCoE/font_awesome_flutter.git" + source: git version: "10.9.0" get: dependency: "direct main" @@ -1083,7 +1084,7 @@ packages: description: path: "." ref: const - resolved-ref: c11f18c031f1045900dc3e817f7519eb863c2550 + resolved-ref: "2bb68d1de3c7bb8c6117fce54d43da65a2387163" url: "https://github.com/bggRGjQaUbCoE/material_design_icons_flutter.git" source: git version: "7.0.7447" @@ -1186,10 +1187,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: "direct main" description: @@ -1764,10 +1765,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.11" tray_manager: dependency: "direct main" description: @@ -2035,4 +2036,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.11.0 <4.0.0" - flutter: "3.41.9" + flutter: "3.44.0-0.3.pre" diff --git a/pubspec.yaml b/pubspec.yaml index 69cd5655f..9c00ad013 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,8 +20,8 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 2.0.7+1 environment: - sdk: ">=3.10.0" - flutter: 3.41.9 # update `.fvmrc` config + sdk: ">=3.11.0" + flutter: 3.44.0-0.3.pre # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -95,7 +95,10 @@ dependencies: ref: master flutter_svg: ^2.0.14 flutter_volume_controller: ^1.3.3 - font_awesome_flutter: 10.9.0 + font_awesome_flutter: + git: + url: https://github.com/bggRGjQaUbCoE/font_awesome_flutter.git + ref: v10.9.0 get: git: url: https://github.com/bggRGjQaUbCoE/getx.git @@ -160,7 +163,6 @@ dependencies: ref: main dependency_overrides: - font_awesome_flutter: 10.9.0 media_kit: git: url: https://github.com/My-Responsitories/media-kit.git