From 1fcc26464fefb6e7f937df8494aa65a10577c1d0 Mon Sep 17 00:00:00 2001 From: dom Date: Sat, 16 May 2026 22:51:33 +0800 Subject: [PATCH] flutter 3.44.0 Signed-off-by: dom --- .fvmrc | 2 +- .github/workflows/build.yml | 2 +- analysis_options.yaml | 1 - android/app/build.gradle.kts | 13 +- .../main/java/io/flutter/SystemChrome.java | 532 ++++++++++++++++++ .../com/example/piliplus/MainActivity.kt | 17 + android/build.gradle.kts | 6 +- android/gradle.properties | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle.kts | 4 +- lib/common/widgets/cropped_image.dart | 22 +- lib/common/widgets/custom_arc.dart | 13 +- lib/common/widgets/custom_height_widget.dart | 7 +- lib/common/widgets/custom_icon.dart | 83 ++- lib/common/widgets/custom_tooltip.dart | 15 +- lib/common/widgets/disabled_icon.dart | 16 +- lib/common/widgets/draggable_sheet/topic.dart | 4 +- lib/common/widgets/extra_hit_test_widget.dart | 4 +- .../widgets/flutter/chat_list_view.dart | 11 +- .../flutter/draggable_scrollable_sheet.dart | 2 + .../widgets/flutter/layout_builder.dart | 4 +- lib/common/widgets/flutter/list_tile.dart | 98 +++- .../widgets/flutter/page/page_view.dart | 88 ++- .../widgets/flutter/page/scrollable.dart | 14 +- .../flutter/page/scrollable_helpers.dart | 4 +- lib/common/widgets/flutter/page/tabs.dart | 2 + lib/common/widgets/flutter/pop_scope.dart | 2 + lib/common/widgets/flutter/popup_menu.dart | 10 +- .../widgets/flutter/refresh_indicator.dart | 4 +- .../flutter/sliver_layout_builder.dart | 2 + .../widgets/flutter/text/paragraph.dart | 22 +- .../widgets/flutter/text/rich_text.dart | 2 + lib/common/widgets/flutter/text/text.dart | 2 +- .../adaptive_text_selection_toolbar.dart | 5 +- .../flutter/text_field/controller.dart | 9 +- .../adaptive_text_selection_toolbar.dart | 5 +- .../spell_check_suggestions_toolbar.dart | 5 +- .../text_field/cupertino/text_field.dart | 15 + .../widgets/flutter/text_field/editable.dart | 4 +- .../flutter/text_field/editable_text.dart | 226 ++++---- .../flutter/text_field/spell_check.dart | 2 + .../spell_check_suggestions_toolbar.dart | 8 +- .../text_field/system_context_menu.dart | 5 +- .../flutter/text_field/text_field.dart | 15 +- .../flutter/text_field/text_selection.dart | 2 + .../widgets/flutter/vertical_slider.dart | 227 +++++--- lib/common/widgets/flutter/vertical_tabs.dart | 2 + .../image/cached_network_svg_image.dart | 42 +- .../image_grid/image_grid_builder.dart | 5 +- .../image_viewer/loading_indicator.dart | 7 +- .../loading_widget/m3e_loading_indicator.dart | 11 +- lib/common/widgets/marquee.dart | 6 +- .../audio_video_progress_bar.dart | 32 +- .../progress_bar/segment_progress_bar.dart | 7 +- .../video_progress_indicator.dart | 16 +- .../sliver/sliver_floating_header.dart | 4 +- .../sliver/sliver_pinned_dynamic_header.dart | 7 +- .../widgets/sliver/sliver_pinned_header.dart | 4 +- lib/common/widgets/sliver_wrap.dart | 8 +- lib/main.dart | 2 +- lib/models_new/followee_votes/vote.dart | 7 +- lib/pages/article/widgets/article_ops.dart | 4 +- lib/pages/common/publish/publish_route.dart | 23 +- lib/pages/live_room/view.dart | 7 +- lib/pages/member/widget/reserve_button.dart | 5 +- lib/pages/search/widgets/hot_keyword.dart | 10 +- lib/plugin/pl_player/controller.dart | 1 - lib/plugin/pl_player/utils/fullscreen.dart | 35 +- lib/plugin/pl_player/view/widgets.dart | 7 +- lib/scripts/bottom_sheet_ios_flutter.patch | 28 +- lib/scripts/patch.ps1 | 14 - pubspec.lock | 35 +- pubspec.yaml | 9 +- 73 files changed, 1350 insertions(+), 530 deletions(-) create mode 100644 android/app/src/main/java/io/flutter/SystemChrome.java diff --git a/.fvmrc b/.fvmrc index d80cf5773..457360f80 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.41.9" + "flutter": "3.44.0" } \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4df05f61c..922170a95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,7 +106,7 @@ jobs: - name: Flutter Build Dev Apk if: ${{ github.event_name == 'pull_request' }} run: | - flutter build apk --release --split-per-abi --android-project-arg dev=1 --pub + flutter build apk --release --split-per-abi --android-project-arg dev=1 --pub - name: Rename run: | diff --git a/analysis_options.yaml b/analysis_options.yaml index 76b083962..143015c4c 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -12,7 +12,6 @@ include: package:flutter_lints/flutter.yaml analyzer: exclude: - lib/grpc/bilibili/** - # - lib/grpc/google/** # - lib/common/widgets/flutter/** formatter: diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 8128334d6..6afd41c78 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") } @@ -18,12 +17,6 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlin { - compilerOptions { - jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) - } - } - defaultConfig { applicationId = "com.example.piliplus" minSdk = flutter.minSdkVersion @@ -82,6 +75,12 @@ android { } } +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} + flutter { source = "../.." } 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 a32d8fb67..b2878bbdf 100644 --- a/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/piliplus/MainActivity.kt @@ -21,6 +21,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 import kotlin.math.roundToInt class MainActivity : AudioServiceActivity() { @@ -183,6 +184,22 @@ class MainActivity : AudioServiceActivity() { result.success(isFoldable) } + "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/build.gradle.kts b/android/build.gradle.kts index be844ccc8..59b31be82 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -43,14 +43,14 @@ subprojects { val pluginCompileSdk = pluginCompileSdkStr ?.removePrefix("android-") ?.toIntOrNull() - if (pluginCompileSdk != null && pluginCompileSdk < 31) { + if (pluginCompileSdk != null && pluginCompileSdk < 36) { project.logger.error( "Warning: Overriding compileSdk version in Flutter plugin: ${project.name} " + - "from $pluginCompileSdk to 31 (to work around https://issuetracker.google.com/issues/199180389).\n" + + "from $pluginCompileSdk to 36 (to work around https://issuetracker.google.com/issues/199180389).\n" + "If there is not a new version of ${project.name}, consider filing an issue against ${project.name} " + "to increase their compileSdk to the latest (otherwise try updating to the latest version)." ) - androidExtension.setCompileSdkVersion(31) + androidExtension.setCompileSdkVersion(36) } } 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/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 82282519d..db0a6f9fe 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index 8de0af82b..c21f0c5b4 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -19,8 +19,8 @@ pluginManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" - id("com.android.application") version "8.12.1" apply false - id("org.jetbrains.kotlin.android") version "2.2.20" apply false + id("com.android.application") version "9.0.1" apply false + id("org.jetbrains.kotlin.android") version "2.3.20" apply false } include(":app") diff --git a/lib/common/widgets/cropped_image.dart b/lib/common/widgets/cropped_image.dart index 46ae1ed10..46123930b 100644 --- a/lib/common/widgets/cropped_image.dart +++ b/lib/common/widgets/cropped_image.dart @@ -70,20 +70,14 @@ class CroppedImage extends LeafRenderObjectWidget { class RenderCroppedImage extends RenderBox { RenderCroppedImage({ - required Size preferredSize, - required ui.Image image, - required Rect srcRect, - required Rect dstRect, - required RRect rrect, - required Paint imgPaint, - required Paint borderPaint, - }) : _preferredSize = preferredSize, - _image = image, - _srcRect = srcRect, - _dstRect = dstRect, - _rrect = rrect, - _imgPaint = imgPaint, - _borderPaint = borderPaint; + required this._preferredSize, + required this._image, + required this._srcRect, + required this._dstRect, + required this._rrect, + required this._imgPaint, + required this._borderPaint, + }); Size _preferredSize; Size get preferredSize => _preferredSize; diff --git a/lib/common/widgets/custom_arc.dart b/lib/common/widgets/custom_arc.dart index 98c5e2f34..2f728c561 100644 --- a/lib/common/widgets/custom_arc.dart +++ b/lib/common/widgets/custom_arc.dart @@ -41,14 +41,11 @@ class Arc extends LeafRenderObjectWidget { class RenderArc extends RenderBox { RenderArc({ - required double preferredSize, - required Color color, - required double progress, - required double strokeWidth, - }) : _preferredSize = preferredSize, - _color = color, - _progress = progress, - _strokeWidth = strokeWidth; + required this._preferredSize, + required this._color, + required this._progress, + required this._strokeWidth, + }); Color _color; Color get color => _color; diff --git a/lib/common/widgets/custom_height_widget.dart b/lib/common/widgets/custom_height_widget.dart index b6958c7ed..64bc5effe 100644 --- a/lib/common/widgets/custom_height_widget.dart +++ b/lib/common/widgets/custom_height_widget.dart @@ -34,10 +34,9 @@ class CustomHeightWidget extends SingleChildRenderObjectWidget { class RenderCustomHeightWidget extends RenderProxyBox { RenderCustomHeightWidget({ - double? height, - required Offset offset, - }) : _height = height, - _offset = offset; + this._height, + required this._offset, + }); double? _height; double? get height => _height; diff --git a/lib/common/widgets/custom_icon.dart b/lib/common/widgets/custom_icon.dart index e7c17d079..00117db15 100644 --- a/lib/common/widgets/custom_icon.dart +++ b/lib/common/widgets/custom_icon.dart @@ -1,47 +1,46 @@ // ignore_for_file: constant_identifier_names -import 'package:flutter/widgets.dart'; +import 'package:flutter/widgets.dart' show IconData; -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); -} +// dart format off +abstract final class CustomIcons { + static const _kFontFam = 'custom_icon'; -class _CustomIconData extends IconData { - const _CustomIconData(super.codePoint) : super(fontFamily: 'custom_icon'); + static const IconData ai_circle = IconData(0xe800, fontFamily: _kFontFam); + static const IconData coin = IconData(0xe801, fontFamily: _kFontFam); + static const IconData dm_off = IconData(0xe802, fontFamily: _kFontFam); + static const IconData dm_on = IconData(0xe803, fontFamily: _kFontFam); + static const IconData dm_settings = IconData(0xe804, fontFamily: _kFontFam); + static const IconData dyn = IconData(0xe805, fontFamily: _kFontFam); + static const IconData fav = IconData(0xe806, fontFamily: _kFontFam); + static const IconData flip_rotate_90 = IconData(0xe807, fontFamily: _kFontFam); + static const IconData identifier_circle = IconData(0xe808, fontFamily: _kFontFam); + static const IconData live_reserve = IconData(0xe809, fontFamily: _kFontFam); + static const IconData open_in_full_rotate_45 = IconData(0xe80a, fontFamily: _kFontFam); + static const IconData player_dm_tip_back = IconData(0xe80b, fontFamily: _kFontFam); + static const IconData player_dm_tip_copy = IconData(0xe80c, fontFamily: _kFontFam); + static const IconData player_dm_tip_like = IconData(0xe80d, fontFamily: _kFontFam); + static const IconData player_dm_tip_like_solid = IconData(0xe80e, fontFamily: _kFontFam); + static const IconData player_dm_tip_recall = IconData(0xe80f, fontFamily: _kFontFam); + static const IconData repeat_rounded_rotate_90 = IconData(0xe810, fontFamily: _kFontFam); + static const IconData share = IconData(0xe811, fontFamily: _kFontFam); + static const IconData share_line = IconData(0xe812, fontFamily: _kFontFam); + static const IconData share_node = IconData(0xe813, fontFamily: _kFontFam); + static const IconData shield_play_arrow = IconData(0xe814, fontFamily: _kFontFam); + static const IconData shield_published = IconData(0xe815, fontFamily: _kFontFam); + static const IconData shield_reply = IconData(0xe816, fontFamily: _kFontFam); + static const IconData shopping_bag_not_interested = IconData(0xe817, fontFamily: _kFontFam); + static const IconData splitscreen_rotate_90 = IconData(0xe818, fontFamily: _kFontFam); + static const IconData star_favorite_line = IconData(0xe819, fontFamily: _kFontFam); + static const IconData star_favorite_solid = IconData(0xe81a, fontFamily: _kFontFam); + static const IconData thumbs_down = IconData(0xe81b, fontFamily: _kFontFam); + static const IconData thumbs_down_outline = IconData(0xe81c, fontFamily: _kFontFam); + static const IconData thumbs_up = IconData(0xe81d, fontFamily: _kFontFam); + static const IconData thumbs_up_fill = IconData(0xe81e, fontFamily: _kFontFam); + static const IconData thumbs_up_line = IconData(0xe81f, fontFamily: _kFontFam); + static const IconData thumbs_up_outline = IconData(0xe820, fontFamily: _kFontFam); + static const IconData topic_tag = IconData(0xe821, fontFamily: _kFontFam); + static const IconData touch_app_rotate_270 = IconData(0xe822, fontFamily: _kFontFam); + static const IconData view_headline_rotate_90 = IconData(0xe823, fontFamily: _kFontFam); + static const IconData watch_later = IconData(0xe824, fontFamily: _kFontFam); } diff --git a/lib/common/widgets/custom_tooltip.dart b/lib/common/widgets/custom_tooltip.dart index b5d22914b..e5b81b8a7 100644 --- a/lib/common/widgets/custom_tooltip.dart +++ b/lib/common/widgets/custom_tooltip.dart @@ -162,11 +162,9 @@ class _RenderToolTip extends RenderBox RenderBoxContainerDefaultsMixin { _RenderToolTip({ VoidCallback? onTap, - required Offset target, - required bool preferBelow, - }) : _target = target, - _preferBelow = preferBelow, - _hitTestSelf = onTap != null { + required this._target, + required this._preferBelow, + }) : _hitTestSelf = onTap != null { if (onTap != null) { _tapGestureRecognizer = TapGestureRecognizer()..onTap = onTap; } @@ -287,10 +285,9 @@ class Triangle extends LeafRenderObjectWidget { class RenderTriangle extends RenderBox { RenderTriangle({ - required Color color, - required Size preferredSize, - }) : _color = color, - _preferredSize = preferredSize; + required this._color, + required this._preferredSize, + }); Color _color; Color get color => _color; diff --git a/lib/common/widgets/disabled_icon.dart b/lib/common/widgets/disabled_icon.dart index 3f38bc222..29d8fe359 100644 --- a/lib/common/widgets/disabled_icon.dart +++ b/lib/common/widgets/disabled_icon.dart @@ -50,16 +50,12 @@ class DisabledIcon extends SingleChildRenderObjectWidget { class RenderMaskedIcon extends RenderProxyBox { RenderMaskedIcon({ - required bool disable, - required double iconSize, - required Color color, - required StrokeCap strokeCap, - required double lineLengthScale, - }) : _disable = disable, - _iconSize = iconSize, - _color = color, - _strokeCap = strokeCap, - _lineLengthScale = lineLengthScale; + required this._disable, + required this._iconSize, + required this._color, + required this._strokeCap, + required this._lineLengthScale, + }); bool _disable; bool get disable => _disable; diff --git a/lib/common/widgets/draggable_sheet/topic.dart b/lib/common/widgets/draggable_sheet/topic.dart index 9e627b3b2..bb3693a4e 100644 --- a/lib/common/widgets/draggable_sheet/topic.dart +++ b/lib/common/widgets/draggable_sheet/topic.dart @@ -50,8 +50,8 @@ class _TopicDraggableScrollableSheetScrollController extends _DraggableScrollableSheetScrollController { _TopicDraggableScrollableSheetScrollController({ required super.extent, - double initialScrollOffset = 0.0, - }) : _initialScrollOffset = initialScrollOffset; + this._initialScrollOffset = 0.0, + }); @override double get initialScrollOffset => _initialScrollOffset; diff --git a/lib/common/widgets/extra_hit_test_widget.dart b/lib/common/widgets/extra_hit_test_widget.dart index 356510f75..c1e30612f 100644 --- a/lib/common/widgets/extra_hit_test_widget.dart +++ b/lib/common/widgets/extra_hit_test_widget.dart @@ -18,8 +18,8 @@ class ExtraHitTestWidget extends SingleChildRenderObjectWidget { class RenderExtraHitTestWidget extends RenderProxyBox { RenderExtraHitTestWidget({ - required double width, - }) : _width = width; + required this._width, + }); final double _width; diff --git a/lib/common/widgets/flutter/chat_list_view.dart b/lib/common/widgets/flutter/chat_list_view.dart index 540aaef0c..2d820f9d4 100644 --- a/lib/common/widgets/flutter/chat_list_view.dart +++ b/lib/common/widgets/flutter/chat_list_view.dart @@ -2,11 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + 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 +34,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/draggable_scrollable_sheet.dart b/lib/common/widgets/flutter/draggable_scrollable_sheet.dart index 9ff5370e5..a020b227b 100644 --- a/lib/common/widgets/flutter/draggable_scrollable_sheet.dart +++ b/lib/common/widgets/flutter/draggable_scrollable_sheet.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'dart:math' as math; import 'package:collection/collection.dart'; diff --git a/lib/common/widgets/flutter/layout_builder.dart b/lib/common/widgets/flutter/layout_builder.dart index af7a7a735..9a58f3b4f 100644 --- a/lib/common/widgets/flutter/layout_builder.dart +++ b/lib/common/widgets/flutter/layout_builder.dart @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + 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..79e1aece0 100644 --- a/lib/common/widgets/flutter/list_tile.dart +++ b/lib/common/widgets/flutter/list_tile.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: uri_does_not_exist_in_doc_import +// ignore_for_file: prefer_initializing_formals, uri_does_not_exist_in_doc_import /// @docImport 'card.dart'; /// @docImport 'checkbox.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..ae23e49fb 100644 --- a/lib/common/widgets/flutter/page/page_view.dart +++ b/lib/common/widgets/flutter/page/page_view.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'package:PiliPlus/common/widgets/flutter/page/scrollable.dart'; import 'package:flutter/gestures.dart' show DragStartBehavior, HorizontalDragGestureRecognizer; @@ -99,13 +101,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 +160,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 +194,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 +205,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 +235,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 +456,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 +465,7 @@ class _PageViewState viewportFraction: _controller.viewportFraction, delegate: widget.childrenDelegate, padEnds: widget.padEnds, + allowImplicitScrolling: widget.allowImplicitScrolling, ), ], ); @@ -447,6 +508,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..1abd487d0 100644 --- a/lib/common/widgets/flutter/page/scrollable.dart +++ b/lib/common/widgets/flutter/page/scrollable.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'dart:async'; import 'dart:math' as math; @@ -948,9 +950,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 +964,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 +972,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/page/scrollable_helpers.dart b/lib/common/widgets/flutter/page/scrollable_helpers.dart index 53a2b2bb7..b68809be3 100644 --- a/lib/common/widgets/flutter/page/scrollable_helpers.dart +++ b/lib/common/widgets/flutter/page/scrollable_helpers.dart @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: dangling_library_doc_comments, prefer_initializing_formals + /// @docImport 'package:flutter/material.dart'; /// /// @docImport 'overscroll_indicator.dart'; /// @docImport 'viewport.dart'; -// ignore_for_file: dangling_library_doc_comments - import 'dart:math' as math; import 'package:PiliPlus/common/widgets/flutter/page/scrollable.dart'; diff --git a/lib/common/widgets/flutter/page/tabs.dart b/lib/common/widgets/flutter/page/tabs.dart index 24be4d084..cd71c0ee4 100644 --- a/lib/common/widgets/flutter/page/tabs.dart +++ b/lib/common/widgets/flutter/page/tabs.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'package:PiliPlus/common/widgets/flutter/page/page_view.dart'; import 'package:flutter/foundation.dart' show clampDouble; import 'package:flutter/gestures.dart' diff --git a/lib/common/widgets/flutter/pop_scope.dart b/lib/common/widgets/flutter/pop_scope.dart index 33cffe2e1..ec0010f62 100644 --- a/lib/common/widgets/flutter/pop_scope.dart +++ b/lib/common/widgets/flutter/pop_scope.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'package:flutter/material.dart' hide PopScope; import 'package:get/get_core/src/get_main.dart'; import 'package:get/get_navigation/src/extension_navigation.dart'; diff --git a/lib/common/widgets/flutter/popup_menu.dart b/lib/common/widgets/flutter/popup_menu.dart index 1e05da9cc..c8e8e8e74 100644 --- a/lib/common/widgets/flutter/popup_menu.dart +++ b/lib/common/widgets/flutter/popup_menu.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library; +// ignore_for_file: prefer_initializing_formals -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide PopupMenuItem; class CustomPopupMenuItem extends PopupMenuEntry { const CustomPopupMenuItem({ @@ -114,7 +114,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 +123,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 d1a5fe944..c350875a7 100644 --- a/lib/common/widgets/flutter/refresh_indicator.dart +++ b/lib/common/widgets/flutter/refresh_indicator.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'dart:async' show Completer; import 'dart:io' show Platform; @@ -517,7 +519,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/sliver_layout_builder.dart b/lib/common/widgets/flutter/sliver_layout_builder.dart index f3f35b773..8d6724a64 100644 --- a/lib/common/widgets/flutter/sliver_layout_builder.dart +++ b/lib/common/widgets/flutter/sliver_layout_builder.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart' diff --git a/lib/common/widgets/flutter/text/paragraph.dart b/lib/common/widgets/flutter/text/paragraph.dart index ff60f8c77..4743ddede 100644 --- a/lib/common/widgets/flutter/text/paragraph.dart +++ b/lib/common/widgets/flutter/text/paragraph.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: uri_does_not_exist_in_doc_import +// ignore_for_file: prefer_initializing_formals, uri_does_not_exist_in_doc_import /// @docImport 'package:flutter/widgets.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/rich_text.dart b/lib/common/widgets/flutter/text/rich_text.dart index 3d82b9bcb..7c1b6e66f 100644 --- a/lib/common/widgets/flutter/text/rich_text.dart +++ b/lib/common/widgets/flutter/text/rich_text.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'dart:ui' as ui show TextHeightBehavior; import 'package:PiliPlus/common/widgets/flutter/text/paragraph.dart'; diff --git a/lib/common/widgets/flutter/text/text.dart b/lib/common/widgets/flutter/text/text.dart index c58b97ff0..846adba89 100644 --- a/lib/common/widgets/flutter/text/text.dart +++ b/lib/common/widgets/flutter/text/text.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: uri_does_not_exist_in_doc_import +// ignore_for_file: prefer_initializing_formals, uri_does_not_exist_in_doc_import /// @docImport 'package:flutter/gestures.dart'; /// @docImport 'package:flutter/material.dart'; 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..aad112abc 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 @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: uri_does_not_exist_in_doc_import +// ignore_for_file: prefer_initializing_formals, uri_does_not_exist_in_doc_import /// @docImport 'selectable_text.dart'; /// @docImport 'selection_area.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/controller.dart b/lib/common/widgets/flutter/text_field/controller.dart index 49cec8776..889dbf8a4 100644 --- a/lib/common/widgets/flutter/text_field/controller.dart +++ b/lib/common/widgets/flutter/text_field/controller.dart @@ -150,20 +150,19 @@ class RichTextItem { RichTextItem({ this.type = RichTextType.text, required this.text, - String? rawText, + this._rawText, required this.range, this.emote, this.id, - }) : _rawText = rawText; + }); RichTextItem.fromStart( this.text, { - String? rawText, + this._rawText, this.type = RichTextType.text, this.emote, this.id, - }) : range = TextRange(start: 0, end: text.length), - _rawText = rawText; + }) : range = TextRange(start: 0, end: text.length); List? onInsert( TextEditingDeltaInsertion delta, 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..499dea6c6 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 @@ -2,11 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + /// @docImport 'package:flutter/material.dart'; 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..1d688428c 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 @@ -2,11 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + /// @docImport 'package:flutter/material.dart'; 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..57f870044 100644 --- a/lib/common/widgets/flutter/text_field/cupertino/text_field.dart +++ b/lib/common/widgets/flutter/text_field/cupertino/text_field.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + /// @docImport 'package:flutter/material.dart'; library; @@ -294,6 +296,7 @@ class CupertinoRichTextField extends StatefulWidget { this.stylusHandwritingEnabled = EditableText.defaultStylusHandwritingEnabled, this.enableIMEPersonalizedLearning = true, + this.enableInlinePrediction, this.contextMenuBuilder = _defaultContextMenuBuilder, this.spellCheckConfiguration, this.magnifierConfiguration, @@ -439,6 +442,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 +803,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 +1123,13 @@ class CupertinoRichTextField extends StatefulWidget { defaultValue: true, ), ) + ..add( + DiagnosticsProperty( + 'enableInlinePrediction', + enableInlinePrediction, + defaultValue: null, + ), + ) ..add( DiagnosticsProperty( 'spellCheckConfiguration', @@ -1738,6 +1752,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..8cce7e999 100644 --- a/lib/common/widgets/flutter/text_field/editable.dart +++ b/lib/common/widgets/flutter/text_field/editable.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + /// @docImport 'package:flutter/cupertino.dart'; library; @@ -19,7 +21,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..ac66f86bf 100644 --- a/lib/common/widgets/flutter/text_field/editable_text.dart +++ b/lib/common/widgets/flutter/text_field/editable_text.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: uri_does_not_exist_in_doc_import +// ignore_for_file: prefer_initializing_formals, uri_does_not_exist_in_doc_import /// @docImport 'package:flutter/cupertino.dart'; /// @docImport 'package:flutter/material.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.dart b/lib/common/widgets/flutter/text_field/spell_check.dart index 649a81151..b1ea2aa20 100644 --- a/lib/common/widgets/flutter/text_field/spell_check.dart +++ b/lib/common/widgets/flutter/text_field/spell_check.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + /// @docImport 'editable_text.dart'; library; 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..5692cc877 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 @@ -2,11 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'package:PiliPlus/common/widgets/flutter/text_field/adaptive_text_selection_toolbar.dart'; 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..ce4de4ea4 100644 --- a/lib/common/widgets/flutter/text_field/system_context_menu.dart +++ b/lib/common/widgets/flutter/text_field/system_context_menu.dart @@ -2,12 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + /// @docImport 'package:flutter/material.dart'; 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..072474c82 100644 --- a/lib/common/widgets/flutter/text_field/text_field.dart +++ b/lib/common/widgets/flutter/text_field/text_field.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: uri_does_not_exist_in_doc_import +// ignore_for_file: prefer_initializing_formals, uri_does_not_exist_in_doc_import /// @docImport 'input_border.dart'; /// @docImport 'material.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/text_field/text_selection.dart b/lib/common/widgets/flutter/text_field/text_selection.dart index f4df4cf31..234be4a84 100644 --- a/lib/common/widgets/flutter/text_field/text_selection.dart +++ b/lib/common/widgets/flutter/text_field/text_selection.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + /// @docImport 'package:flutter/cupertino.dart'; /// @docImport 'package:flutter/material.dart'; library; diff --git a/lib/common/widgets/flutter/vertical_slider.dart b/lib/common/widgets/flutter/vertical_slider.dart index df40eadd6..f87971610 100644 --- a/lib/common/widgets/flutter/vertical_slider.dart +++ b/lib/common/widgets/flutter/vertical_slider.dart @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'dart:async'; 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 +1021,7 @@ class _VerticalSliderState extends State onChangeEnd: _handleDragEnd, state: this, semanticFormatterCallback: widget.semanticFormatterCallback, + onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus, hasFocus: _focused, hovering: _hovering, allowedInteraction: effectiveAllowedInteraction, @@ -1037,22 +1040,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 +1109,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 +1126,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 +1148,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 +1174,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 +1197,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 +1295,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 +1812,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 +1925,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 +1978,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.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; + return Offset( + trackRect.center.dx, + clampDouble( + thumbPosition, + trackRect.top + thumbPadding, + trackRect.bottom - thumbPadding, + ), + ); + } + + 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 +2092,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/flutter/vertical_tabs.dart b/lib/common/widgets/flutter/vertical_tabs.dart index 70ad39945..bc0a5814e 100644 --- a/lib/common/widgets/flutter/vertical_tabs.dart +++ b/lib/common/widgets/flutter/vertical_tabs.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: prefer_initializing_formals + import 'dart:math' as math; import 'dart:ui' show SemanticsRole, lerpDouble; diff --git a/lib/common/widgets/image/cached_network_svg_image.dart b/lib/common/widgets/image/cached_network_svg_image.dart index 7e89032a8..0775612ef 100644 --- a/lib/common/widgets/image/cached_network_svg_image.dart +++ b/lib/common/widgets/image/cached_network_svg_image.dart @@ -13,37 +13,23 @@ class CachedNetworkSVGImage extends StatefulWidget { String url, { Key? key, String? cacheKey, - Widget? placeholder, - WidgetBuilder? errorBuilder, - double? width, - double? height, - Map? headers, - BoxFit fit = BoxFit.contain, - AlignmentGeometry alignment = Alignment.center, - bool matchTextDirection = false, - bool allowDrawingOutsideViewBox = false, - String? semanticsLabel, - bool excludeFromSemantics = false, - SvgTheme theme = const SvgTheme(), - ColorFilter? colorFilter, - WidgetBuilder? placeholderBuilder, + this._placeholder, + this._errorBuilder, + this._width, + this._height, + this._headers, + this._fit = BoxFit.contain, + this._alignment = Alignment.center, + this._matchTextDirection = false, + this._allowDrawingOutsideViewBox = false, + this._semanticsLabel, + this._excludeFromSemantics = false, + this._theme = const SvgTheme(), + this._colorFilter, + this._placeholderBuilder, BaseCacheManager? cacheManager, }) : _url = url, _cacheKey = cacheKey, - _placeholder = placeholder, - _errorBuilder = errorBuilder, - _width = width, - _height = height, - _headers = headers, - _fit = fit, - _alignment = alignment, - _matchTextDirection = matchTextDirection, - _allowDrawingOutsideViewBox = allowDrawingOutsideViewBox, - _semanticsLabel = semanticsLabel, - _excludeFromSemantics = excludeFromSemantics, - _theme = theme, - _colorFilter = colorFilter, - _placeholderBuilder = placeholderBuilder, _cacheManager = cacheManager ?? DefaultCacheManager(), super(key: key ?? ValueKey(cacheKey ?? url)); diff --git a/lib/common/widgets/image_grid/image_grid_builder.dart b/lib/common/widgets/image_grid/image_grid_builder.dart index ddd65f9fe..e8c17a003 100644 --- a/lib/common/widgets/image_grid/image_grid_builder.dart +++ b/lib/common/widgets/image_grid/image_grid_builder.dart @@ -94,11 +94,10 @@ class RenderImageGrid extends RenderBox RenderBoxContainerDefaultsMixin, RenderObjectWithLayoutCallbackMixin { RenderImageGrid({ - required ValueChanged onTap, + required this._onTap, required OnShowMenu? onSecondaryTapUp, required OnShowMenu? onLongPressStart, - }) : _onTap = onTap, - _onSecondaryTapUp = onSecondaryTapUp, + }) : _onSecondaryTapUp = onSecondaryTapUp, _onLongPressStart = onLongPressStart { _tapGestureRecognizer = TapGestureRecognizer()..onTap = _handleOnTap; if (onSecondaryTapUp != null) { diff --git a/lib/common/widgets/image_viewer/loading_indicator.dart b/lib/common/widgets/image_viewer/loading_indicator.dart index 54efaba40..01056a5c8 100644 --- a/lib/common/widgets/image_viewer/loading_indicator.dart +++ b/lib/common/widgets/image_viewer/loading_indicator.dart @@ -55,10 +55,9 @@ class LoadingIndicator extends LeafRenderObjectWidget { class RenderLoadingIndicator extends RenderBox { RenderLoadingIndicator({ - required double preferredSize, - required double progress, - }) : _preferredSize = preferredSize, - _progress = progress; + required this._preferredSize, + required this._progress, + }); double _preferredSize; double get preferredSize => _preferredSize; diff --git a/lib/common/widgets/loading_widget/m3e_loading_indicator.dart b/lib/common/widgets/loading_widget/m3e_loading_indicator.dart index 52ebffa61..77ade6ffe 100644 --- a/lib/common/widgets/loading_widget/m3e_loading_indicator.dart +++ b/lib/common/widgets/loading_widget/m3e_loading_indicator.dart @@ -175,15 +175,12 @@ class RawM3ELoadingIndicator extends LeafRenderObjectWidget { class RenderM3ELoadingIndicator extends RenderBox { RenderM3ELoadingIndicator({ - required Morph morph, - required double progress, - required double angle, + required this._morph, + required this._progress, + required this._angle, required Color color, required Size size, - }) : _morph = morph, - _progress = progress, - _angle = angle, - _preferredSize = size, + }) : _preferredSize = size, _color = color, _paint = Paint() ..style = PaintingStyle.fill diff --git a/lib/common/widgets/marquee.dart b/lib/common/widgets/marquee.dart index 31d9b7002..02393987d 100644 --- a/lib/common/widgets/marquee.dart +++ b/lib/common/widgets/marquee.dart @@ -114,15 +114,13 @@ class BounceMarquee extends Marquee { abstract class MarqueeRender extends RenderBox with RenderObjectWithChildMixin { MarqueeRender({ - required Axis direction, - required double velocity, + required this._direction, + required this._velocity, required double spacing, required this.clipBehavior, required ContextSingleTicker provider, }) : _ticker = provider, _spacing = spacing, - _velocity = velocity, - _direction = direction, assert(spacing.isFinite && !spacing.isNaN); Clip clipBehavior; diff --git a/lib/common/widgets/progress_bar/audio_video_progress_bar.dart b/lib/common/widgets/progress_bar/audio_video_progress_bar.dart index 642bd6deb..e3bf9cd19 100644 --- a/lib/common/widgets/progress_bar/audio_video_progress_bar.dart +++ b/lib/common/widgets/progress_bar/audio_video_progress_bar.dart @@ -321,37 +321,27 @@ class _EagerHorizontalDragGestureRecognizer class RenderProgressBar extends RenderBox implements MouseTrackerAnnotation { RenderProgressBar({ required Duration progress, - required Duration total, - required Duration buffered, - ValueChanged? onSeek, + required this._total, + required this._buffered, + this._onSeek, ThumbDragStartCallback? onDragStart, ThumbDragUpdateCallback? onDragUpdate, VoidCallback? onDragEnd, - required double barHeight, - required Color baseBarColor, - required Color progressBarColor, - required Color bufferedBarColor, + required this._barHeight, + required this._baseBarColor, + required this._progressBarColor, + required this._bufferedBarColor, double thumbRadius = 20.0, - required Color thumbColor, - required Color thumbGlowColor, + required this._thumbColor, + required this._thumbGlowColor, double thumbGlowRadius = 30.0, - bool thumbCanPaintOutsideBar = true, - }) : _total = total, - _buffered = buffered, - _onSeek = onSeek, - _onDragStartUserCallback = onDragStart, + this._thumbCanPaintOutsideBar = true, + }) : _onDragStartUserCallback = onDragStart, _onDragUpdateUserCallback = onDragUpdate, _onDragEndUserCallback = onDragEnd, - _barHeight = barHeight, - _baseBarColor = baseBarColor, - _progressBarColor = progressBarColor, - _bufferedBarColor = bufferedBarColor, _thumbRadius = thumbRadius, - _thumbColor = thumbColor, - _thumbGlowColor = thumbGlowColor, _thumbGlowRadius = thumbGlowRadius, _paintThumbGlow = thumbGlowRadius > thumbRadius, - _thumbCanPaintOutsideBar = thumbCanPaintOutsideBar, _hitTestSelf = onDragStart != null { if (onDragStart != null) { _drag = _EagerHorizontalDragGestureRecognizer() diff --git a/lib/common/widgets/progress_bar/segment_progress_bar.dart b/lib/common/widgets/progress_bar/segment_progress_bar.dart index 123040714..e2cbae375 100644 --- a/lib/common/widgets/progress_bar/segment_progress_bar.dart +++ b/lib/common/widgets/progress_bar/segment_progress_bar.dart @@ -353,10 +353,9 @@ abstract class BaseSegmentProgressBar class BaseRenderProgressBar extends RenderBox { BaseRenderProgressBar({ - required double height, - required List segments, - }) : _height = height, - _segments = segments; + required this._height, + required this._segments, + }); double _height; double get height => _height; diff --git a/lib/common/widgets/progress_bar/video_progress_indicator.dart b/lib/common/widgets/progress_bar/video_progress_indicator.dart index 0057eb41a..b64c64504 100644 --- a/lib/common/widgets/progress_bar/video_progress_indicator.dart +++ b/lib/common/widgets/progress_bar/video_progress_indicator.dart @@ -60,16 +60,12 @@ class VideoProgressIndicator extends LeafRenderObjectWidget { class RenderProgressBar extends RenderBox { RenderProgressBar({ - required Color color, - required Color backgroundColor, - required double radius, - required double height, - required double progress, - }) : _color = color, - _backgroundColor = backgroundColor, - _radius = radius, - _height = height, - _progress = progress; + required this._color, + required this._backgroundColor, + required this._radius, + required this._height, + required this._progress, + }); Color _color; Color get color => _color; diff --git a/lib/common/widgets/sliver/sliver_floating_header.dart b/lib/common/widgets/sliver/sliver_floating_header.dart index 5b87b9e8e..000b7ca59 100644 --- a/lib/common/widgets/sliver/sliver_floating_header.dart +++ b/lib/common/widgets/sliver/sliver_floating_header.dart @@ -113,8 +113,8 @@ class _SliverFloatingHeaderWidget extends SingleChildRenderObjectWidget { class RenderSliverFloatingHeader extends RenderSliverSingleBoxAdapter { RenderSliverFloatingHeader({ - required Color backgroundColor, - }) : _backgroundColor = backgroundColor; + required this._backgroundColor, + }); Color _backgroundColor; set backgroundColor(Color value) { diff --git a/lib/common/widgets/sliver/sliver_pinned_dynamic_header.dart b/lib/common/widgets/sliver/sliver_pinned_dynamic_header.dart index 725e32d81..32977ed71 100644 --- a/lib/common/widgets/sliver/sliver_pinned_dynamic_header.dart +++ b/lib/common/widgets/sliver/sliver_pinned_dynamic_header.dart @@ -55,10 +55,9 @@ class SliverPinnedDynamicHeader extends SingleChildRenderObjectWidget { class RenderSliverPinnedDynamicHeader extends RenderSliverSingleBoxAdapter { RenderSliverPinnedDynamicHeader({ - required double minExtent, - required double maxExtent, - }) : _minExtent = minExtent, - _maxExtent = maxExtent; + required this._minExtent, + required this._maxExtent, + }); double _minExtent; double get minExtent => _minExtent; diff --git a/lib/common/widgets/sliver/sliver_pinned_header.dart b/lib/common/widgets/sliver/sliver_pinned_header.dart index 2a1df95a8..2d111e192 100644 --- a/lib/common/widgets/sliver/sliver_pinned_header.dart +++ b/lib/common/widgets/sliver/sliver_pinned_header.dart @@ -47,8 +47,8 @@ class SliverPinnedHeader extends SingleChildRenderObjectWidget { class RenderSliverPinnedHeader extends RenderSliverSingleBoxAdapter { RenderSliverPinnedHeader({ - required Color? backgroundColor, - }) : _backgroundColor = backgroundColor; + required this._backgroundColor, + }); Color? _backgroundColor; set backgroundColor(Color? value) { diff --git a/lib/common/widgets/sliver_wrap.dart b/lib/common/widgets/sliver_wrap.dart index 492a8b8fd..f23b79903 100644 --- a/lib/common/widgets/sliver_wrap.dart +++ b/lib/common/widgets/sliver_wrap.dart @@ -65,11 +65,9 @@ class RenderSliverFixedWrap extends RenderSliverMultiBoxAdaptor { RenderSliverFixedWrap({ required super.childManager, required double mainAxisExtent, - double spacing = 0.0, - double runSpacing = 0.0, - }) : _mainAxisExtent = mainAxisExtent, - _spacing = spacing, - _runSpacing = runSpacing { + this._spacing = 0.0, + this._runSpacing = 0.0, + }) : _mainAxisExtent = mainAxisExtent { assert(mainAxisExtent > 0.0 && mainAxisExtent.isFinite); } diff --git a/lib/main.dart b/lib/main.dart index a29331a11..d9328cc71 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -138,7 +138,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/models_new/followee_votes/vote.dart b/lib/models_new/followee_votes/vote.dart index c33763428..ea42ab2d6 100644 --- a/lib/models_new/followee_votes/vote.dart +++ b/lib/models_new/followee_votes/vote.dart @@ -12,12 +12,11 @@ class FolloweeVote extends Owner { FolloweeVote({ required super.mid, - required String name, - required String face, + required this._name, + required this._face, required this.votes, required this.ctime, - }) : _name = name, - _face = face; + }); factory FolloweeVote.fromJson(Map json) => FolloweeVote( mid: json['uid'], diff --git a/lib/pages/article/widgets/article_ops.dart b/lib/pages/article/widgets/article_ops.dart index 368a42473..fd1d3e19e 100644 --- a/lib/pages/article/widgets/article_ops.dart +++ b/lib/pages/article/widgets/article_ops.dart @@ -13,9 +13,9 @@ import 'package:get/get.dart'; class ArticleOpus extends StatelessWidget { const ArticleOpus({ super.key, - required List? ops, + required this._ops, required this.maxWidth, - }) : _ops = ops; + }); final List? _ops; final double maxWidth; diff --git a/lib/pages/common/publish/publish_route.dart b/lib/pages/common/publish/publish_route.dart index 6d03acae4..632d1aa93 100644 --- a/lib/pages/common/publish/publish_route.dart +++ b/lib/pages/common/publish/publish_route.dart @@ -2,21 +2,16 @@ import 'package:flutter/material.dart'; class PublishRoute extends PopupRoute { PublishRoute({ - required RoutePageBuilder pageBuilder, - bool barrierDismissible = true, - String? barrierLabel, - Color barrierColor = const Color(0x80000000), - Duration transitionDuration = const Duration(milliseconds: 500), - RouteTransitionsBuilder? transitionBuilder, + required this.pageBuilder, + this._barrierDismissible = true, + this._barrierLabel, + this._barrierColor = const Color(0x80000000), + this._transitionDuration = const Duration(milliseconds: 500), + this._transitionBuilder, super.settings, - }) : widget = pageBuilder, - _barrierDismissible = barrierDismissible, - _barrierLabel = barrierLabel, - _barrierColor = barrierColor, - _transitionDuration = transitionDuration, - _transitionBuilder = transitionBuilder; + }); - final RoutePageBuilder widget; + final RoutePageBuilder pageBuilder; @override bool get barrierDismissible => _barrierDismissible; @@ -45,7 +40,7 @@ class PublishRoute extends PopupRoute { return Semantics( scopesRoute: true, explicitChildNodes: true, - child: widget(context, animation, secondaryAnimation), + child: pageBuilder(context, animation, secondaryAnimation), ); } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 69bf88b32..d27437784 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -1018,10 +1018,9 @@ class _BorderIndicator extends LeafRenderObjectWidget { class _RenderBorderIndicator extends RenderBox { _RenderBorderIndicator({ - required Radius radius, - required bool isLeft, - }) : _radius = radius, - _isLeft = isLeft; + required this._radius, + required this._isLeft, + }); Radius _radius; Radius get radius => _radius; diff --git a/lib/pages/member/widget/reserve_button.dart b/lib/pages/member/widget/reserve_button.dart index c19649f87..7d06ca62c 100644 --- a/lib/pages/member/widget/reserve_button.dart +++ b/lib/pages/member/widget/reserve_button.dart @@ -45,9 +45,8 @@ class ReserveButton extends SingleChildRenderObjectWidget { class RenderReserveBtn extends RenderProxyBox { RenderReserveBtn({ required int count, - required Color color, - }) : _count = count, - _color = color { + required this._color, + }) : _count = count { _textPainter = TextPainter( textDirection: .ltr, text: _getTextSpan(count), diff --git a/lib/pages/search/widgets/hot_keyword.dart b/lib/pages/search/widgets/hot_keyword.dart index 20cf6a7e1..eb8856d46 100644 --- a/lib/pages/search/widgets/hot_keyword.dart +++ b/lib/pages/search/widgets/hot_keyword.dart @@ -135,12 +135,10 @@ class _RenderHotKeywordGrid extends RenderBox ContainerRenderObjectMixin, RenderBoxContainerDefaultsMixin { _RenderHotKeywordGrid({ - required int crossAxisCount, - required double mainAxisSpacing, - required double crossAxisSpacing, - }) : _crossAxisCount = crossAxisCount, - _mainAxisSpacing = mainAxisSpacing, - _crossAxisSpacing = crossAxisSpacing; + required this._crossAxisCount, + required this._mainAxisSpacing, + required this._crossAxisSpacing, + }); int _crossAxisCount; int get crossAxisCount => _crossAxisCount; diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 570871bc9..0b48fffd5 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -568,7 +568,6 @@ class PlPlayerController with BlockConfigMixin { if (PlatformUtils.isMobile) { _orientationListener = NativeDeviceOrientationPlatform.instance .onOrientationChanged( - useSensor: Platform.isAndroid, checkIsAutoRotate: checkIsAutoRotate, angleDegrees: Platform.isAndroid ? Pref.angleDegrees : null, ) diff --git a/lib/plugin/pl_player/utils/fullscreen.dart b/lib/plugin/pl_player/utils/fullscreen.dart index 15783f449..ef490e712 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,32 @@ Future? showSystemBar() { return null; } _showSystemBar = true; - return SystemChrome.setEnabledSystemUIMode( + return setEnabledSystemUIMode( Platform.isAndroid && DeviceUtils.sdkInt < 29 ? .manual : .edgeToEdge, overlays: SystemUiOverlay.values, ); } + +// TODO: remove +// https://github.com/flutter/flutter/issues/186723 +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/plugin/pl_player/view/widgets.dart b/lib/plugin/pl_player/view/widgets.dart index d1227d719..a3cec8cb7 100644 --- a/lib/plugin/pl_player/view/widgets.dart +++ b/lib/plugin/pl_player/view/widgets.dart @@ -407,10 +407,9 @@ class _VideoTime extends LeafRenderObjectWidget { class _RenderVideoTime extends RenderBox { _RenderVideoTime({ - required String position, - required String duration, - }) : _position = position, - _duration = duration; + required this._position, + required this._duration, + }); String _duration; set duration(String value) { 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 a9be557aa..1a35c17ea 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"; @@ -63,13 +59,10 @@ switch ($platform.ToLower()) { $patches += $NavigatorPatch } "linux" { - $picks += $ToolTipFix } "macos" { - $picks += $ToolTipFix } "windows" { - $picks += $ToolTipFix } default {} } @@ -105,10 +98,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 c86713f5f..15b61cf27 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: @@ -359,10 +359,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: @@ -629,12 +629,13 @@ packages: source: hosted version: "6.1.5" flutter_inappwebview_android: - dependency: transitive + dependency: "direct overridden" description: - name: flutter_inappwebview_android - sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" - url: "https://pub.dev" - source: hosted + path: flutter_inappwebview_android + ref: "v6.1.5" + resolved-ref: e0e82ff8492bbc77aecc37e3b4d02c0f3e3de40f + url: "https://github.com/bggRGjQaUbCoE/flutter_inappwebview.git" + source: git version: "1.1.3" flutter_inappwebview_internal_annotations: dependency: transitive @@ -1235,10 +1236,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: @@ -1810,10 +1811,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: @@ -2089,5 +2090,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.11.0 <4.0.0" - flutter: "3.41.9" + dart: ">=3.12.0 <4.0.0" + flutter: "3.44.0" diff --git a/pubspec.yaml b/pubspec.yaml index 505b4edb2..a47e2589f 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.12.0" + flutter: 3.44.0 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -171,6 +171,11 @@ dependencies: ref: main dependency_overrides: + flutter_inappwebview_android: + git: + url: https://github.com/bggRGjQaUbCoE/flutter_inappwebview.git + path: flutter_inappwebview_android + ref: v6.1.5 media_kit: git: url: https://github.com/My-Responsitories/media-kit.git