From 11d46e41f80ae0e8a3b7e9ddce42d31bc5e4d8aa Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 16 Nov 2025 10:10:31 -0800 Subject: [PATCH 1/3] Update serious_python dependency to use git source Switched serious_python from version 0.9.4 to a git reference pointing to the ios-bundle-id-fix branch. This allows using the latest changes from that branch instead of the published version. --- {{cookiecutter.out_dir}}/pubspec.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/{{cookiecutter.out_dir}}/pubspec.yaml b/{{cookiecutter.out_dir}}/pubspec.yaml index 221dde1..bd0e295 100644 --- a/{{cookiecutter.out_dir}}/pubspec.yaml +++ b/{{cookiecutter.out_dir}}/pubspec.yaml @@ -29,13 +29,13 @@ dependency_overrides: ref: main url: https://github.com/flet-dev/flet.git - serious_python: 0.9.4 + # serious_python: 0.9.4 - # serious_python: - # git: - # url: https://github.com/flet-dev/serious-python.git - # ref: main - # path: src/serious_python + serious_python: + git: + url: https://github.com/flet-dev/serious-python.git + ref: ios-bundle-id-fix + path: src/serious_python wakelock_plus: ^1.2.10 web: ^1.0.0 From 1a928b53b455c793a0a6a8bacf0fe547e2983010 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 26 Nov 2025 09:55:13 -0800 Subject: [PATCH 2/3] Rename isProduction to isRelease and update usage Replaces the isProduction constant with isRelease for clarity and updates its usage in the prepareApp function to ensure debugPrint is disabled only in release mode. --- {{cookiecutter.out_dir}}/lib/main.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.out_dir}}/lib/main.dart b/{{cookiecutter.out_dir}}/lib/main.dart index c0782aa..2b04d2b 100644 --- a/{{cookiecutter.out_dir}}/lib/main.dart +++ b/{{cookiecutter.out_dir}}/lib/main.dart @@ -37,7 +37,7 @@ show_startup_screen: {{ show_startup_screen }} startup_screen_message: {{ startup_screen_message }} */ -const bool isProduction = bool.fromEnvironment('dart.vm.product'); +const bool isRelease = bool.fromEnvironment('dart.vm.product'); const assetPath = "app/app.zip"; const pythonModuleName = "{{ cookiecutter.python_module_name }}"; @@ -120,7 +120,7 @@ void main(List args) async { } Future prepareApp() async { - if (!_args.contains("--debug")) { + if (!_args.contains("--debug") && isRelease) { // ignore: avoid_returning_null_for_void debugPrint = (String? message, {int? wrapWidth}) => null; } else { From 4c63345e28a5c3e91608e6710282cf30e27c4053 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 27 Nov 2025 17:12:15 -0800 Subject: [PATCH 3/3] Migrate Android build scripts to Kotlin DSL (#58) Replaces Groovy-based Gradle files with Kotlin DSL equivalents for build.gradle, settings.gradle, and app/build.gradle. Updates AndroidManifest.xml to add taskAffinity and queries for PROCESS_TEXT intent. Refactors MainActivity.kt for cleaner syntax. Increases Gradle JVM memory settings and updates Gradle wrapper and plugin versions for improved build performance and compatibility. --- .../android/app/build.gradle | 105 ------------------ .../android/app/build.gradle.kts | 103 +++++++++++++++++ .../android/app/src/main/AndroidManifest.xml | 14 ++- .../MainActivity.kt | 3 +- {{cookiecutter.out_dir}}/android/build.gradle | 18 --- .../android/build.gradle.kts | 24 ++++ .../android/gradle.properties | 3 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../android/settings.gradle | 30 ----- .../android/settings.gradle.kts | 27 +++++ 10 files changed, 170 insertions(+), 159 deletions(-) delete mode 100644 {{cookiecutter.out_dir}}/android/app/build.gradle create mode 100644 {{cookiecutter.out_dir}}/android/app/build.gradle.kts delete mode 100644 {{cookiecutter.out_dir}}/android/build.gradle create mode 100644 {{cookiecutter.out_dir}}/android/build.gradle.kts delete mode 100644 {{cookiecutter.out_dir}}/android/settings.gradle create mode 100644 {{cookiecutter.out_dir}}/android/settings.gradle.kts diff --git a/{{cookiecutter.out_dir}}/android/app/build.gradle b/{{cookiecutter.out_dir}}/android/app/build.gradle deleted file mode 100644 index 3615911..0000000 --- a/{{cookiecutter.out_dir}}/android/app/build.gradle +++ /dev/null @@ -1,105 +0,0 @@ -plugins { - id "com.android.application" - id "org.jetbrains.kotlin.android" - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -android { - namespace "{{ cookiecutter.org_name_2 }}.{{ cookiecutter.package_name }}" - compileSdkVersion flutter.compileSdkVersion - - packagingOptions { - jniLibs { - useLegacyPackaging true - } - doNotStrip "*/arm64-v8a/libpython*.so" - doNotStrip "*/armeabi-v7a/libpython*.so" - doNotStrip "*/x86/libpython*.so" - doNotStrip "*/x86_64/libpython*.so" - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - - kotlinOptions { - jvmTarget = '17' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - {% set min_sdk_version = get_pyproject("tool.flet.android.min_sdk_version") %} - {% set target_sdk_version = get_pyproject("tool.flet.android.target_sdk_version") %} - - defaultConfig { - applicationId "{{ cookiecutter.org_name_2 }}.{{ cookiecutter.package_name }}" - minSdkVersion {{ min_sdk_version if min_sdk_version else "flutter.minSdkVersion" }} - targetSdkVersion {{ target_sdk_version if target_sdk_version else "flutter.targetSdkVersion" }} - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - - println("Gradle build config:") - println(" minSdkVersion: {{ min_sdk_version if min_sdk_version else "$flutter.minSdkVersion" }}") - println(" targetSdkVersion: {{ target_sdk_version if target_sdk_version else "$flutter.targetSdkVersion" }}") - println(" versionCode: $flutter.versionCode") - println(" versionName: $flutter.versionName") - -// flet: split_per_abi {% if not cookiecutter.split_per_abi %} - ndk { - {% if cookiecutter.options.target_arch %} - abiFilters {% for arch in cookiecutter.options.target_arch %}'{{ arch }}'{% if not loop.last %}, {% endif %}{% endfor %} - {% else %} - abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64' - {% endif %} - } -// flet: end of split_per_abi {% endif %} - } - -// flet: android_signing {% if cookiecutter.options.android_signing %} - signingConfigs { - release { - keyAlias System.getenv('FLET_ANDROID_SIGNING_KEY_ALIAS') - keyPassword System.getenv('FLET_ANDROID_SIGNING_KEY_PASSWORD') - storeFile System.getenv('FLET_ANDROID_SIGNING_KEY_STORE') ? file(System.getenv('FLET_ANDROID_SIGNING_KEY_STORE')) : null - storePassword System.getenv('FLET_ANDROID_SIGNING_KEY_STORE_PASSWORD') - } - } -// flet: end of android_signing {% endif %} - - buildTypes { - release { -// flet: android_signing {% if cookiecutter.options.android_signing %} - signingConfig signingConfigs.release -// {% else %} - signingConfig signingConfigs.debug -// flet: end of android_signing {% endif %} - } - } -} - -flutter { - source '../..' -} - -dependencies {} diff --git a/{{cookiecutter.out_dir}}/android/app/build.gradle.kts b/{{cookiecutter.out_dir}}/android/app/build.gradle.kts new file mode 100644 index 0000000..0e25dd9 --- /dev/null +++ b/{{cookiecutter.out_dir}}/android/app/build.gradle.kts @@ -0,0 +1,103 @@ +import java.util.Properties + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +val localProperties = Properties().apply { + val localPropertiesFile = rootProject.file("local.properties") + if (localPropertiesFile.exists()) { + localPropertiesFile.inputStream().use { load(it) } + } +} + +val flutterVersionCode = localProperties.getProperty("flutter.versionCode")?.toIntOrNull() ?: 1 +val flutterVersionName = localProperties.getProperty("flutter.versionName") ?: "1.0" + +android { + namespace = "{{ cookiecutter.org_name_2 }}.{{ cookiecutter.package_name }}" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + packaging { + jniLibs { + useLegacyPackaging = true + keepDebugSymbols += listOf( + "*/arm64-v8a/libpython*.so", + "*/armeabi-v7a/libpython*.so", + "*/x86/libpython*.so", + "*/x86_64/libpython*.so", + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + sourceSets["main"].java.srcDir("src/main/kotlin") + + {% set min_sdk_version = get_pyproject("tool.flet.android.min_sdk_version") %} + {% set target_sdk_version = get_pyproject("tool.flet.android.target_sdk_version") %} + + defaultConfig { + applicationId = "{{ cookiecutter.org_name_2 }}.{{ cookiecutter.package_name }}" + val resolvedMinSdk = {{ min_sdk_version if min_sdk_version else "flutter.minSdkVersion" }} + minSdk = resolvedMinSdk + val resolvedTargetSdk = {{ target_sdk_version if target_sdk_version else "flutter.targetSdkVersion" }} + targetSdk = resolvedTargetSdk + versionCode = flutterVersionCode + versionName = flutterVersionName + + println("Gradle build config:") + println(" minSdkVersion: $resolvedMinSdk") + println(" targetSdkVersion: $resolvedTargetSdk") + println(" versionCode: $flutterVersionCode") + println(" versionName: $flutterVersionName") + +// flet: split_per_abi {% if not cookiecutter.split_per_abi %} + ndk { + {% if cookiecutter.options.target_arch %} + abiFilters += listOf({% for arch in cookiecutter.options.target_arch %}"{{ arch }}"{% if not loop.last %}, {% endif %}{% endfor %}) + {% else %} + abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64") + {% endif %} + } +// flet: end of split_per_abi {% endif %} + } + +// flet: android_signing {% if cookiecutter.options.android_signing %} + signingConfigs { + create("release") { + keyAlias = System.getenv("FLET_ANDROID_SIGNING_KEY_ALIAS") + keyPassword = System.getenv("FLET_ANDROID_SIGNING_KEY_PASSWORD") + storeFile = System.getenv("FLET_ANDROID_SIGNING_KEY_STORE")?.let { file(it) } + storePassword = System.getenv("FLET_ANDROID_SIGNING_KEY_STORE_PASSWORD") + } + } +// flet: end of android_signing {% endif %} + + buildTypes { + release { +// flet: android_signing {% if cookiecutter.options.android_signing %} + signingConfig = signingConfigs.getByName("release") +// {% else %} + signingConfig = signingConfigs.getByName("debug") +// flet: end of android_signing {% endif %} + } + } +} + +flutter { + source = "../.." +} + +dependencies {} diff --git a/{{cookiecutter.out_dir}}/android/app/src/main/AndroidManifest.xml b/{{cookiecutter.out_dir}}/android/app/src/main/AndroidManifest.xml index ea5ee3f..b01da97 100644 --- a/{{cookiecutter.out_dir}}/android/app/src/main/AndroidManifest.xml +++ b/{{cookiecutter.out_dir}}/android/app/src/main/AndroidManifest.xml @@ -23,6 +23,7 @@ android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" + android:taskAffinity="" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" @@ -57,4 +58,15 @@ android:name="flutterEmbedding" android:value="2" /> - \ No newline at end of file + + + + + + + + diff --git a/{{cookiecutter.out_dir}}/android/app/src/main/kotlin/{{ cookiecutter.kotlin_dir }}/MainActivity.kt b/{{cookiecutter.out_dir}}/android/app/src/main/kotlin/{{ cookiecutter.kotlin_dir }}/MainActivity.kt index 8e797f0..8ab8e07 100644 --- a/{{cookiecutter.out_dir}}/android/app/src/main/kotlin/{{ cookiecutter.kotlin_dir }}/MainActivity.kt +++ b/{{cookiecutter.out_dir}}/android/app/src/main/kotlin/{{ cookiecutter.kotlin_dir }}/MainActivity.kt @@ -2,5 +2,4 @@ package {{ cookiecutter.org_name_2 }}.{{ cookiecutter.package_name }} import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() { -} +class MainActivity : FlutterActivity() diff --git a/{{cookiecutter.out_dir}}/android/build.gradle b/{{cookiecutter.out_dir}}/android/build.gradle deleted file mode 100644 index bc157bd..0000000 --- a/{{cookiecutter.out_dir}}/android/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/{{cookiecutter.out_dir}}/android/build.gradle.kts b/{{cookiecutter.out_dir}}/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/{{cookiecutter.out_dir}}/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/{{cookiecutter.out_dir}}/android/gradle.properties b/{{cookiecutter.out_dir}}/android/gradle.properties index 2597170..fbee1d8 100644 --- a/{{cookiecutter.out_dir}}/android/gradle.properties +++ b/{{cookiecutter.out_dir}}/android/gradle.properties @@ -1,3 +1,2 @@ -org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true -android.enableJetifier=true diff --git a/{{cookiecutter.out_dir}}/android/gradle/wrapper/gradle-wrapper.properties b/{{cookiecutter.out_dir}}/android/gradle/wrapper/gradle-wrapper.properties index 6b74f0f..e4ef43f 100644 --- a/{{cookiecutter.out_dir}}/android/gradle/wrapper/gradle-wrapper.properties +++ b/{{cookiecutter.out_dir}}/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/{{cookiecutter.out_dir}}/android/settings.gradle b/{{cookiecutter.out_dir}}/android/settings.gradle deleted file mode 100644 index 68cadce..0000000 --- a/{{cookiecutter.out_dir}}/android/settings.gradle +++ /dev/null @@ -1,30 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - } - settings.ext.flutterSdkPath = flutterSdkPath() - - includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } - - plugins { - id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.6.1" apply false - id "org.jetbrains.kotlin.android" version "2.1.0" apply false -} - -include ":app" diff --git a/{{cookiecutter.out_dir}}/android/settings.gradle.kts b/{{cookiecutter.out_dir}}/android/settings.gradle.kts new file mode 100644 index 0000000..e095d7c --- /dev/null +++ b/{{cookiecutter.out_dir}}/android/settings.gradle.kts @@ -0,0 +1,27 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + settings.extra["flutterSdkPath"] = flutterSdkPath + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false +} + +include(":app")