From 3a96cdc0381c874a3303b31e93b412386c6982fb Mon Sep 17 00:00:00 2001 From: ObaidAbdullah16 Date: Sat, 4 Apr 2026 20:15:08 +0530 Subject: [PATCH 1/3] chore(android): regenerate platform scaffolding for Flutter 3.41 --- .metadata | 39 ++- analysis_options.yaml | 28 ++ android/.gitignore | 5 +- android/app/build.gradle | 56 ---- android/app/build.gradle.kts | 44 +++ android/app/src/debug/AndroidManifest.xml | 6 +- android/app/src/main/AndroidManifest.xml | 73 ++--- .../com/example/flood_mobile/MainActivity.kt | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 2202 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1529 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 3225 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 5263 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 7527 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 4 +- android/app/src/main/res/values/styles.xml | 4 +- android/app/src/profile/AndroidManifest.xml | 6 +- android/build.gradle | 31 -- android/build.gradle.kts | 24 ++ android/gradle.properties | 3 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 11 - android/settings.gradle.kts | 26 ++ android_old/.gitignore | 11 + android_old/app/build.gradle | 44 +++ android_old/app/src/debug/AndroidManifest.xml | 7 + android_old/app/src/main/AndroidManifest.xml | 76 +++++ .../app/src/main/ic_launcher-playstore.png | Bin .../flood_mobile/MainActivity.java | 0 .../res/drawable-v21/launch_background.xml | 12 + .../app/src/main/res/drawable/ic_launcher.png | Bin .../res/drawable/ic_launcher_background.xml | 0 .../main/res/drawable/launch_background.xml | 12 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2202 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1529 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3225 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 5263 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 7527 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../app/src/main/res/values-night/styles.xml | 18 ++ .../app/src/main/res/values/styles.xml | 18 ++ .../app/src/profile/AndroidManifest.xml | 7 + android_old/build.gradle | 10 + android_old/gradle.properties | 9 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android_old/settings.gradle | 30 ++ ios/Flutter/ephemeral/flutter_lldb_helper.py | 32 ++ ios/Flutter/ephemeral/flutter_lldbinit | 5 + ios/Runner/AppDelegate.swift | 16 + .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes ios/Runner/Runner-Bridging-Header.h | 1 + ios/Runner/SceneDelegate.swift | 6 + ios/RunnerTests/RunnerTests.swift | 12 + linux/.gitignore | 1 + linux/CMakeLists.txt | 128 ++++++++ linux/flutter/CMakeLists.txt | 88 ++++++ linux/flutter/generated_plugin_registrant.cc | 27 ++ linux/flutter/generated_plugin_registrant.h | 15 + linux/flutter/generated_plugins.cmake | 27 ++ linux/runner/CMakeLists.txt | 26 ++ linux/runner/main.cc | 6 + linux/runner/my_application.cc | 148 +++++++++ linux/runner/my_application.h | 21 ++ macos/RunnerTests/RunnerTests.swift | 12 + pubspec.lock | 110 ++++--- test/widget_test.dart | 30 ++ web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes windows/.gitignore | 17 ++ windows/CMakeLists.txt | 108 +++++++ windows/flutter/CMakeLists.txt | 109 +++++++ .../flutter/generated_plugin_registrant.cc | 23 ++ windows/flutter/generated_plugin_registrant.h | 15 + windows/flutter/generated_plugins.cmake | 27 ++ windows/runner/CMakeLists.txt | 40 +++ windows/runner/Runner.rc | 121 ++++++++ windows/runner/flutter_window.cpp | 71 +++++ windows/runner/flutter_window.h | 33 ++ windows/runner/main.cpp | 43 +++ windows/runner/resource.h | 16 + windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes windows/runner/runner.exe.manifest | 14 + windows/runner/utils.cpp | 65 ++++ windows/runner/utils.h | 19 ++ windows/runner/win32_window.cpp | 288 ++++++++++++++++++ windows/runner/win32_window.h | 102 +++++++ 110 files changed, 2243 insertions(+), 205 deletions(-) create mode 100644 analysis_options.yaml delete mode 100644 android/app/build.gradle create mode 100644 android/app/build.gradle.kts create mode 100644 android/app/src/main/kotlin/com/example/flood_mobile/MainActivity.kt delete mode 100644 android/build.gradle create mode 100644 android/build.gradle.kts delete mode 100644 android/settings.gradle create mode 100644 android/settings.gradle.kts create mode 100644 android_old/.gitignore create mode 100644 android_old/app/build.gradle create mode 100644 android_old/app/src/debug/AndroidManifest.xml create mode 100644 android_old/app/src/main/AndroidManifest.xml rename {android => android_old}/app/src/main/ic_launcher-playstore.png (100%) rename {android => android_old}/app/src/main/java/com/hustlecreatives/flood_mobile/MainActivity.java (100%) create mode 100644 android_old/app/src/main/res/drawable-v21/launch_background.xml rename {android => android_old}/app/src/main/res/drawable/ic_launcher.png (100%) rename {android => android_old}/app/src/main/res/drawable/ic_launcher_background.xml (100%) create mode 100644 android_old/app/src/main/res/drawable/launch_background.xml rename {android => android_old}/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename {android => android_old}/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) create mode 100644 android_old/app/src/main/res/mipmap-hdpi/ic_launcher.png rename {android => android_old}/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png (100%) rename {android => android_old}/app/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) create mode 100644 android_old/app/src/main/res/mipmap-mdpi/ic_launcher.png rename {android => android_old}/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png (100%) rename {android => android_old}/app/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) create mode 100644 android_old/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename {android => android_old}/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png (100%) rename {android => android_old}/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) create mode 100644 android_old/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename {android => android_old}/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png (100%) rename {android => android_old}/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) create mode 100644 android_old/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename {android => android_old}/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png (100%) rename {android => android_old}/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) create mode 100644 android_old/app/src/main/res/values-night/styles.xml create mode 100644 android_old/app/src/main/res/values/styles.xml create mode 100644 android_old/app/src/profile/AndroidManifest.xml create mode 100644 android_old/build.gradle create mode 100644 android_old/gradle.properties create mode 100644 android_old/gradle/wrapper/gradle-wrapper.properties create mode 100644 android_old/settings.gradle create mode 100644 ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 ios/Flutter/ephemeral/flutter_lldbinit create mode 100644 ios/Runner/AppDelegate.swift create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 ios/Runner/Runner-Bridging-Header.h create mode 100644 ios/Runner/SceneDelegate.swift create mode 100644 ios/RunnerTests/RunnerTests.swift create mode 100644 linux/.gitignore create mode 100644 linux/CMakeLists.txt create mode 100644 linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/generated_plugins.cmake create mode 100644 linux/runner/CMakeLists.txt create mode 100644 linux/runner/main.cc create mode 100644 linux/runner/my_application.cc create mode 100644 linux/runner/my_application.h create mode 100644 macos/RunnerTests/RunnerTests.swift create mode 100644 test/widget_test.dart create mode 100644 web/icons/Icon-maskable-192.png create mode 100644 web/icons/Icon-maskable-512.png create mode 100644 windows/.gitignore create mode 100644 windows/CMakeLists.txt create mode 100644 windows/flutter/CMakeLists.txt create mode 100644 windows/flutter/generated_plugin_registrant.cc create mode 100644 windows/flutter/generated_plugin_registrant.h create mode 100644 windows/flutter/generated_plugins.cmake create mode 100644 windows/runner/CMakeLists.txt create mode 100644 windows/runner/Runner.rc create mode 100644 windows/runner/flutter_window.cpp create mode 100644 windows/runner/flutter_window.h create mode 100644 windows/runner/main.cpp create mode 100644 windows/runner/resource.h create mode 100644 windows/runner/resources/app_icon.ico create mode 100644 windows/runner/runner.exe.manifest create mode 100644 windows/runner/utils.cpp create mode 100644 windows/runner/utils.h create mode 100644 windows/runner/win32_window.cpp create mode 100644 windows/runner/win32_window.h diff --git a/.metadata b/.metadata index 80206eb5..51c0bb52 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,42 @@ # This file should be version controlled and should not be manually edited. version: - revision: c5a4b4029c0798f37c4a39b479d7cb75daa7b05c - channel: stable + revision: "db50e20168db8fee486b9abf32fc912de3bc5b6a" + channel: "stable" project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: android + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: ios + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: linux + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: macos + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: web + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + - platform: windows + create_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + base_revision: db50e20168db8fee486b9abf32fc912de3bc5b6a + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..0d290213 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore index 0a741cb4..be3943c9 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -5,7 +5,10 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java +.cxx/ # Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +# See https://flutter.dev/to/reference-keystore key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index 10c07631..00000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,56 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 33 - - defaultConfig { - multiDexEnabled true - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - multiDexEnabled true - applicationId "com.hustlecreatives.flood_mobile" - minSdkVersion 21 - targetSdkVersion 31 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - dependencies { - implementation 'com.android.support:multidex:1.0.3' - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} \ No newline at end of file diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 00000000..7c75f240 --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +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") +} + +android { + namespace = "com.example.flood_mobile" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.flood_mobile" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 7a29cdb1..399f6981 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,6 +1,6 @@ - - diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 17a0aa94..2b21e637 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,35 +1,13 @@ - - - - - - - - - - - - - + - - - - - + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" + /> - - - - - - - - - + android:value="2" /> - \ No newline at end of file + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/flood_mobile/MainActivity.kt b/android/app/src/main/kotlin/com/example/flood_mobile/MainActivity.kt new file mode 100644 index 00000000..b222d3db --- /dev/null +++ b/android/app/src/main/kotlin/com/example/flood_mobile/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.flood_mobile + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 6ea9c0edbafa2feba14f266b3e25823e7510b017..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 100644 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ delta 2201 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 literal 3225 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 LNkvXXu0mjfHu4;J diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index cf5eec1444f9622951f8eaeb9673cb294d96575c..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c 100644 GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 5263 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 F{{e0M3$FkG diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index bee261a330ca635b9323fe6e4ec287cefd275ad4..4d6372eebdb28e45604e46eeda8dd24651419bc0 100644 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 7527 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 DdNf3r diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml index 449a9f93..06952be7 100644 --- a/android/app/src/main/res/values-night/styles.xml +++ b/android/app/src/main/res/values-night/styles.xml @@ -3,14 +3,14 @@ + + + diff --git a/android_old/app/src/main/res/values/styles.xml b/android_old/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..d74aa35c --- /dev/null +++ b/android_old/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android_old/app/src/profile/AndroidManifest.xml b/android_old/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..7a29cdb1 --- /dev/null +++ b/android_old/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android_old/build.gradle b/android_old/build.gradle new file mode 100644 index 00000000..f04fd0e3 --- /dev/null +++ b/android_old/build.gradle @@ -0,0 +1,10 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +tasks.register("clean", Delete) { + delete rootProject.layout.buildDirectory +} \ No newline at end of file diff --git a/android_old/gradle.properties b/android_old/gradle.properties new file mode 100644 index 00000000..6f78cf48 --- /dev/null +++ b/android_old/gradle.properties @@ -0,0 +1,9 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true + +# Force Gradle to use JDK 17 (important on Windows) +org.gradle.java.home=C:\\Program Files\\Eclipse Adoptium\\jdk-17.0.18.8-hotspot + +# Opt out of AGP 9+ new DSL mode (Flutter warning you saw) +android.newDsl=false \ No newline at end of file diff --git a/android_old/gradle/wrapper/gradle-wrapper.properties b/android_old/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..d54cd5d5 --- /dev/null +++ b/android_old/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip \ No newline at end of file diff --git a/android_old/settings.gradle b/android_old/settings.gradle new file mode 100644 index 00000000..74551e4f --- /dev/null +++ b/android_old/settings.gradle @@ -0,0 +1,30 @@ +pluginManagement { + def localPropertiesFile = new File(settingsDir, "local.properties") + def properties = new Properties() + + assert localPropertiesFile.exists() + localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null : "flutter.sdk not set in local.properties" + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + + // Pin plugin versions (prevents accidentally resolving AGP 9+) + plugins { + id "com.android.application" version "8.3.2" + id "org.jetbrains.kotlin.android" version "1.9.24" + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" +} + +include ":app" \ No newline at end of file diff --git a/ios/Flutter/ephemeral/flutter_lldb_helper.py b/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 00000000..a88caf99 --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/ios/Flutter/ephemeral/flutter_lldbinit b/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 00000000..e3ba6fbe --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..c30b367e --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,16 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ios/Runner/SceneDelegate.swift b/ios/Runner/SceneDelegate.swift new file mode 100644 index 00000000..b9ce8ea2 --- /dev/null +++ b/ios/Runner/SceneDelegate.swift @@ -0,0 +1,6 @@ +import Flutter +import UIKit + +class SceneDelegate: FlutterSceneDelegate { + +} diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..86a7c3b1 --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 00000000..d3896c98 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 00000000..5892174d --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,128 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flood_mobile") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.flood_mobile") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..d5bd0164 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..5027a9a1 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,27 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) awesome_notifications_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "AwesomeNotificationsPlugin"); + awesome_notifications_plugin_register_with_registrar(awesome_notifications_registrar); + g_autoptr(FlPluginRegistrar) battery_plus_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "none"); + none_register_with_registrar(battery_plus_linux_registrar); + g_autoptr(FlPluginRegistrar) connectivity_plus_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "none"); + none_register_with_registrar(connectivity_plus_linux_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..e0f0a47b --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..e093c78b --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,27 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + awesome_notifications + battery_plus_linux + connectivity_plus_linux + url_launcher_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/runner/CMakeLists.txt b/linux/runner/CMakeLists.txt new file mode 100644 index 00000000..e97dabc7 --- /dev/null +++ b/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/linux/runner/main.cc b/linux/runner/main.cc new file mode 100644 index 00000000..e7c5c543 --- /dev/null +++ b/linux/runner/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/runner/my_application.cc b/linux/runner/my_application.cc new file mode 100644 index 00000000..88525bd0 --- /dev/null +++ b/linux/runner/my_application.cc @@ -0,0 +1,148 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Called when first Flutter frame received. +static void first_frame_cb(MyApplication* self, FlView* view) { + gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view))); +} + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "flood_mobile"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "flood_mobile"); + } + + gtk_window_set_default_size(window, 1280, 720); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + GdkRGBA background_color; + // Background defaults to black, override it here if necessary, e.g. #00000000 + // for transparent. + gdk_rgba_parse(&background_color, "#000000"); + fl_view_set_background_color(view, &background_color); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + // Show the window when Flutter renders. + // Requires the view to be realized so we can start rendering. + g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), + self); + gtk_widget_realize(GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, + gchar*** arguments, + int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = + my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, "flags", + G_APPLICATION_NON_UNIQUE, nullptr)); +} diff --git a/linux/runner/my_application.h b/linux/runner/my_application.h new file mode 100644 index 00000000..db16367a --- /dev/null +++ b/linux/runner/my_application.h @@ -0,0 +1,21 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, + my_application, + MY, + APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..61f3bd1f --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/pubspec.lock b/pubspec.lock index 9e2e895e..b262371a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: args - sha256: b003c3098049a51720352d219b0bb5f219b60fbfb68e7a4748139a06a5676515 + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.7.0" async: dependency: transitive description: @@ -205,10 +205,10 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" checked_yaml: dependency: transitive description: @@ -225,6 +225,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" clipboard: dependency: "direct main" description: @@ -237,10 +245,10 @@ packages: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" code_builder: dependency: transitive description: @@ -253,10 +261,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.19.1" connectivity_plus: dependency: "direct main" description: @@ -333,10 +341,10 @@ packages: dependency: transitive description: name: coverage - sha256: d2494157c32b303f47dedee955b1479f2979c4ff66934eb7c0def44fd9e0267a + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" url: "https://pub.dev" source: hosted - version: "1.6.1" + version: "1.15.0" crypto: dependency: transitive description: @@ -429,10 +437,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: @@ -633,10 +641,10 @@ packages: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" google_fonts: dependency: "direct main" description: @@ -741,6 +749,30 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" loading_overlay: dependency: "direct main" description: @@ -761,18 +793,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.19" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.13.0" meta: dependency: "direct overridden" description: @@ -841,10 +873,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.1" path_drawing: dependency: transitive description: @@ -1113,7 +1145,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" smooth_page_indicator: dependency: "direct main" description: @@ -1166,18 +1198,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.4" stream_transform: dependency: transitive description: @@ -1222,26 +1254,26 @@ packages: dependency: transitive description: name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7" url: "https://pub.dev" source: hosted - version: "1.24.1" + version: "1.30.0" test_api: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.7.10" test_core: dependency: transitive description: name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.16" timing: dependency: transitive description: @@ -1358,10 +1390,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" video_player: dependency: "direct main" description: @@ -1406,10 +1438,10 @@ packages: dependency: transitive description: name: vm_service - sha256: e7fb6c2282f7631712b69c19d1bff82f3767eea33a2321c14fa59ad67ea391c7 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "9.4.0" + version: "15.0.2" wakelock: dependency: "direct main" description: @@ -1510,10 +1542,10 @@ packages: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.3" sdks: - dart: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.9.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 00000000..73605b91 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flood_mobile/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 00000000..3ca39127 --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(flood_mobile LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flood_mobile") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..903f4899 --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..ebfd39e0 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,23 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + AwesomeNotificationsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AwesomeNotificationsPluginCApi")); + BatteryPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("BatteryPlusWindowsPlugin")); + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..b789e0dd --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,27 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + awesome_notifications + battery_plus_windows + connectivity_plus_windows + url_launcher_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..394917c0 --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 00000000..34a6210a --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "flood_mobile" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "flood_mobile" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "flood_mobile.exe" "\0" + VALUE "ProductName", "flood_mobile" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..955ee303 --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 00000000..f3984643 --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"flood_mobile", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..153653e8 --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 00000000..3a0b4651 --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 00000000..60608d0f --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 00000000..e901dde6 --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ From 5a0ef61b897076875058c61b2ef918e07a3c52c0 Mon Sep 17 00:00:00 2001 From: ObaidAbdullah16 Date: Sun, 5 Apr 2026 03:01:59 +0530 Subject: [PATCH 2/3] Fix Flutter 3 / AGP8 build and dependency compatibility --- .gitignore | 3 + android/app/src/main/AndroidManifest.xml | 16 +- l10n.yaml | 5 + lib/Api/event_handler_api.dart | 73 +- .../power_management_bloc.dart | 12 +- .../notification_controller.dart | 2 +- lib/Pages/home_screen/home_screen.dart | 360 +++++----- .../widgets/rss_feed_home_page.dart | 225 +++--- .../widgets/custom_dialog_animation.dart | 142 ++-- .../widgets/power_management_section.dart | 103 +-- .../torrent_screen/widgets/speed_graph.dart | 48 +- .../torrent_screen/widgets/torrent_tile.dart | 166 ++--- lib/l10n/l10n.dart | 5 +- linux/flutter/generated_plugin_registrant.cc | 12 +- linux/flutter/generated_plugins.cmake | 3 +- macos/Flutter/GeneratedPluginRegistrant.swift | 22 +- pubspec.lock | 662 ++++++++---------- pubspec.yaml | 104 +-- test/unit_test/api_bloc_unit_test.dart | 2 + test/unit_test/language_bloc_unit_test.dart | 3 + test/unit_test/theme_bloc_unit_test.dart | 2 + test/widget_test.dart | 30 - .../torrent_screen_widget_test.dart | 17 +- .../flutter/generated_plugin_registrant.cc | 10 +- windows/flutter/generated_plugins.cmake | 6 +- 25 files changed, 989 insertions(+), 1044 deletions(-) create mode 100644 l10n.yaml delete mode 100644 test/widget_test.dart diff --git a/.gitignore b/.gitignore index 0fa6b675..d23bd9e6 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +/android/build/ +/lib/gen/ diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2b21e637..99b14736 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,8 @@ + android:icon="@mipmap/ic_launcher" + android:enableOnBackInvokedCallback="true"> - - + - - + \ No newline at end of file diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 00000000..d531d191 --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,5 @@ +arb-dir: lib/l10n/arb +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations +output-dir: lib/gen/l10n diff --git a/lib/Api/event_handler_api.dart b/lib/Api/event_handler_api.dart index b643413e..2a611ad6 100644 --- a/lib/Api/event_handler_api.dart +++ b/lib/Api/event_handler_api.dart @@ -1,6 +1,7 @@ import 'dart:collection'; import 'dart:convert'; import 'dart:io'; + import 'package:battery_plus/battery_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/cupertino.dart'; @@ -8,7 +9,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_client_sse/flutter_client_sse.dart'; import 'package:json_patch/json_patch.dart'; -import 'package:wifi_iot/wifi_iot.dart'; + import 'package:flood_mobile/Api/torrent_api.dart'; import 'package:flood_mobile/Blocs/filter_torrent_bloc/filter_torrent_bloc.dart'; import 'package:flood_mobile/Blocs/home_screen_bloc/home_screen_bloc.dart'; @@ -20,7 +21,7 @@ import 'package:flood_mobile/Services/file_size_helper.dart'; String torrentLength = '0'; class EventHandlerApi { - //Sets the transfer rate if the event returned is TRANSFER_SUMMARY_FULL_UPDATE + // Sets the transfer rate if the event returned is TRANSFER_SUMMARY_FULL_UPDATE static void setTransferRate({ required SSEModel model, required BuildContext context, @@ -33,14 +34,14 @@ class EventHandlerApi { .add(SetSpeedEvent(up: upSpeed, down: downSpeed)); } - //Getting full list of torrents + // Getting full list of torrents static void setFullTorrentList({ required SSEModel model, required BuildContext context, }) { Map data = json.decode(model.data ?? ''); if (data.isNotEmpty) { - //Storing the full list of torrent is string to use it through PATCH + // Storing the full list of torrent is string to use it through PATCH BlocProvider.of(context, listen: false) .add(SetTorrentListJsonEvent(newTorrentListJson: data)); print('---SET FULL TORRENT LIST---'); @@ -53,14 +54,14 @@ class EventHandlerApi { print(error.toString()); } } - //Setting the full list of torrent + // Setting the full list of torrent BlocProvider.of(context, listen: false) .add(SetTorrentListEvent(newTorrentList: newTorrentList)); filterDataRephrasor(newTorrentList, context); } } - //Setting notification count + // Setting notification count static void setNotificationCount({ required SSEModel model, required BuildContext context, @@ -68,13 +69,13 @@ class EventHandlerApi { Map data = json.decode(model.data ?? ''); if (data.isNotEmpty) { print('---SET UNREAD NOTIFICATION COUNT---'); - //Set unread notification count + // Set unread notification count BlocProvider.of(context, listen: false) .add(SetUnreadNotificationsEvent(count: data['unread'])); } } - //Updating the full list of torrent + // Updating the full list of torrent static Future updateFullTorrentList({ required SSEModel model, required BuildContext context, @@ -84,21 +85,21 @@ class EventHandlerApi { .state .torrentListJson; - //Parsing the patch in list format + // Parsing the patch in list format List> newTorrentMap = []; List patchList = json.decode(model.data ?? ''); for (var i in patchList) { newTorrentMap.add(i); } - //Applying patch + // Applying patch final newTorrentListJson = JsonPatch.apply( oldTorrentList, newTorrentMap, strict: true, ); - //Updating data in provider + // Updating data in provider BlocProvider.of(context, listen: false) .add(SetTorrentListJsonEvent(newTorrentListJson: newTorrentListJson)); print('---UPDATE TORRENT LIST---'); @@ -124,27 +125,27 @@ class EventHandlerApi { await filterDataRephrasor(torrentList, context); } - //Setting the full list of torrent + // Setting the full list of torrent BlocProvider.of(context, listen: false) .add(SetTorrentListEvent(newTorrentList: torrentList)); final PowerManagementBloc powerManagementBloc = BlocProvider.of(context, listen: false); - //Exit screen on all download finished + // Exit screen on all download finished if (powerManagementBloc.state.shutDownWhenFinishDownload && isAllDownloadFinished(context)) { SystemChannels.platform.invokeMethod('SystemNavigator.pop'); exit(0); } - //Turn off wifi on all download finished + // Turn off wifi on all download finished if (powerManagementBloc.state.shutDownWifi && isAllDownloadFinished(context)) { await turnOffWiFi(powerManagementBloc.state.shutDownWifi); } - //Stop all download on wifi disconnect + // Stop all download on wifi disconnect if (powerManagementBloc.state.wifiOnlyDownload) { final connectivityResult = await Connectivity().checkConnectivity(); if (connectivityResult != ConnectivityResult.wifi) { @@ -174,8 +175,7 @@ class EventHandlerApi { List> trackersSizeList = []; List> tagsSizeList = []; - //For torrent trackerURI - //make a List of Map for trackerURIs and their corresponding size + // For torrent trackerURI try { for (int i = 0; i < torrentList.length; i++) { for (int j = 0; j < torrentList[i].trackerURIs.length; j++) { @@ -190,7 +190,8 @@ class EventHandlerApi { } catch (error) { print(error); } - //make a map of trackerURIs and their corresponding status + + // make a map of trackerURIs and their corresponding status try { filterBloc.state.trackersSizeList.forEach((element) { if (!maptrackerURIs.containsKey(element.keys.first)) { @@ -200,11 +201,10 @@ class EventHandlerApi { ]; } else { maptrackerURIs.update( - element.keys.first, - (value) => [ - value[0] + 1, - value[1] + double.parse(element.values.first) - ]); + element.keys.first, + (value) => + [value[0] + 1, value[1] + double.parse(element.values.first)], + ); } }); maptrackerURIs = SplayTreeMap.from(maptrackerURIs); @@ -213,8 +213,7 @@ class EventHandlerApi { print(error); } - //For torrent tags - //make a List of Map for tags and their corresponding size + // For torrent tags try { for (int i = 0; i < torrentList.length; i++) { if (torrentList[i].tags.isEmpty) { @@ -232,18 +231,18 @@ class EventHandlerApi { } catch (error) { print(error); } - //make a map of tags and their corresponding status + + // make a map of tags and their corresponding status try { filterBloc.state.tagsSizeList.forEach((element) { if (!mapTags.containsKey(element.keys.first)) { mapTags[element.keys.first] = [1, double.parse(element.values.first)]; } else { mapTags.update( - element.keys.first, - (value) => [ - value[0] + 1, - value[1] + double.parse(element.values.first) - ]); + element.keys.first, + (value) => + [value[0] + 1, value[1] + double.parse(element.values.first)], + ); } }); mapTags = SplayTreeMap.from(mapTags); @@ -252,7 +251,7 @@ class EventHandlerApi { print(error); } - //For torrent status + // For torrent status try { for (int i = 0; i < torrentList.length; i++) { for (int j = 0; j < torrentList[i].status.length; j++) { @@ -290,8 +289,16 @@ bool isAllDownloadFinished(BuildContext context) { ); } +/// AGP 8 + modern Android restrictions: +/// Third-party apps cannot reliably enable/disable Wi‑Fi programmatically. +/// The old `wifi_iot` plugin is not AGP-8 compatible, so we disable this behavior. +/// +/// We keep the function so the rest of the app keeps working. Future turnOffWiFi(bool wifiStatus) async { - await WiFiForIoTPlugin.setEnabled(!wifiStatus); + // Previous behavior: + // await WiFiForIoTPlugin.setEnabled(!wifiStatus); + print( + '[PowerManagement] Requested Wi‑Fi toggle = ${!wifiStatus}, but Wi‑Fi toggling is disabled.'); } Future stopAllDownload(BuildContext context) async { diff --git a/lib/Blocs/power_management_bloc/power_management_bloc.dart b/lib/Blocs/power_management_bloc/power_management_bloc.dart index efc87fcf..ed22a012 100644 --- a/lib/Blocs/power_management_bloc/power_management_bloc.dart +++ b/lib/Blocs/power_management_bloc/power_management_bloc.dart @@ -2,7 +2,7 @@ import 'package:battery_plus/battery_plus.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; part 'power_management_event.dart'; part 'power_management_state.dart'; @@ -63,11 +63,15 @@ class PowerManagementBloc emit(state.copyWith(keepRunningBackground: event.keepRunningBackground)); } - void _setKeepCpuAwakeEvent( + Future _setKeepCpuAwakeEvent( SetKeepCpuAwakeEvent event, Emitter emit, - ) { - Wakelock.toggle(enable: event.keepCpuAwake); + ) async { + if (event.keepCpuAwake) { + await WakelockPlus.enable(); + } else { + await WakelockPlus.disable(); + } emit(state.copyWith(keepCpuAwake: event.keepCpuAwake)); } diff --git a/lib/Notifications/notification_controller.dart b/lib/Notifications/notification_controller.dart index a4fabd7b..415c6142 100644 --- a/lib/Notifications/notification_controller.dart +++ b/lib/Notifications/notification_controller.dart @@ -116,7 +116,7 @@ Future createTorrentDownloadNotification( ), abbreviated: true, ), - progress: homeModel.torrentList[id].percentComplete.round(), + progress: homeModel.torrentList[id].percentComplete.roundToDouble(), summary: isPaused ? 'Paused' : 'Downloading', locked: true, autoDismissible: false, diff --git a/lib/Pages/home_screen/home_screen.dart b/lib/Pages/home_screen/home_screen.dart index f0cb01b0..d9b75817 100644 --- a/lib/Pages/home_screen/home_screen.dart +++ b/lib/Pages/home_screen/home_screen.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; + +import 'package:app_links/app_links.dart'; import 'package:awesome_notifications/awesome_notifications.dart'; import 'package:badges/badges.dart'; import 'package:battery_plus/battery_plus.dart'; @@ -10,8 +12,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hidden_drawer_menu/simple_hidden_drawer/simple_hidden_drawer.dart'; -import 'package:uni_links/uni_links.dart'; -import 'package:uri_to_file/uri_to_file.dart'; + import 'package:flood_mobile/Api/auth_api.dart'; import 'package:flood_mobile/Api/client_api.dart'; import 'package:flood_mobile/Api/notifications_api.dart'; @@ -37,10 +38,10 @@ import 'package:flood_mobile/l10n/l10n.dart'; class HomeScreen extends StatefulWidget { final int? themeIndex; - final GlobalKey navigatorKey = - new GlobalKey(); + final GlobalKey navigatorKey = GlobalKey(); HomeScreen({Key? key, required this.themeIndex}) : super(key: key); + @override _HomeScreenState createState() => _HomeScreenState(); } @@ -49,15 +50,22 @@ class _HomeScreenState extends State { File? _file; late String base64; late String directoryDefault; + DateTime timeBackPressed = DateTime.now(); bool isDark = false; + final Battery _battery = Battery(); BatteryState? _batteryState; StreamSubscription? _batteryStateSubscription; + // app_links (replacement for uni_links) + final AppLinks _appLinks = AppLinks(); + StreamSubscription? _uriLinkSub; + Future _updateBatteryState(BatteryState state) async { if (_batteryState == state) return; _batteryState = state; + BlocProvider.of(context, listen: false).add( SetDownloadChargingConnectedEvent(currentBatteryState: _batteryState), ); @@ -65,6 +73,7 @@ class _HomeScreenState extends State { bool isChargingConnected = BlocProvider.of(context) .state .downloadChargingConnected; + if (isChargingConnected && !(_batteryState == BatteryState.charging)) { BlocProvider.of(context, listen: false) .state @@ -80,11 +89,14 @@ class _HomeScreenState extends State { @override void initState() { super.initState(); + AwesomeNotifications().setListeners( onActionReceivedMethod: NotificationController.onActionReceivedMethod, ); + _processInitialUri(); _listenForUri(); + AuthApi.getUsersList(context); BlocProvider.of(context, listen: false) .add(GetPreviousSetUserInterfaceEvent()); @@ -92,41 +104,63 @@ class _HomeScreenState extends State { @override void dispose() { - if (_batteryStateSubscription != null) { - _batteryStateSubscription!.cancel(); - } + _batteryStateSubscription?.cancel(); + _uriLinkSub?.cancel(); super.dispose(); } @override void didChangeDependencies() { - //Initialize the app BlocProvider.of(context, listen: false) .add(SetSSEListenEvent(context: context)); ClientApi.getClientSettings(context); NotificationApi.getNotifications(context: context); + _batteryStateSubscription = _battery.onBatteryStateChanged.listen(_updateBatteryState); + super.didChangeDependencies(); } Future _processInitialUri() async { - String? uriString = await getInitialLink(); - _processUriandAddTorrent(uriString); + try { + final Uri? initialUri = await _appLinks.getInitialLink(); + _processUriandAddTorrent(initialUri?.toString()); + } catch (_) { + // ignore + } } void _listenForUri() { - linkStream.listen((uriString) => _processUriandAddTorrent(uriString)); + _uriLinkSub = _appLinks.uriLinkStream.listen( + (Uri uri) => _processUriandAddTorrent(uri.toString()), + onError: (_) { + // ignore + }, + ); + } + + File _fileFromUriString(String uriString) { + final uri = Uri.parse(uriString); + + // Most common case: file://... uri + if (uri.scheme == 'file') { + return File.fromUri(uri); + } + + // Fallback: treat it as a normal path (some sources may give a raw path) + return File(uriString); } Future _processUriandAddTorrent(String? uriString) async { try { if (uriString != null) { - _file = await toFile(uriString); + _file = _fileFromUriString(uriString); List imageBytes = _file!.readAsBytesSync(); setState(() { base64 = base64Encode(imageBytes); }); + showModalBottomSheet( shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( @@ -170,162 +204,160 @@ class _HomeScreenState extends State { Widget build(BuildContext context) { final mediaQuery = MediaQuery.of(context); return DarkTransition( - themeIndex: widget.themeIndex ?? 2, - isDark: isDark, - offset: Offset(mediaQuery.viewPadding.left + 115 + 23, - mediaQuery.viewPadding.top + 35 + 25), - duration: const Duration(milliseconds: 800), - childBuilder: (context, int themeIndex, bool needsSetup, int position, - Function(int) updatePosition) { - return KeyboardDismissOnTap( - child: SimpleHiddenDrawer( - withShadow: true, - slidePercent: 80, - initPositionSelected: position, - contentCornerRadius: 40, - menu: Menu( - toggleTheme: toggleTheme, - themeIndex: themeIndex, - updatePosition: updatePosition), - screenSelectedBuilder: (_, controller) { - Widget screenCurrent = Container(); - switch (position) { - case 0: - screenCurrent = TorrentScreen(themeIndex: themeIndex); - break; - case 1: - screenCurrent = TorrentScreen(themeIndex: themeIndex); - break; - case 2: - screenCurrent = SettingsScreen(themeIndex: themeIndex); - break; - case 4: - screenCurrent = AboutScreen(themeIndex: themeIndex); - break; - } - if (needsSetup) { - SchedulerBinding.instance.addPostFrameCallback((_) { - controller.open(); - }); - } - - return BlocBuilder( - builder: (context, homeScreenState) { - return BlocBuilder( - builder: (context, state) { - final selectedTorrent = - BlocProvider.of(context, - listen: false); - return WillPopScope( - onWillPop: () => - onBackPressed(timeBackPressed, context), - child: Scaffold( - appBar: AppBar( - key: Key('AppBar $themeIndex'), - leading: !selectedTorrent.state.isSelectionMode - ? IconButton( - icon: Icon( - Icons.menu, - key: Key('Menu Icon $themeIndex'), - color: ThemeBloc.theme(themeIndex) - .textTheme - .bodyLarge - ?.color, - ), - onPressed: () { - controller.toggle(); - }, - ) - : IconButton( - onPressed: () { - setState(() { - selectedTorrent - .add(ChangeSelectionModeEvent()); - selectedTorrent.add( - RemoveAllItemsFromListEvent()); - selectedTorrent.add( - RemoveAllIndexFromListEvent()); - }); - }, - icon: Icon(Icons.close)), - title: Image( - key: Key('Flood Icon $themeIndex'), - image: AssetImage( - 'assets/images/icon.png', - ), - width: 60, - height: 60, - ), - centerTitle: true, - backgroundColor: - ThemeBloc.theme(themeIndex).primaryColor, - elevation: 0, - actions: [ - if (!selectedTorrent.state.isSelectionMode) - RSSFeedButtonWidget(themeIndex: themeIndex), - if (!selectedTorrent.state.isSelectionMode) - Badge( - showBadge: - homeScreenState.unreadNotifications == 0 - ? false - : true, - key: Key('Badge Widget $themeIndex'), - badgeColor: ThemeBloc.theme(themeIndex) - .colorScheme - .secondary, - badgeContent: Center( - child: Text( - homeScreenState.unreadNotifications - .toString(), - style: TextStyle(color: Colors.white), - ), - ), - position: BadgePosition(top: 0, end: 3), - child: IconButton( - key: Key( - 'Notification Button $themeIndex'), - icon: Icon( - Icons.notifications, - ), - onPressed: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - key: Key( - 'Notification Alert Dialog $themeIndex'), - elevation: 0, - backgroundColor: - ThemeBloc.theme(themeIndex) - .primaryColor, - content: - NotificationPopupDialogueContainer( - themeIndex: themeIndex, - ), - ); - }, - ); - }, + themeIndex: widget.themeIndex ?? 2, + isDark: isDark, + offset: Offset( + mediaQuery.viewPadding.left + 115 + 23, + mediaQuery.viewPadding.top + 35 + 25, + ), + duration: const Duration(milliseconds: 800), + childBuilder: (context, int themeIndex, bool needsSetup, int position, + Function(int) updatePosition) { + return KeyboardDismissOnTap( + child: SimpleHiddenDrawer( + withShadow: true, + slidePercent: 80, + initPositionSelected: position, + contentCornerRadius: 40, + menu: Menu( + toggleTheme: toggleTheme, + themeIndex: themeIndex, + updatePosition: updatePosition, + ), + screenSelectedBuilder: (_, controller) { + Widget screenCurrent = Container(); + switch (position) { + case 0: + screenCurrent = TorrentScreen(themeIndex: themeIndex); + break; + case 1: + screenCurrent = TorrentScreen(themeIndex: themeIndex); + break; + case 2: + screenCurrent = SettingsScreen(themeIndex: themeIndex); + break; + case 4: + screenCurrent = AboutScreen(themeIndex: themeIndex); + break; + } + if (needsSetup) { + SchedulerBinding.instance.addPostFrameCallback((_) { + controller.open(); + }); + } + + return BlocBuilder( + builder: (context, homeScreenState) { + return BlocBuilder( + builder: (context, state) { + final selectedTorrent = + BlocProvider.of(context, + listen: false); + return WillPopScope( + onWillPop: () => + onBackPressed(timeBackPressed, context), + child: Scaffold( + appBar: AppBar( + key: Key('AppBar $themeIndex'), + leading: !selectedTorrent.state.isSelectionMode + ? IconButton( + icon: Icon( + Icons.menu, + key: Key('Menu Icon $themeIndex'), + color: ThemeBloc.theme(themeIndex) + .textTheme + .bodyLarge + ?.color, ), - ), - if (selectedTorrent.state.isSelectionMode) - PopupMenuButtons( - themeIndex: themeIndex, + onPressed: () { + controller.toggle(); + }, ) - ], + : IconButton( + onPressed: () { + setState(() { + selectedTorrent + .add(ChangeSelectionModeEvent()); + selectedTorrent + .add(RemoveAllItemsFromListEvent()); + selectedTorrent + .add(RemoveAllIndexFromListEvent()); + }); + }, + icon: Icon(Icons.close), + ), + title: Image( + key: Key('Flood Icon $themeIndex'), + image: AssetImage('assets/images/icon.png'), + width: 60, + height: 60, ), - body: screenCurrent, + centerTitle: true, + backgroundColor: + ThemeBloc.theme(themeIndex).primaryColor, + elevation: 0, + actions: [ + if (!selectedTorrent.state.isSelectionMode) + RSSFeedButtonWidget(themeIndex: themeIndex), + if (!selectedTorrent.state.isSelectionMode) + Badge( + showBadge: + homeScreenState.unreadNotifications == 0 + ? false + : true, + key: Key('Badge Widget $themeIndex'), + badgeColor: ThemeBloc.theme(themeIndex) + .colorScheme + .secondary, + badgeContent: Center( + child: Text( + homeScreenState.unreadNotifications + .toString(), + style: TextStyle(color: Colors.white), + ), + ), + position: BadgePosition(top: 0, end: 3), + child: IconButton( + key: Key('Notification Button $themeIndex'), + icon: Icon(Icons.notifications), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + key: Key( + 'Notification Alert Dialog $themeIndex'), + elevation: 0, + backgroundColor: + ThemeBloc.theme(themeIndex) + .primaryColor, + content: + NotificationPopupDialogueContainer( + themeIndex: themeIndex, + ), + ); + }, + ); + }, + ), + ), + if (selectedTorrent.state.isSelectionMode) + PopupMenuButtons(themeIndex: themeIndex) + ], ), - ); - }, - ); - }, - ); - }, - ), - ); - }); + body: screenCurrent, + ), + ); + }, + ); + }, + ); + }, + ), + ); + }, + ); } } diff --git a/lib/Pages/home_screen/widgets/rss_feed_home_page.dart b/lib/Pages/home_screen/widgets/rss_feed_home_page.dart index 487091c2..e754adad 100644 --- a/lib/Pages/home_screen/widgets/rss_feed_home_page.dart +++ b/lib/Pages/home_screen/widgets/rss_feed_home_page.dart @@ -1,6 +1,5 @@ import 'package:clipboard/clipboard.dart'; import 'package:contained_tab_bar_view/contained_tab_bar_view.dart'; -import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flood_mobile/Api/client_api.dart'; @@ -491,12 +490,10 @@ class _RSSFeedHomePageState extends State mainAxisAlignment: MainAxisAlignment.center, children: [ - DropdownButtonFormField2( + DropdownButtonFormField( key: Key( 'Interval type dropdown'), decoration: InputDecoration( - //Add isDense true and zero Padding. - //Add Horizontal padding using buttonPadding and Vertical padding by increasing buttonHeight instead of add Padding here so that The whole TextFormField Button become clickable, and also the dropdown menu open under The whole TextFormField Button. isDense: true, contentPadding: EdgeInsets.zero, @@ -505,22 +502,22 @@ class _RSSFeedHomePageState extends State BorderRadius.circular( 5), ), - //Add more decoration as you want here - //Add label If you want but add hint outside the decoration to be aligned in the button perfectly. ), isExpanded: true, hint: Text( context .l10n.feeds_interval_unit, style: TextStyle( - color: ThemeBloc.theme( - widget.themeIndex) - .textTheme - .bodyLarge - ?.color), + color: ThemeBloc.theme( + widget.themeIndex) + .textTheme + .bodyLarge + ?.color, + ), ), - value: context - .l10n.feeds_time_minutes, + value: selectedValue ?? + context.l10n + .feeds_time_minutes, icon: Icon( Icons.keyboard_arrow_down, color: ThemeBloc.theme( @@ -529,41 +526,19 @@ class _RSSFeedHomePageState extends State .bodyLarge ?.color, ), - buttonHeight: 58, - buttonPadding: - const EdgeInsets.only( - left: 20, right: 10), - dropdownDecoration: - BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.black12 - .withOpacity(0.5), - spreadRadius: 5, - blurRadius: 7, - offset: Offset(0, - 3), // changes position of shadow - ), - ], - borderRadius: - BorderRadius.circular(5), - color: ThemeBloc.theme( - widget.themeIndex) - .primaryColorLight, - ), items: intervalunits - .map((item) => - DropdownMenuItem< - String>( - value: item, - child: Text( - item, + .map( + (item) => + DropdownMenuItem< + String>( + value: item, + child: Text(item, style: const TextStyle( - fontSize: 14, - ), - ), - )) + fontSize: + 14)), + ), + ) .toList(), validator: (value) { if (value == null) { @@ -573,7 +548,9 @@ class _RSSFeedHomePageState extends State return null; }, onChanged: (value) { - if (value.toString() == + if (value == null) return; + + if (value == l10n.feeds_time_hours) { intervalController .text = (int.parse( @@ -582,7 +559,7 @@ class _RSSFeedHomePageState extends State 60) .toString(); } - if (value.toString() == + if (value == l10n.feeds_time_days) { intervalController .text = (int.parse( @@ -592,10 +569,12 @@ class _RSSFeedHomePageState extends State 60) .toString(); } - selectedValue = - value.toString(); + + setState(() { + selectedValue = value; + }); }, - ), + ) ], ), ), @@ -830,11 +809,9 @@ class _RSSFeedHomePageState extends State child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - DropdownButtonFormField2( + DropdownButtonFormField( key: Key('Browse feeds dropdown'), decoration: InputDecoration( - //Add isDense true and zero Padding. - //Add Horizontal padding using buttonPadding and Vertical padding by increasing buttonHeight instead of add Padding here so that The whole TextFormField Button become clickable, and also the dropdown menu open under The whole TextFormField Button. isDense: true, contentPadding: EdgeInsets.zero, border: OutlineInputBorder( @@ -848,18 +825,18 @@ class _RSSFeedHomePageState extends State ?.color, size: 25, ), - //Add more decoration as you want here - //Add label If you want but add hint outside the decoration to be aligned in the button perfectly. ), isExpanded: true, hint: Text( l10n.feeds_select_feed, style: TextStyle( - color: ThemeBloc.theme(widget.themeIndex) - .textTheme - .bodyLarge - ?.color), + color: ThemeBloc.theme(widget.themeIndex) + .textTheme + .bodyLarge + ?.color, + ), ), + value: browsefeedSelected, icon: Icon( Icons.keyboard_arrow_down, color: ThemeBloc.theme(widget.themeIndex) @@ -867,33 +844,15 @@ class _RSSFeedHomePageState extends State .bodyLarge ?.color, ), - buttonHeight: 58, - buttonPadding: - const EdgeInsets.only(left: 20, right: 10), - dropdownDecoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.black12.withOpacity(0.5), - spreadRadius: 5, - blurRadius: 7, - offset: Offset( - 0, 3), // changes position of shadow - ), - ], - borderRadius: BorderRadius.circular(5), - color: ThemeBloc.theme(widget.themeIndex) - .primaryColorLight, - ), items: feedlabelgetter(state.rssFeedsList) - .map((item) => DropdownMenuItem( - value: item, - child: Text( - item, - style: const TextStyle( - fontSize: 14, - ), - ), - )) + .map( + (item) => DropdownMenuItem( + value: item.toString(), + child: Text(item.toString(), + style: + const TextStyle(fontSize: 14)), + ), + ) .toList(), validator: (value) { if (value == null) { @@ -902,15 +861,18 @@ class _RSSFeedHomePageState extends State return null; }, onChanged: (value) { - isbrowseFeedsContentSelected = true; - browsefeedSelected = value.toString(); + if (value == null) return; + setState(() { + isbrowseFeedsContentSelected = true; + browsefeedSelected = value; + }); + FeedsContentsApi.listAllFeedsContents( - context: context, - id: feedidgetter( - browsefeedSelected.toString(), - state.rssFeedsList)); + context: context, + id: feedidgetter(value, state.rssFeedsList), + ); }, - ), + ) ], ), ), @@ -1808,11 +1770,9 @@ class _RSSFeedHomePageState extends State mainAxisAlignment: MainAxisAlignment.center, children: [ - DropdownButtonFormField2( - value: applicableFeedSelected, + DropdownButtonFormField( + key: Key('applicable feed dropdown'), decoration: InputDecoration( - //Add isDense true and zero Padding. - //Add Horizontal padding using buttonPadding and Vertical padding by increasing buttonHeight instead of add Padding here so that The whole TextFormField Button become clickable, and also the dropdown menu open under The whole TextFormField Button. isDense: true, contentPadding: EdgeInsets.zero, border: OutlineInputBorder( @@ -1828,20 +1788,19 @@ class _RSSFeedHomePageState extends State ?.color, size: 25, ), - //Add more decoration as you want here - //Add label If you want but add hint outside the decoration to be aligned in the button perfectly. ), - key: Key('applicable feed dropdown'), isExpanded: true, hint: Text( l10n.feeds_applicable_feed, style: TextStyle( - color: ThemeBloc.theme( - widget.themeIndex) - .textTheme - .bodyLarge - ?.color), + color: ThemeBloc.theme( + widget.themeIndex) + .textTheme + .bodyLarge + ?.color, + ), ), + value: applicableFeedSelected, icon: Icon( Icons.keyboard_arrow_down, color: ThemeBloc.theme( @@ -1850,38 +1809,17 @@ class _RSSFeedHomePageState extends State .bodyLarge ?.color, ), - buttonHeight: 58, - buttonPadding: const EdgeInsets.only( - left: 20, right: 10), - dropdownDecoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.black12 - .withOpacity(0.5), - spreadRadius: 5, - blurRadius: 7, - offset: Offset(0, - 3), // changes position of shadow - ), - ], - borderRadius: - BorderRadius.circular(5), - color: ThemeBloc.theme( - widget.themeIndex) - .primaryColorLight, - ), items: feedlabelgetter( state.rssFeedsList) - .map((item) => - DropdownMenuItem( - value: item, - child: Text( - item, + .map( + (item) => + DropdownMenuItem( + value: item.toString(), + child: Text(item.toString(), style: const TextStyle( - fontSize: 14, - ), - ), - )) + fontSize: 14)), + ), + ) .toList(), validator: (value) { if (value == null) { @@ -1891,21 +1829,22 @@ class _RSSFeedHomePageState extends State return null; }, onChanged: (value) { - applicableFeedSelected = - value.toString(); + if (value == null) return; + setState(() { + applicableFeedSelected = value; + }); + FeedsContentsApi .listAllFeedsContents( - context: context, - id: feedidgetter( - applicableFeedSelected - .toString(), - state.rssFeedsList)); + context: context, + id: feedidgetter( + value, state.rssFeedsList), + ); }, onSaved: (value) { - applicableFeedSelected = - value.toString(); + applicableFeedSelected = value; }, - ), + ) ], ), Padding( diff --git a/lib/Pages/login_screen/widgets/custom_dialog_animation.dart b/lib/Pages/login_screen/widgets/custom_dialog_animation.dart index 4b3d3b58..4681b94d 100644 --- a/lib/Pages/login_screen/widgets/custom_dialog_animation.dart +++ b/lib/Pages/login_screen/widgets/custom_dialog_animation.dart @@ -1,13 +1,36 @@ import 'package:flutter/material.dart'; -import 'package:optimize_battery/optimize_battery.dart'; import 'package:flood_mobile/Blocs/theme_bloc/theme_bloc.dart'; import 'package:flood_mobile/l10n/l10n.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:url_launcher/url_launcher.dart'; class CustomDialogAnimation extends StatelessWidget { final int themeIndex; const CustomDialogAnimation({Key? key, required this.themeIndex}) : super(key: key); + + Future _openBatteryOptimizationSettings() async { + // Android intent to Battery Optimization settings + final Uri intentUri = + Uri.parse('android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS'); + + try { + final bool launched = await launchUrl( + intentUri, + mode: LaunchMode.externalApplication, + ); + + if (!launched) { + Fluttertoast.showToast( + msg: 'Unable to open battery optimization settings.'); + } + } catch (_) { + Fluttertoast.showToast( + msg: 'Unable to open battery optimization settings.'); + } + } + @override Widget build(BuildContext context) { return Dialog( @@ -23,16 +46,12 @@ class CustomDialogAnimation extends StatelessWidget { alignment: Alignment.topCenter, children: [ Container( - // Bottom rectangular box - margin: - EdgeInsets.only(top: 40), // to push the box half way below circle + margin: EdgeInsets.only(top: 40), decoration: BoxDecoration( color: ThemeBloc.theme(themeIndex).primaryColorLight, borderRadius: BorderRadius.circular(12), ), - padding: EdgeInsets.only( - top: 0, - ), // spacing inside the box + padding: EdgeInsets.only(top: 0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, @@ -55,38 +74,47 @@ class CustomDialogAnimation extends StatelessWidget { }, ), Center( - child: Padding( - padding: const EdgeInsets.only(left: 8, right: 8, top: 20), - child: Column( - children: [ - new Text(l10n.battery_optimization_dialog_title, + child: Padding( + padding: const EdgeInsets.only(left: 8, right: 8, top: 20), + child: Column( + children: [ + Text( + l10n.battery_optimization_dialog_title, style: TextStyle( - fontSize: 20.0, - color: ThemeBloc.theme(themeIndex) - .textTheme - .bodyLarge - ?.color, - fontWeight: FontWeight.bold)), - SizedBox(height: 10), - Text(l10n.battery_optimization_dialog_desc1, + fontSize: 20.0, + color: ThemeBloc.theme(themeIndex) + .textTheme + .bodyLarge + ?.color, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + Text( + l10n.battery_optimization_dialog_desc1, textAlign: TextAlign.center, style: TextStyle( - fontSize: 14.0, - color: ThemeBloc.theme(themeIndex) - .textTheme - .bodyLarge - ?.color)), - SizedBox(height: 10), - Text(l10n.battery_optimization_dialog_desc2, + fontSize: 14.0, + color: ThemeBloc.theme(themeIndex) + .textTheme + .bodyLarge + ?.color, + ), + ), + SizedBox(height: 10), + Text( + l10n.battery_optimization_dialog_desc2, textAlign: TextAlign.center, style: TextStyle( - fontSize: 11.0, - fontWeight: FontWeight.w500, - color: Colors.black45)) - ], - ), - ) // + fontSize: 11.0, + fontWeight: FontWeight.w500, + color: Colors.black45, + ), + ) + ], ), + ), + ), SizedBox(height: 24.0), InkWell( child: Container( @@ -94,8 +122,9 @@ class CustomDialogAnimation extends StatelessWidget { decoration: BoxDecoration( color: Colors.white54, borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(10.0), - bottomRight: Radius.circular(10.0)), + bottomLeft: Radius.circular(10.0), + bottomRight: Radius.circular(10.0), + ), ), child: Padding( padding: const EdgeInsets.only(), @@ -120,33 +149,37 @@ class CustomDialogAnimation extends StatelessWidget { Text( "${l10n.remove_battery_text} \n${l10n.optimisation_text}", style: TextStyle( - color: Colors.black87, - fontSize: 17.0, - fontWeight: FontWeight.w600), + color: Colors.black87, + fontSize: 17.0, + fontWeight: FontWeight.w600, + ), ), SizedBox(height: 10), Text( l10n.remove_battery_optimization_text, style: TextStyle( - color: Colors.black54, - fontSize: 14.0, - fontWeight: FontWeight.w500), + color: Colors.black54, + fontSize: 14.0, + fontWeight: FontWeight.w500, + ), ), SizedBox(height: 10), Text( l10n.or_text, style: TextStyle( - color: Colors.black, - fontSize: 14.0, - fontWeight: FontWeight.w600), + color: Colors.black, + fontSize: 14.0, + fontWeight: FontWeight.w600, + ), ), SizedBox(height: 10), Text( l10n.remove_battery_optimization_steps, style: TextStyle( - color: Colors.black54, - fontSize: 14.0, - fontWeight: FontWeight.w500), + color: Colors.black54, + fontSize: 14.0, + fontWeight: FontWeight.w500, + ), ), ], ), @@ -166,8 +199,8 @@ class CustomDialogAnimation extends StatelessWidget { padding: const EdgeInsets.only( left: 20.0, right: 20.0, top: 10), child: ElevatedButton( - onPressed: () { - OptimizeBattery.openBatteryOptimizationSettings(); + onPressed: () async { + await _openBatteryOptimizationSettings(); }, style: ElevatedButton.styleFrom( side: BorderSide(width: 0.2, color: Colors.black), @@ -184,9 +217,10 @@ class CustomDialogAnimation extends StatelessWidget { child: Text( l10n.update_batter_settings_button, style: TextStyle( - color: Colors.black, - fontSize: 14.0, - fontWeight: FontWeight.w800), + color: Colors.black, + fontSize: 14.0, + fontWeight: FontWeight.w800, + ), textAlign: TextAlign.center, ), ), @@ -197,8 +231,8 @@ class CustomDialogAnimation extends StatelessWidget { ), ), ), - onTap: () { - OptimizeBattery.openBatteryOptimizationSettings(); + onTap: () async { + await _openBatteryOptimizationSettings(); }, ) ], diff --git a/lib/Pages/settings_screen/widgets/power_management_section.dart b/lib/Pages/settings_screen/widgets/power_management_section.dart index cfeb7340..7cce129a 100644 --- a/lib/Pages/settings_screen/widgets/power_management_section.dart +++ b/lib/Pages/settings_screen/widgets/power_management_section.dart @@ -1,11 +1,12 @@ import 'package:expansion_tile_card/expansion_tile_card.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:optimize_battery/optimize_battery.dart'; import 'package:flood_mobile/Blocs/power_management_bloc/power_management_bloc.dart'; import 'package:flood_mobile/Blocs/theme_bloc/theme_bloc.dart'; import 'package:flood_mobile/Pages/widgets/text_size.dart'; import 'package:flood_mobile/l10n/l10n.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:url_launcher/url_launcher.dart'; // ignore: must_be_immutable class PowerManagementSection extends StatelessWidget { @@ -16,6 +17,26 @@ class PowerManagementSection extends StatelessWidget { required this.themeIndex, }) : super(key: key); + Future _openBatteryOptimizationSettings() async { + final Uri intentUri = + Uri.parse('android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS'); + + try { + final bool launched = await launchUrl( + intentUri, + mode: LaunchMode.externalApplication, + ); + + if (!launched) { + Fluttertoast.showToast( + msg: 'Unable to open battery optimization settings.'); + } + } catch (_) { + Fluttertoast.showToast( + msg: 'Unable to open battery optimization settings.'); + } + } + @override Widget build(BuildContext context) { final AppLocalizations l10n = context.l10n; @@ -130,15 +151,13 @@ class PowerManagementSection extends StatelessWidget { EdgeInsets.symmetric(horizontal: 10, vertical: 5), ), ListTile( - title: Text( - l10n.battery_optimization_title, - ), + title: Text(l10n.battery_optimization_title), subtitle: Text( l10n.battery_optimization_subtitle, style: TextStyle(fontSize: 13), ), - onTap: () { - OptimizeBattery.openBatteryOptimizationSettings(); + onTap: () async { + await _openBatteryOptimizationSettings(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), @@ -147,44 +166,44 @@ class PowerManagementSection extends StatelessWidget { EdgeInsets.symmetric(horizontal: 10, vertical: 5), ), StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return ListTile( - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - l10n.battery_low_title, + builder: (BuildContext context, StateSetter setState) { + return ListTile( + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(l10n.battery_low_title), + Text('${batteryLevel.toStringAsFixed(0)} %'), + ], + ), + subtitle: SliderTheme( + data: SliderThemeData(trackShape: CustomTrackShape()), + child: Slider( + value: batteryLevel, + min: 0.0, + max: 100.0, + label: '${batteryLevel.round()}%', + onChanged: (value) { + setState(() { + batteryLevel = value; + }); + }, + onChangeEnd: (value) { + BlocProvider.of(context).add( + SetBatteryLimitLevelEvent( + batteryLimitLevel: value.toInt(), + ), + ); + }, ), - Text(batteryLevel.toStringAsFixed(0) + ' %'), - ], - ), - subtitle: SliderTheme( - data: SliderThemeData(trackShape: CustomTrackShape()), - child: Slider( - value: batteryLevel, - min: 0.0, - max: 100.0, - label: batteryLevel.round().toString() + '%', - onChanged: (value) { - setState(() { - batteryLevel = value; - }); - }, - onChangeEnd: (value) { - BlocProvider.of(context).add( - SetBatteryLimitLevelEvent( - batteryLimitLevel: value.toInt()), - ); - }, ), - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - contentPadding: - EdgeInsets.symmetric(horizontal: 10, vertical: 5), - ); - }), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + contentPadding: + EdgeInsets.symmetric(horizontal: 10, vertical: 5), + ); + }, + ), ], ); }, diff --git a/lib/Pages/torrent_screen/widgets/speed_graph.dart b/lib/Pages/torrent_screen/widgets/speed_graph.dart index b0f55dfe..14979c5a 100644 --- a/lib/Pages/torrent_screen/widgets/speed_graph.dart +++ b/lib/Pages/torrent_screen/widgets/speed_graph.dart @@ -1,7 +1,9 @@ import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncfusion_flutter_charts/charts.dart'; + import 'package:flood_mobile/Blocs/graph_bloc/graph_bloc.dart'; import 'package:flood_mobile/Blocs/home_screen_bloc/home_screen_bloc.dart'; import 'package:flood_mobile/Blocs/theme_bloc/theme_bloc.dart'; @@ -10,6 +12,7 @@ import 'package:flood_mobile/Model/graph_model.dart'; class SpeedGraph extends StatefulWidget { final HomeScreenBloc model; final int themeIndex; + const SpeedGraph({Key? key, required this.model, required this.themeIndex}) : super(key: key); @@ -26,15 +29,13 @@ class SpeedSpeedGraphState extends State { super.initState(); _speedGraphBloc = BlocProvider.of(context, listen: false); - // Start the timer when the widget is initialized - _timer = Timer.periodic(Duration(seconds: 2), (_) { + _timer = Timer.periodic(const Duration(seconds: 2), (_) { _speedGraphBloc.add(UpdateDataSourceEvent(model: widget.model)); }); } @override void dispose() { - // Cancel the timer when the widget is disposed _timer.cancel(); super.dispose(); } @@ -42,8 +43,8 @@ class SpeedSpeedGraphState extends State { @override Widget build(BuildContext context) { return SfCartesianChart( - key: Key('Speed Graph'), - margin: EdgeInsets.all(0), + key: const Key('Speed Graph'), + margin: EdgeInsets.zero, borderWidth: 0, borderColor: Colors.transparent, plotAreaBorderWidth: 0, @@ -59,39 +60,40 @@ class SpeedSpeedGraphState extends State { borderWidth: 0, borderColor: Colors.transparent, ), - series: >[ - SplineAreaSeries( + // Fix: ChartSeries -> CartesianSeries + series: >[ + SplineAreaSeries( dataSource: _speedGraphBloc.state.uploadGraphData, - xValueMapper: (GraphModel data, index) => data.second, - yValueMapper: (GraphModel data, index) => data.speed, + xValueMapper: (GraphModel data, _) => data.second, + yValueMapper: (GraphModel data, _) => data.speed, splineType: SplineType.natural, gradient: LinearGradient( colors: BlocProvider.of(context).isDarkMode ? [ - Color.fromARGB(255, 0, 111, 82), - Color(0xff191d2d).withAlpha(20) + const Color.fromARGB(255, 0, 111, 82), + const Color(0xff191d2d).withAlpha(20), ] : [ ThemeBloc.theme(widget.themeIndex) .primaryColorDark .withAlpha(130), - Color.fromARGB(67, 255, 255, 255) + const Color.fromARGB(67, 255, 255, 255), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), - SplineSeries( + SplineSeries( dataSource: _speedGraphBloc.state.uploadGraphData, width: 1.8, - xValueMapper: (GraphModel data, index) => data.second, - yValueMapper: (GraphModel data, index) => data.speed, + xValueMapper: (GraphModel data, _) => data.second, + yValueMapper: (GraphModel data, _) => data.speed, color: ThemeBloc.theme(widget.themeIndex).primaryColorDark, ), - SplineAreaSeries( + SplineAreaSeries( dataSource: _speedGraphBloc.state.downloadGraphData, - xValueMapper: (GraphModel data2, index) => data2.second, - yValueMapper: (GraphModel data2, index) => data2.speed, + xValueMapper: (GraphModel data, _) => data.second, + yValueMapper: (GraphModel data, _) => data.speed, splineType: SplineType.natural, gradient: LinearGradient( colors: BlocProvider.of(context).isDarkMode @@ -100,23 +102,23 @@ class SpeedSpeedGraphState extends State { .colorScheme .secondary .withAlpha(150), - Color(0xff191d2d).withAlpha(20) + const Color(0xff191d2d).withAlpha(20), ] : [ ThemeBloc.theme(widget.themeIndex) .colorScheme .secondary .withAlpha(120), - Color.fromARGB(37, 255, 255, 255) + const Color.fromARGB(37, 255, 255, 255), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), - SplineSeries( + SplineSeries( dataSource: _speedGraphBloc.state.downloadGraphData, - xValueMapper: (GraphModel data2, index) => data2.second, - yValueMapper: (GraphModel data2, index) => data2.speed, + xValueMapper: (GraphModel data, _) => data.second, + yValueMapper: (GraphModel data, _) => data.speed, width: 1.8, color: ThemeBloc.theme(widget.themeIndex).colorScheme.secondary, ), diff --git a/lib/Pages/torrent_screen/widgets/torrent_tile.dart b/lib/Pages/torrent_screen/widgets/torrent_tile.dart index 4961850d..8b8b0613 100644 --- a/lib/Pages/torrent_screen/widgets/torrent_tile.dart +++ b/lib/Pages/torrent_screen/widgets/torrent_tile.dart @@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:focused_menu/focused_menu.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; + import 'package:flood_mobile/Api/torrent_api.dart'; import 'package:flood_mobile/Blocs/multiple_select_torrent_bloc/multiple_select_torrent_bloc.dart'; import 'package:flood_mobile/Blocs/theme_bloc/theme_bloc.dart'; @@ -21,8 +22,11 @@ class TorrentTile extends StatefulWidget { final int themeIndex; final List indexes; - TorrentTile( - {required this.model, required this.themeIndex, required this.indexes}); + TorrentTile({ + required this.model, + required this.themeIndex, + required this.indexes, + }); @override _TorrentTileState createState() => _TorrentTileState(); @@ -33,8 +37,9 @@ class _TorrentTileState extends State { @override Widget build(BuildContext context) { - double hp = MediaQuery.of(context).size.height; - double wp = MediaQuery.of(context).size.width; + final double hp = MediaQuery.of(context).size.height; + final double wp = MediaQuery.of(context).size.width; + return BlocBuilder( builder: (context, state) { return Row( @@ -55,7 +60,7 @@ class _TorrentTileState extends State { width: 30, height: 90, color: Colors.transparent, - padding: EdgeInsets.only(left: 15), + padding: const EdgeInsets.only(left: 15), child: Center( child: Checkbox( activeColor: @@ -63,9 +68,10 @@ class _TorrentTileState extends State { value: state.selectedTorrentList .any((element) => element.hash == widget.model.hash), onChanged: (bool? value) { - var selectTorrent = + final selectTorrent = BlocProvider.of(context); - if (value!) { + + if (value == true) { selectTorrent .add(AddItemToListEvent(model: widget.model)); selectTorrent @@ -83,23 +89,47 @@ class _TorrentTileState extends State { ), Expanded( child: Slidable( - actionPane: SlidableBehindActionPane(), - actionExtentRatio: 0.25, + key: Key('slidable-${widget.model.hash}'), + endActionPane: ActionPane( + motion: const BehindMotion(), + extentRatio: 0.25, + children: [ + SlidableAction( + onPressed: (_) { + deleteTorrent( + context: context, + indexes: widget.indexes, + torrentModels: [widget.model], + themeIndex: widget.themeIndex, + ); + }, + backgroundColor: Colors.redAccent, + foregroundColor: Colors.white, + icon: Icons.delete, + label: context.l10n.button_delete, + ), + ], + ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: FocusedMenuHolder( - key: Key('Long Press Torrent Tile Menu'), + key: const Key('Long Press Torrent Tile Menu'), menuBoxDecoration: BoxDecoration( - color: ThemeBloc.theme(widget.themeIndex) - .textTheme - .bodyLarge - ?.color, - borderRadius: BorderRadius.circular(50)), + color: ThemeBloc.theme(widget.themeIndex) + .textTheme + .bodyLarge + ?.color, + borderRadius: BorderRadius.circular(50), + ), menuWidth: MediaQuery.of(context).size.width * 0.5, menuItemExtent: 60, onPressed: () {}, - menuItems: getFocusedMenuItems(context, widget.indexes, - widget.model, widget.themeIndex), + menuItems: getFocusedMenuItems( + context, + widget.indexes, + widget.model, + widget.themeIndex, + ), child: ExpansionTileCard( key: Key(widget.model.hash), onExpansionChanged: (value) { @@ -117,7 +147,7 @@ class _TorrentTileState extends State { .secondary, title: ListTile( key: Key(widget.model.hash), - contentPadding: EdgeInsets.all(0), + contentPadding: EdgeInsets.zero, title: Text( widget.model.name, overflow: TextOverflow.ellipsis, @@ -130,7 +160,7 @@ class _TorrentTileState extends State { ), ), subtitle: Container( - padding: EdgeInsets.symmetric(vertical: 8.0), + padding: const EdgeInsets.symmetric(vertical: 8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -143,8 +173,9 @@ class _TorrentTileState extends State { children: [ Expanded( child: LinearPercentIndicator( - key: Key('Linear Progress Indicator'), - padding: EdgeInsets.all(0), + key: const Key( + 'Linear Progress Indicator'), + padding: EdgeInsets.zero, lineHeight: 5.0, percent: widget.model.percentComplete .roundToDouble() / @@ -163,12 +194,10 @@ class _TorrentTileState extends State { : Colors.blue, ), ), - SizedBox( - width: 30, + const SizedBox(width: 30), + Text( + '${widget.model.percentComplete.toStringAsFixed(1)} %', ), - Text(widget.model.percentComplete - .toStringAsFixed(1) + - " %"), ], ), if (BlocProvider.of(context, @@ -176,9 +205,7 @@ class _TorrentTileState extends State { .state .model .showProgressBar) - SizedBox( - height: hp * 0.01, - ), + SizedBox(height: hp * 0.01), Row( children: [ Text( @@ -186,7 +213,7 @@ class _TorrentTileState extends State { .contains('downloading')) ? '${context.l10n.filter_status_downloading} ' : '${context.l10n.filter_status_stopped} ', - key: Key('status widget'), + key: const Key('status widget'), style: TextStyle( color: ThemeBloc.theme(widget.themeIndex) .textTheme @@ -197,31 +224,30 @@ class _TorrentTileState extends State { Flexible( child: Text( (widget.model.eta.toInt() != -1) - ? ('ETA: ') + - prettyDuration( - Duration( - seconds: widget.model.eta - .toInt(), - ), - abbreviated: true) + ? 'ETA: ${prettyDuration( + Duration( + seconds: + widget.model.eta.toInt(), + ), + abbreviated: true, + )}' : 'ETA : ∞', overflow: TextOverflow.ellipsis, - key: Key('eta widget'), + key: const Key('eta widget'), style: TextStyle( - color: - ThemeBloc.theme(widget.themeIndex) - .textTheme - .bodyLarge - ?.color), + color: + ThemeBloc.theme(widget.themeIndex) + .textTheme + .bodyLarge + ?.color, + ), ), ), ], ), - SizedBox( - height: hp * 0.002, - ), + SizedBox(height: hp * 0.002), Row( - key: Key('download done data widget'), + key: const Key('download done data widget'), children: [ Text( filesize(widget.model.bytesDone.toInt()), @@ -232,7 +258,7 @@ class _TorrentTileState extends State { ?.color, ), ), - Text(' / '), + const Text(' / '), Text( filesize(widget.model.sizeBytes.toInt()), style: TextStyle( @@ -271,8 +297,9 @@ class _TorrentTileState extends State { ), onTap: () { TorrentApi.stopTorrent( - context: context, - hashes: [widget.model.hash]); + context: context, + hashes: [widget.model.hash], + ); }, ) : GestureDetector( @@ -294,46 +321,29 @@ class _TorrentTileState extends State { ), onTap: () { TorrentApi.startTorrent( - context: context, - hashes: [widget.model.hash]); + context: context, + hashes: [widget.model.hash], + ); }, ), (!isExpanded) - ? Icon( - Icons.keyboard_arrow_down_rounded, - ) - : Icon( - Icons.keyboard_arrow_up_rounded, - ), + ? const Icon(Icons.keyboard_arrow_down_rounded) + : const Icon(Icons.keyboard_arrow_up_rounded), ], ), children: [ TorrentDescription( - model: widget.model, - themeIndex: widget.themeIndex, - hp: hp, - wp: wp) + model: widget.model, + themeIndex: widget.themeIndex, + hp: hp, + wp: wp, + ) ], ), ), ), - secondaryActions: [ - IconSlideAction( - caption: context.l10n.button_delete, - color: Colors.redAccent, - icon: Icons.delete, - onTap: () { - deleteTorrent( - context: context, - indexes: widget.indexes, - torrentModels: [widget.model], - themeIndex: widget.themeIndex, - ); - }, - ), - ], ), - ) + ), ], ); }, diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 3e911a9c..f712768f 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -export 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flood_mobile/gen/l10n/app_localizations.dart'; + +export 'package:flood_mobile/gen/l10n/app_localizations.dart'; extension AppLocalizationsX on BuildContext { AppLocalizations get l10n => AppLocalizations.of(this)!; diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 5027a9a1..b2a3d55b 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,20 +7,16 @@ #include "generated_plugin_registrant.h" #include -#include -#include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) awesome_notifications_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "AwesomeNotificationsPlugin"); awesome_notifications_plugin_register_with_registrar(awesome_notifications_registrar); - g_autoptr(FlPluginRegistrar) battery_plus_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "none"); - none_register_with_registrar(battery_plus_linux_registrar); - g_autoptr(FlPluginRegistrar) connectivity_plus_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "none"); - none_register_with_registrar(connectivity_plus_linux_registrar); + g_autoptr(FlPluginRegistrar) gtk_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); + gtk_plugin_register_with_registrar(gtk_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index e093c78b..e1220656 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,8 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST awesome_notifications - battery_plus_linux - connectivity_plus_linux + gtk url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 22a9f1e7..0add1112 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,20 +5,28 @@ import FlutterMacOS import Foundation +import app_links import awesome_notifications -import battery_plus_macos -import connectivity_plus_macos -import path_provider_macos -import shared_preferences_macos +import battery_plus +import connectivity_plus +import file_picker +import package_info_plus +import path_provider_foundation +import shared_preferences_foundation import url_launcher_macos -import wakelock_macos +import video_player_avfoundation +import wakelock_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) AwesomeNotificationsPlugin.register(with: registry.registrar(forPlugin: "AwesomeNotificationsPlugin")) BatteryPlusMacosPlugin.register(with: registry.registrar(forPlugin: "BatteryPlusMacosPlugin")) - ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) + FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) - WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) + FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) + WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index b262371a..eb4ce774 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,50 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: e6dd5609f0945ef6bc72bdcd28af6eeabefef99a6d62b7790320133789217759 + sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e" url: "https://pub.dev" source: hosted - version: "38.0.0" + version: "92.0.0" analyzer: - dependency: "direct overridden" + dependency: transitive description: name: analyzer - sha256: c71e50e4e1674c9ffeaf053bb8d38e4a03b94d08af6a27fb352f3ff569becc44 + sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e" + url: "https://pub.dev" + source: hosted + version: "9.0.0" + app_links: + dependency: "direct main" + description: + name: app_links + sha256: "5f88447519add627fe1cbcab4fd1da3d4fed15b9baf29f28b22535c95ecee3e8" + url: "https://pub.dev" + source: hosted + version: "6.4.1" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.dev" + source: hosted + version: "1.0.4" args: dependency: transitive description: @@ -37,10 +69,10 @@ packages: dependency: "direct main" description: name: awesome_notifications - sha256: "2b430c75cc879d6cfd52bb6eb2b5c1591ed425347816408cdcbd3f6916bba14c" + sha256: "0d5fa4457f2ba4e536adc3ef6af709cdcecf4a05a1f3035981e9afa2f899b2a8" url: "https://pub.dev" source: hosted - version: "0.7.4+1" + version: "0.10.1" badges: dependency: "direct main" description: @@ -53,50 +85,18 @@ packages: dependency: "direct main" description: name: battery_plus - sha256: b3f215bf2f49dcc4fb1cf40b21dc486014bad19f3f7a56e4131300efe738682a - url: "https://pub.dev" - source: hosted - version: "2.0.2" - battery_plus_linux: - dependency: transitive - description: - name: battery_plus_linux - sha256: "0bca215a3e8be08951afd2bd7aff70ff90e6f3ada0895ff299b12f30f54718e0" - url: "https://pub.dev" - source: hosted - version: "1.0.3" - battery_plus_macos: - dependency: transitive - description: - name: battery_plus_macos - sha256: "0607bcb26efb56eeb419df2f919ac30f1590c06027bdf7aed366dbb3d1bae485" + sha256: ad16fcb55b7384be6b4bbc763d5e2031ac7ea62b2d9b6b661490c7b9741155bf url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "7.0.0" battery_plus_platform_interface: dependency: transitive description: name: battery_plus_platform_interface - sha256: "19fd8418a81aeb2dea8fb7026b1fdf56b6c6d319baf8d703fa0d13c1d5c7ba2f" - url: "https://pub.dev" - source: hosted - version: "1.2.2" - battery_plus_web: - dependency: transitive - description: - name: battery_plus_web - sha256: "8bfa1ac36f8cdb07d64f60a342f986312036bfd910838c35d21dd786af13808b" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - battery_plus_windows: - dependency: transitive - description: - name: battery_plus_windows - sha256: "7b57fa77f50cd0401473f51339b701eddf1c68f040be644a649c67afe51bbba7" + sha256: e8342c0f32de4b1dfd0223114b6785e48e579bfc398da9471c9179b907fa4910 url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "2.0.1" bloc: dependency: "direct main" description: @@ -133,26 +133,26 @@ packages: dependency: "direct main" description: name: bottom_sheet - sha256: "7a3d4a1515eba91a7d9e1359e49416147de339889170fc879a8b905d27958c94" + sha256: efd28f52357d23e1c01eaeb45466b407f1e29318305bd6d10baf814fda18bd7e url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "4.0.4" build: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: aadd943f4f8cc946882c954c187e6115a84c98c81ad1d9c6cbf0895a8c85da9c url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "4.0.5" build_config: dependency: transitive description: name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.3.0" build_daemon: dependency: transitive description: @@ -161,30 +161,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "7c35a3a7868626257d8aee47b51c26b9dba11eaddf3431117ed2744951416aab" - url: "https://pub.dev" - source: hosted - version: "2.1.0" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "5e1929ad37d48bd382b124266cb8e521de5548d406a45a5ae6656c13dab73e37" - url: "https://pub.dev" - source: hosted - version: "2.4.5" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "0db1b64c84fa803603fa406f8721959036e898cc9575d6ce4a3067581b9276c0" + sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e" url: "https://pub.dev" source: hosted - version: "7.2.2" + version: "2.13.1" built_collection: dependency: transitive description: @@ -197,10 +181,10 @@ packages: dependency: transitive description: name: built_value - sha256: "59e08b0079bb75f7e27392498e26339387c1089c6bd58525a14eb8508637277b" + sha256: "0730c18c770d05636a8f945c32a4d7d81cb6e0f0148c8db4ad12e7748f7e49af" url: "https://pub.dev" source: hosted - version: "8.4.2" + version: "8.12.5" characters: dependency: transitive description: @@ -221,10 +205,10 @@ packages: dependency: "direct main" description: name: chewie - sha256: "745e81e84c6d7f3835f89f85bb49771c0a66099e4caf8f8e9e9a372bc66fb2c1" + sha256: "8bc4ac4cf3f316e50a25958c0f5eb9bb12cf7e8308bb1d74a43b230da2cfc144" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.7.5" cli_config: dependency: transitive description: @@ -269,50 +253,18 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "63b6c0cfcefc2b81f803f04ff033d614f4969ed74ac369643455d5829947908a" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - connectivity_plus_linux: - dependency: transitive - description: - name: connectivity_plus_linux - sha256: "0b5133ef6c4919c0d4e15ff7038a6a330ce2f15d5271074b9873873b8f4b2823" + sha256: b8fe52979ff12432ecf8f0abf6ff70410b1bb734be1c9e4f2f86807ad7166c79 url: "https://pub.dev" source: hosted - version: "1.1.0" - connectivity_plus_macos: - dependency: transitive - description: - name: connectivity_plus_macos - sha256: "488d2de1e47e1224ad486e501b20b088686ba1f4ee9c4420ecbc3b9824f0b920" - url: "https://pub.dev" - source: hosted - version: "1.2.6" + version: "7.1.0" connectivity_plus_platform_interface: dependency: transitive description: name: connectivity_plus_platform_interface - sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a - url: "https://pub.dev" - source: hosted - version: "1.2.4" - connectivity_plus_web: - dependency: transitive - description: - name: connectivity_plus_web - sha256: "81332be1b4baf8898fed17bb4fdef27abb7c6fd990bf98c54fd978478adf2f1a" + sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed" url: "https://pub.dev" source: hosted - version: "1.2.5" - connectivity_plus_windows: - dependency: transitive - description: - name: connectivity_plus_windows - sha256: "535b0404b4d5605c4dd8453d67e5d6d2ea0dd36e3b477f50f31af51b0aeab9dd" - url: "https://pub.dev" - source: hosted - version: "1.2.2" + version: "2.1.0" contained_tab_bar_view: dependency: "direct main" description: @@ -345,6 +297,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" + url: "https://pub.dev" + source: hosted + version: "0.3.5+2" crypto: dependency: transitive description: @@ -373,18 +333,18 @@ packages: dependency: transitive description: name: dart_style - sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4" + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "3.1.3" dbus: dependency: transitive description: name: dbus - sha256: "3350efa144252eaa4264055dded4404a94b770cfe914f1d08c20953aee55cac2" + sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 url: "https://pub.dev" source: hosted - version: "0.5.4" + version: "0.7.12" diff_match_patch: dependency: transitive description: @@ -405,10 +365,10 @@ packages: dependency: "direct main" description: name: dropdown_button2 - sha256: "580bfe26a91e822a95d96e227ac02c1e14a7d5eea32501d0fbc6fe8d5c5606e5" + sha256: fbb9ea9ea81b04e48d27bb181852b7e11e684aa2a32e95aec4c131be1849e33a url: "https://pub.dev" source: hosted - version: "1.9.2" + version: "3.0.0" duration: dependency: "direct main" description: @@ -445,10 +405,10 @@ packages: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.2.0" file: dependency: transitive description: @@ -461,10 +421,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: f9245fc33aeba9e0b938d7f3785f10b7a7230e05b8fc40f5a6a8342d7899e391 + sha256: "57d9a1dd5063f85fa3107fb42d1faffda52fdc948cefd5fe5ea85267a5fc7343" url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "10.3.10" fixnum: dependency: transitive description: @@ -490,26 +450,26 @@ packages: dependency: "direct main" description: name: flutter_client_sse - sha256: "7d5d44f129db8107768dc52e02aa390340a21e94c4cd5ef7b741458d9cc258fa" + sha256: "4ce0297206473dfc064b255fe086713240002e149f52519bd48c21423e4aa5d2" url: "https://pub.dev" source: hosted - version: "2.0.0-beta.1" + version: "2.0.3" flutter_downloader: dependency: "direct main" description: name: flutter_downloader - sha256: "11f0c7a1ac185abddfe96ae2b5d315a552e9d7a62046eedfa09a0dc4f5734ed6" + sha256: "93a9ddbd561f8a3f5483b4189453fba145a0a1014a88143c96a966296b78a118" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.12.0" flutter_keyboard_visibility: dependency: "direct main" description: name: flutter_keyboard_visibility - sha256: "86b71bbaffa38e885f5c21b1182408b9be6951fd125432cf6652c636254cef2d" + sha256: "98664be7be0e3ffca00de50f7f6a287ab62c763fc8c762e0a21584584a3ff4f8" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.0" flutter_keyboard_visibility_linux: dependency: transitive description: @@ -550,6 +510,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" flutter_localizations: dependency: "direct main" description: flutter @@ -559,18 +527,18 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + sha256: "38d1c268de9097ff59cf0e844ac38759fc78f76836d37edad06fa21e182055a0" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.34" flutter_slidable: dependency: "direct main" description: name: flutter_slidable - sha256: c7607eb808cdef19c8468246e95a133308aeaeb3971cdd9edfb9d5e31cedfbe9 + sha256: ea369262929d3cc6ebf9d8a00c196127966f117fe433a5e5cb47fb08008ca203 url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "4.0.3" flutter_speed_dial: dependency: "direct main" description: @@ -591,10 +559,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "9ac1967e2f72a08af11b05b39167920f90d043cf67163d13a544a358c8f31afa" + sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c url: "https://pub.dev" source: hosted - version: "0.22.0" + version: "2.0.9" flutter_test: dependency: "direct dev" description: flutter @@ -609,10 +577,10 @@ packages: dependency: "direct main" description: name: fluttertoast - sha256: "7cc92eabe01e3f1babe1571c5560b135dfc762a34e41e9056881e2196b178ec1" + sha256: "144ddd74d49c865eba47abe31cbc746c7b311c82d6c32e571fd73c4264b740e2" url: "https://pub.dev" source: hosted - version: "8.1.2" + version: "9.0.0" focused_menu: dependency: "direct main" description: @@ -649,10 +617,10 @@ packages: dependency: "direct main" description: name: google_fonts - sha256: "6b6f10f0ce3c42f6552d1c70d2c28d764cf22bb487f50f66cca31dcd5194f4d6" + sha256: ba03d03bcaa2f6cb7bd920e3b5027181db75ab524f8891c8bc3aa603885b8055 url: "https://pub.dev" source: hosted - version: "4.0.4" + version: "6.3.3" graphs: dependency: transitive description: @@ -661,6 +629,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.dev" + source: hosted + version: "2.1.0" hidden_drawer_menu: dependency: "direct main" description: @@ -681,10 +657,10 @@ packages: dependency: "direct main" description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -702,13 +678,13 @@ packages: source: hosted version: "4.0.2" intl: - dependency: "direct overridden" + dependency: transitive description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.20.2" io: dependency: transitive description: @@ -717,22 +693,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.3" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" json_annotation: dependency: "direct main" description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.11.0" json_patch: dependency: "direct main" description: @@ -745,10 +713,10 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: "61a60716544392a82726dd0fa1dd6f5f1fd32aec66422b6e229e7b90d52325c4" + sha256: "44729f5c45748e6748f6b9a57ab8f7e4336edc8ae41fc295070e3814e616a6c0" url: "https://pub.dev" source: hosted - version: "6.7.0" + version: "6.13.0" leak_tracker: dependency: transitive description: @@ -773,6 +741,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + url: "https://pub.dev" + source: hosted + version: "6.1.0" loading_overlay: dependency: "direct main" description: @@ -806,13 +782,13 @@ packages: source: hosted version: "0.13.0" meta: - dependency: "direct overridden" + dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.17.0" mime: dependency: transitive description: @@ -841,10 +817,10 @@ packages: dependency: transitive description: name: nm - sha256: b47776ec6a4799d7df9e8dff48319e66efd791bf2bcab8d26408db8f716e3a0e + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.5.0" node_preamble: dependency: transitive description: @@ -853,14 +829,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" - optimize_battery: - dependency: "direct main" - description: - name: optimize_battery - sha256: "4f0f974addbe54d3a705c1da5bf3a4bdae39502b1b2d2a17f9da1e558a34a4f8" - url: "https://pub.dev" - source: hosted - version: "0.0.4" package_config: dependency: transitive description: @@ -869,94 +837,86 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" - path: + package_info_plus: dependency: transitive description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + name: package_info_plus + sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20" url: "https://pub.dev" source: hosted - version: "1.9.1" - path_drawing: + version: "9.0.1" + package_info_plus_platform_interface: dependency: transitive description: - name: path_drawing - sha256: "3bdd251dae9ffaef944450b73f168610db7e968e7b20daf0c3907f8b4aafc8a2" + name: package_info_plus_platform_interface + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" url: "https://pub.dev" source: hosted - version: "0.5.1+1" + version: "3.2.1" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" path_parsing: dependency: transitive description: name: path_parsing - sha256: ee5c47c1058ad66b4a41746ec3996af9593d0858872807bcd64ac118f0700337 + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "1.1.0" path_provider: dependency: "direct main" description: name: path_provider - sha256: "050e8e85e4b7fecdf2bb3682c1c64c4887a183720c802d323de8a5fd76d372dd" + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.1.5" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba" url: "https://pub.dev" source: hosted - version: "2.0.22" - path_provider_ios: + version: "2.2.23" + path_provider_foundation: dependency: transitive description: - name: path_provider_ios - sha256: "03d639406f5343478352433f00d3c4394d52dac8df3d847869c5e2333e0bbce8" + name: path_provider_foundation + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.5.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 - url: "https://pub.dev" - source: hosted - version: "2.1.7" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - sha256: "2a97e7fbb7ae9dcd0dfc1220a78e9ec3e71da691912e617e8715ff2a13086ae8" + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c - url: "https://pub.dev" - source: hosted - version: "2.1.3" - pedantic: - dependency: transitive - description: - name: pedantic - sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "2.3.0" percent_indicator: dependency: "direct main" description: @@ -969,26 +929,58 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "4356882e9abf51aa0d56e8fb886e0d6162719f2310dd71f0b8fa7f34908b128d" + sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "12.0.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" + url: "https://pub.dev" + source: hosted + version: "13.0.1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 + url: "https://pub.dev" + source: hosted + version: "9.4.7" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + url: "https://pub.dev" + source: hosted + version: "4.3.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" url: "https://pub.dev" source: hosted - version: "3.9.0" + version: "0.2.1" petitparser: dependency: transitive description: name: petitparser - sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "7.0.2" platform: dependency: transitive description: @@ -1001,10 +993,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.8" pool: dependency: transitive description: @@ -1033,10 +1025,10 @@ packages: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.2.0" pubspec_parse: dependency: transitive description: @@ -1049,66 +1041,58 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "76917b7d4b9526b2ba416808a7eb9fb2863c1a09cf63ec85f1453da240fa818a" + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.5.5" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "8e251f3c986002b65fed6396bce81f379fb63c27317d49743cf289fd0fd1ab97" + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 url: "https://pub.dev" source: hosted - version: "2.0.14" - shared_preferences_ios: + version: "2.4.23" + shared_preferences_foundation: dependency: transitive description: - name: shared_preferences_ios - sha256: "585a14cefec7da8c9c2fb8cd283a3bb726b4155c0952afe6a0caaa7b2272de34" + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.5.6" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: fbc3cd6826896b66a5f576b025e4f344f780c84ea7f8203097a353370607a2c8 + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" url: "https://pub.dev" source: hosted - version: "2.1.2" - shared_preferences_macos: - dependency: transitive - description: - name: shared_preferences_macos - sha256: fbb94bf296576f49be37a1496d5951796211a8db0aa22cc0d68c46440dad808c - url: "https://pub.dev" - source: hosted - version: "2.0.4" + version: "2.4.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.4.2" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.4.3" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "07c274c2115d4d5e4280622abb09f0980e2c5b1fcdc98ae9f59a3bad5bfc1f26" + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.4.1" shelf: dependency: transitive description: @@ -1158,18 +1142,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33" + sha256: "732792cfd197d2161a65bb029606a46e0a18ff30ef9e141a7a82172b05ea8ecd" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "4.2.2" source_helper: dependency: transitive description: name: source_helper - sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" + sha256: "1d3b229b2934034fb2e691fbb3d53e0f75a4af7b1407f88425ed8f209bcb1b8f" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.11" source_map_stack_trace: dependency: transitive description: @@ -1230,18 +1214,18 @@ packages: dependency: "direct main" description: name: syncfusion_flutter_charts - sha256: "0222ac9d8cb6c671f014effe9bd5c0aef35eadb16471355345ba87cc0ac007b3" + sha256: "95eb44257e08dd769e449cd445a35ff38749337a7a37e47b44c30a45d357edb9" url: "https://pub.dev" source: hosted - version: "20.4.54" + version: "33.1.46" syncfusion_flutter_core: dependency: transitive description: name: syncfusion_flutter_core - sha256: "3979f0b1c5a97422cadae52d476c21fa3e0fb671ef51de6cae1d646d8b99fe1f" + sha256: "6d2505a8a8f9fcc6a070e07715d7fab06868725bb15b5eaf9432ece807ccd93b" url: "https://pub.dev" source: hosted - version: "20.4.54" + version: "33.1.46" term_glyph: dependency: transitive description: @@ -1274,14 +1258,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.16" - timing: - dependency: transitive - description: - name: timing - sha256: c386d07d7f5efc613479a7c4d9d64b03710b03cfaa7e8ad5f2bfb295a1f0dfad - url: "https://pub.dev" - source: hosted - version: "1.0.0" typed_data: dependency: transitive description: @@ -1290,102 +1266,102 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" - uni_links: - dependency: "direct main" - description: - name: uni_links - sha256: "051098acfc9e26a9fde03b487bef5d3d228ca8f67693480c6f33fd4fbb8e2b6e" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - uni_links_platform_interface: - dependency: transitive - description: - name: uni_links_platform_interface - sha256: "929cf1a71b59e3b7c2d8a2605a9cf7e0b125b13bc858e55083d88c62722d4507" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - uni_links_web: + upower: dependency: transitive description: - name: uni_links_web - sha256: "7539db908e25f67de2438e33cc1020b30ab94e66720b5677ba6763b25f6394df" - url: "https://pub.dev" - source: hosted - version: "0.1.0" - uri_to_file: - dependency: "direct main" - description: - name: uri_to_file - sha256: "84afd633b1492fc465c768141e1a29edd519061bf99935b6b4d0d5de8ec7c108" + name: upower + sha256: cf042403154751180affa1d15614db7fa50234bc2373cd21c3db666c38543ebf url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.7.0" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: "3c92b0efb5e9dcb8f846aefabf9f0f739f91682ed486b991ceda51c288e60896" + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 url: "https://pub.dev" source: hosted - version: "6.1.7" + version: "6.3.2" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "6f91d30ce9060c204b2dbe728adb300750fa4b228e8f7ed1b961aa1ceb728799" + sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572" url: "https://pub.dev" source: hosted - version: "6.0.22" + version: "6.3.29" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "6ba7dddee26c9fae27c9203c424631109d73c8fa26cfa7bc3e35e751cb87f62e" + sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" url: "https://pub.dev" source: hosted - version: "6.0.17" + version: "6.4.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "360fa359ab06bcb4f7c5cd3123a2a9a4d3364d4575d27c4b33468bd4497dd094" + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.2.2" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: a9b3ea9043eabfaadfa3fb89de67a11210d85569086d22b3854484beab8b3978 + sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.2.5" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6" + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "5669882643b96bb6d5786637cac727c6e918a790053b09245fd4513b8a07df2a" + sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f url: "https://pub.dev" source: hosted - version: "2.0.13" + version: "2.4.2" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: e3c3b16d3104260c10eea3b0e34272aaa57921f83148b0619f74c2eced9b7ef1 + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.5" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "4ac59808bbfca6da38c99f415ff2d3a5d7ca0a6b4809c71d9cf30fba5daf9752" + url: "https://pub.dev" + source: hosted + version: "1.1.10+1" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: f3247e7ab0ec77dc759263e68394990edc608fb2b480b80db8aa86ed09279e33 + url: "https://pub.dev" + source: hosted + version: "1.1.10+1" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a" + url: "https://pub.dev" + source: hosted + version: "1.1.10+1" vector_math: dependency: transitive description: @@ -1398,42 +1374,42 @@ packages: dependency: "direct main" description: name: video_player - sha256: "86b4fb9e30613ef4ff7e47367bfec4b080ab17205b7d969cd12bbebde49476b1" + sha256: "48a7bdaa38a3d50ec10c78627abdbfad863fdf6f0d6e08c7c3c040cfd80ae36f" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.11.1" video_player_android: dependency: transitive description: name: video_player_android - sha256: "984388511230bac63feb53b2911a70e829fe0976b6b2213f5c579c4e0a882db3" + sha256: "877a6c7ba772456077d7bfd71314629b3fe2b73733ce503fc77c3314d43a0ca0" url: "https://pub.dev" source: hosted - version: "2.3.10" + version: "2.9.5" video_player_avfoundation: dependency: transitive description: name: video_player_avfoundation - sha256: d9f7a46d6a77680adb03ec05a381025d6e890ebe636637c6c3014cc3926b97e9 + sha256: af0e5b8a7a4876fb37e7cc8cb2a011e82bb3ecfa45844ef672e32cb14a1f259e url: "https://pub.dev" source: hosted - version: "2.3.8" + version: "2.9.4" video_player_platform_interface: dependency: transitive description: name: video_player_platform_interface - sha256: "42bb75de5e9b79e1f20f1d95f688fac0f95beac4d89c6eb2cd421724d4432dae" + sha256: "57c5d73173f76d801129d0531c2774052c5a7c11ccb962f1830630decd9f24ec" url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.6.0" video_player_web: dependency: transitive description: name: video_player_web - sha256: b649b07b8f8f553bee4a97a0a53d0fe78a70b115eafaf0105b612b32b05ddb99 + sha256: "9f3c00be2ef9b76a95d94ac5119fb843dca6f2c69e6c9968f6f2b6c9e7afbdeb" url: "https://pub.dev" source: hosted - version: "2.0.13" + version: "2.4.0" vm_service: dependency: transitive description: @@ -1442,62 +1418,46 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.2" - wakelock: + wakelock_plus: dependency: "direct main" description: - name: wakelock - sha256: "769ecf42eb2d07128407b50cb93d7c10bd2ee48f0276ef0119db1d25cc2f87db" - url: "https://pub.dev" - source: hosted - version: "0.6.2" - wakelock_macos: - dependency: transitive - description: - name: wakelock_macos - sha256: "047c6be2f88cb6b76d02553bca5a3a3b95323b15d30867eca53a19a0a319d4cd" + name: wakelock_plus + sha256: "8b12256f616346910c519a35606fb69b1fe0737c06b6a447c6df43888b097f39" url: "https://pub.dev" source: hosted - version: "0.4.0" - wakelock_platform_interface: - dependency: transitive - description: - name: wakelock_platform_interface - sha256: "1f4aeb81fb592b863da83d2d0f7b8196067451e4df91046c26b54a403f9de621" - url: "https://pub.dev" - source: hosted - version: "0.3.0" - wakelock_web: + version: "1.5.1" + wakelock_plus_platform_interface: dependency: transitive description: - name: wakelock_web - sha256: "1b256b811ee3f0834888efddfe03da8d18d0819317f20f6193e2922b41a501b5" + name: wakelock_plus_platform_interface + sha256: "24b84143787220a403491c2e5de0877fbbb87baf3f0b18a2a988973863db4b03" url: "https://pub.dev" source: hosted - version: "0.4.0" - wakelock_windows: + version: "1.4.0" + watcher: dependency: transitive description: - name: wakelock_windows - sha256: "857f77b3fe6ae82dd045455baa626bc4b93cb9bb6c86bf3f27c182167c3a5567" + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" url: "https://pub.dev" source: hosted - version: "0.2.1" - watcher: + version: "1.2.1" + web: dependency: transitive description: - name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "3a969ddcc204a3e34e863d204b29c0752716f78b6f9cc8235083208d268a4ccd" + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.0" webkit_inspection_protocol: dependency: transitive description: @@ -1506,22 +1466,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" - wifi_iot: - dependency: "direct main" - description: - name: wifi_iot - sha256: "6c8d014ca935a893e1bfe0589a6e848a9027678237f856754da99e7bab3cf36b" - url: "https://pub.dev" - source: hosted - version: "0.3.18" win32: dependency: transitive description: name: win32 - sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "5.15.0" xdg_directories: dependency: transitive description: @@ -1534,10 +1486,10 @@ packages: dependency: transitive description: name: xml - sha256: "80d494c09849dc3f899d227a78c30c5b949b985ededf884cb3f3bcd39f4b447a" + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" url: "https://pub.dev" source: hosted - version: "5.4.1" + version: "6.6.1" yaml: dependency: transitive description: @@ -1547,5 +1499,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.9.0-0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.10.0 <4.0.0" + flutter: ">=3.41.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9289ad20..d131c9ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,84 +1,63 @@ name: flood_mobile description: A new Flutter application. -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: "none" # Remove this line if you wish to publish to pub.dev +publish_to: "none" -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: - sdk: ">=2.14.0 <3.0.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.41.0" dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - # flutter pub run build_runner watch --delete-conflicting-outputs - dropdown_button2: ^1.5.2 + + dropdown_button2: ^3.0.0 clipboard: ^0.1.3 cupertino_icons: ^1.0.3 chewie: ^1.2.2 dio: ^4.0.0 duration: ^3.0.6 expansion_tile_card: ^3.0.0 - flutter_keyboard_visibility: ^5.0.3 - flutter_svg: ^0.22.0 - flutter_slidable: ^0.6.0 + flutter_keyboard_visibility: ^6.0.0 + flutter_svg: ^2.0.9 + flutter_slidable: ^4.0.1 flutter_spinkit: ^5.0.0 - fluttertoast: ^8.0.8 - file_picker: ^3.0.4 + fluttertoast: ^9.0.0 + file_picker: ^10.3.10 focused_menu: ^1.0.5 font_awesome_flutter: ^9.1.0 hidden_drawer_menu: ^3.0.0 - http: ^0.13.3 + http: ^1.6.0 json_annotation: ^4.1.0 json_patch: ^3.0.0 loading_overlay: ^0.3.0 percent_indicator: ^3.0.1 - shared_preferences: ^2.0.6 + shared_preferences: ^2.5.5 video_player: ^2.1.12 badges: ^2.0.1 - flutter_downloader: ^1.6.1 - path_provider: ^2.0.2 - permission_handler: ^8.1.4+2 + flutter_downloader: ^1.12.0 + path_provider: ^2.1.5 + permission_handler: ^12.0.1 flutter_client_sse: ^2.0.0-beta.1 - url_launcher: ^6.0.12 - optimize_battery: ^0.0.4 + url_launcher: ^6.3.2 smooth_page_indicator: ^1.0.0+2 - uri_to_file: ^0.2.0 - bottom_sheet: ^3.1.1 + bottom_sheet: ^4.0.4 contained_tab_bar_view: ^0.8.0 - uni_links: ^0.5.1 + app_links: ^6.4.1 flutter_speed_dial: ^6.0.0 - syncfusion_flutter_charts: ^20.4.44 + syncfusion_flutter_charts: ^33.1.46 bloc: ^8.1.2 flutter_bloc: ^8.1.3 equatable: ^2.0.5 - google_fonts: ^4.0.4 - awesome_notifications: ^0.7.4+1 - connectivity_plus: ^2.1.0 - wakelock: ^0.6.2 - battery_plus: ^2.0.2 - wifi_iot: ^0.3.18 - -dependency_overrides: - meta: ^1.7.0 - analyzer: ^3.0.0 - intl: ^0.18.0 + google_fonts: ^6.3.0 + awesome_notifications: ^0.10.0 + connectivity_plus: ^7.1.0 + wakelock_plus: ^1.2.8 + battery_plus: ^7.0.0 dev_dependencies: build_runner: ^2.1.1 @@ -87,43 +66,10 @@ dev_dependencies: json_serializable: ^6.7.0 bloc_test: ^9.1.3 mocktail: ^0.3.0 + flutter_lints: ^6.0.0 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true generate: true - # To add assets to your application, add an assets section, like this: assets: - assets/images/ - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/test/unit_test/api_bloc_unit_test.dart b/test/unit_test/api_bloc_unit_test.dart index 0df5c639..034da802 100644 --- a/test/unit_test/api_bloc_unit_test.dart +++ b/test/unit_test/api_bloc_unit_test.dart @@ -3,9 +3,11 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flood_mobile/Blocs/api_bloc/api_bloc.dart'; import 'package:flood_mobile/Blocs/api_bloc/api_bloc_event.dart'; import 'package:flood_mobile/Blocs/api_bloc/api_bloc_state.dart'; +import 'package:shared_preferences/shared_preferences.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); // Initialize Flutter bindings + SharedPreferences.setMockInitialValues({}); group('ApiBloc', () { late ApiBloc apiBloc; diff --git a/test/unit_test/language_bloc_unit_test.dart b/test/unit_test/language_bloc_unit_test.dart index 7c3a1e3d..8698c180 100644 --- a/test/unit_test/language_bloc_unit_test.dart +++ b/test/unit_test/language_bloc_unit_test.dart @@ -2,8 +2,11 @@ import 'package:bloc_test/bloc_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flood_mobile/Blocs/language_bloc/language_bloc.dart'; +import 'package:shared_preferences/shared_preferences.dart'; void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + SharedPreferences.setMockInitialValues({}); group('LanguageBloc', () { late LanguageBloc languageBloc; diff --git a/test/unit_test/theme_bloc_unit_test.dart b/test/unit_test/theme_bloc_unit_test.dart index cd4063be..079b03a2 100644 --- a/test/unit_test/theme_bloc_unit_test.dart +++ b/test/unit_test/theme_bloc_unit_test.dart @@ -6,6 +6,8 @@ import 'package:flood_mobile/Blocs/theme_bloc/theme_bloc.dart'; import 'package:flood_mobile/Constants/theme_constant.dart'; void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + SharedPreferences.setMockInitialValues({}); WidgetsFlutterBinding.ensureInitialized(); group('ThemeBloc', () { late ThemeBloc themeBloc; diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 73605b91..00000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:flood_mobile/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/test/widget_test/torrent_screen_widget_test.dart b/test/widget_test/torrent_screen_widget_test.dart index 12c01b99..adf5f683 100644 --- a/test/widget_test/torrent_screen_widget_test.dart +++ b/test/widget_test/torrent_screen_widget_test.dart @@ -277,8 +277,13 @@ void main() { expect(find.text('10 Kb/s'), findsOneWidget); expect(find.byIcon(Icons.arrow_downward_rounded), findsOneWidget); expect(find.text('20 Kb/s'), findsOneWidget); - expect(find.byKey(Key("Show Chart Button")), findsOneWidget); - await tester.tap(find.byKey(Key("Show Chart Button"))); + final showChartButton = find.byKey(const Key("Show Chart Button")); + expect(showChartButton, findsOneWidget); + + await tester.ensureVisible(showChartButton); + await tester.pumpAndSettle(); + + await tester.tap(showChartButton); await tester.pumpAndSettle(); expect(find.byKey(Key("Speed Graph")), findsOneWidget); expect(find.byKey(Key('Search Torrent TextField')), findsOneWidget); @@ -343,7 +348,13 @@ void main() { expect(find.byKey(Key("Error Torrent ListTile")), findsOneWidget); expect(find.text('Filter by tags'), findsOneWidget); expect(find.text('Filter by trackers'), findsOneWidget); - await tester.tap(find.byKey(Key("Show Chart Button"))); + await tester.ensureVisible(showChartButton); + await tester.pumpAndSettle(); + + await tester.ensureVisible(showChartButton); + await tester.pumpAndSettle(); + await tester.tap(showChartButton, + warnIfMissed: false); // add warnIfMissed false if needed await tester.pumpAndSettle(); expect(find.byIcon(FontAwesomeIcons.sortAlphaUp), findsOneWidget); await tester.tap(find.byIcon(FontAwesomeIcons.sortAlphaUp)); diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index ebfd39e0..90d78e3a 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,18 +6,24 @@ #include "generated_plugin_registrant.h" +#include #include -#include -#include +#include +#include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + AppLinksPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AppLinksPluginCApi")); AwesomeNotificationsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("AwesomeNotificationsPluginCApi")); BatteryPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("BatteryPlusWindowsPlugin")); ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b789e0dd..fd01c80e 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,9 +3,11 @@ # list(APPEND FLUTTER_PLUGIN_LIST + app_links awesome_notifications - battery_plus_windows - connectivity_plus_windows + battery_plus + connectivity_plus + permission_handler_windows url_launcher_windows ) From 60536bc6fe4c0042ec49202a7722d29476d9217f Mon Sep 17 00:00:00 2001 From: ObaidAbdullah16 Date: Sun, 5 Apr 2026 03:12:12 +0530 Subject: [PATCH 3/3] Fix onboarding RenderFlex overflow by removing fixed height --- .../widgets/onboard_page.dart | 127 ++++++++++-------- 1 file changed, 70 insertions(+), 57 deletions(-) diff --git a/lib/Pages/onboarding_main_screen/widgets/onboard_page.dart b/lib/Pages/onboarding_main_screen/widgets/onboard_page.dart index 381e8160..8e1f0477 100644 --- a/lib/Pages/onboarding_main_screen/widgets/onboard_page.dart +++ b/lib/Pages/onboarding_main_screen/widgets/onboard_page.dart @@ -9,9 +9,11 @@ class OnboardPage extends StatefulWidget { final PageController pageController; final OnboardPageModel pageModel; - const OnboardPage( - {required Key key, required this.pageModel, required this.pageController}) - : super(key: key); + const OnboardPage({ + required Key key, + required this.pageModel, + required this.pageController, + }) : super(key: key); @override _OnboardPageState createState() => _OnboardPageState(); @@ -25,13 +27,17 @@ class _OnboardPageState extends State @override void initState() { - animationController = - AnimationController(vsync: this, duration: Duration(milliseconds: 750)); + animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 750), + ); heroAnimation = Tween(begin: -40, end: 0).animate( - CurvedAnimation(parent: animationController, curve: Curves.bounceOut)); + CurvedAnimation(parent: animationController, curve: Curves.bounceOut), + ); borderAnimation = Tween(begin: 75, end: 50).animate( - CurvedAnimation(parent: animationController, curve: Curves.bounceOut)); + CurvedAnimation(parent: animationController, curve: Curves.bounceOut), + ); animationController.forward(from: 0); super.initState(); @@ -44,12 +50,11 @@ class _OnboardPageState extends State } _nextButtonPressed() { - BlocProvider.of(context, listen: false) - .add(SetColorEvent(color: widget.pageModel.nextAccentColor)); + BlocProvider.of(context, listen: false).add( + SetColorEvent(color: widget.pageModel.nextAccentColor), + ); widget.pageController.nextPage( - duration: Duration( - milliseconds: 1500, - ), + duration: const Duration(milliseconds: 1500), curve: Curves.easeInOutCubicEmphasized, ); } @@ -78,50 +83,57 @@ class _OnboardPageState extends State }, ), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 32.0), - child: Container( - height: 250, - width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Text( - widget.pageModel.caption, - style: TextStyle( - fontSize: 24, - color: - widget.pageModel.accentColor.withOpacity(0.8), - letterSpacing: 1, - fontStyle: FontStyle.normal), - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Text( - widget.pageModel.subhead, - style: TextStyle( - fontSize: 35, - fontWeight: FontWeight.bold, - color: widget.pageModel.accentColor, - letterSpacing: 1, - fontStyle: FontStyle.normal), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - widget.pageModel.description, - style: TextStyle( - fontSize: 18, - color: - widget.pageModel.accentColor.withOpacity(0.9), + + // FIX: remove hardcoded height and allow content to scroll if needed + Flexible( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0), + child: SingleChildScrollView( + child: SizedBox( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0), + child: Text( + widget.pageModel.caption, + style: TextStyle( + fontSize: 24, + color: widget.pageModel.accentColor + .withOpacity(0.8), + letterSpacing: 1, + fontStyle: FontStyle.normal, + ), + ), ), - ), + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + widget.pageModel.subhead, + style: TextStyle( + fontSize: 35, + fontWeight: FontWeight.bold, + color: widget.pageModel.accentColor, + letterSpacing: 1, + fontStyle: FontStyle.normal, + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + widget.pageModel.description, + style: TextStyle( + fontSize: 18, + color: widget.pageModel.accentColor + .withOpacity(0.9), + ), + ), + ), + ], ), - ], + ), ), ), ), @@ -137,7 +149,7 @@ class _OnboardPageState extends State painter: DrawerPaint( curveColor: widget.pageModel.accentColor, ), - child: Container( + child: SizedBox( width: borderAnimation.value, height: double.infinity, child: Align( @@ -153,8 +165,9 @@ class _OnboardPageState extends State onPressed: () { if (widget.pageController.page?.round() == 2) { Navigator.of(context).pushNamedAndRemoveUntil( - Routes.loginScreenRoute, - (Route route) => false); + Routes.loginScreenRoute, + (Route route) => false, + ); } else { _nextButtonPressed(); }