Browse Source

Add `nav_cupcake` example (#4826)

Move https://github.com/MatkovIvan/nav_cupcake repo to the examples
folder.

This is a Compose Multiplatform adaptation of [Navigate between screens
with
Compose](https://developer.android.com/codelabs/basic-android-kotlin-compose-navigation)
codelab

<img width="2040" alt="multiplatform_screenshot_light"
src="https://github.com/JetBrains/compose-multiplatform/assets/1836384/a579574a-44fe-414c-8a89-1507aaeea250">
<img width="2040" alt="multiplatform_screenshot_dark"
src="https://github.com/JetBrains/compose-multiplatform/assets/1836384/a216841a-958e-4597-a98b-2198a203228e">
igor.demin/fix-skiko-api-2
Ivan Matkov 6 months ago committed by GitHub
parent
commit
f9585f54d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      examples/nav_cupcake/.gitignore
  2. 4
      examples/nav_cupcake/README.md
  3. 9
      examples/nav_cupcake/build.gradle.kts
  4. 149
      examples/nav_cupcake/composeApp/build.gradle.kts
  5. 20
      examples/nav_cupcake/composeApp/src/androidMain/AndroidManifest.xml
  6. 26
      examples/nav_cupcake/composeApp/src/androidMain/kotlin/org/jetbrains/nav_cupcake/MainActivity.kt
  7. 3
      examples/nav_cupcake/composeApp/src/androidMain/res/values/strings.xml
  8. 44
      examples/nav_cupcake/composeApp/src/commonMain/composeResources/drawable/cupcake.xml
  9. 28
      examples/nav_cupcake/composeApp/src/commonMain/composeResources/values/strings.xml
  10. 10
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/App.kt
  11. 185
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/CupcakeScreen.kt
  12. 35
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/data/DataSource.kt
  13. 33
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/data/OrderUiState.kt
  14. 114
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/OrderViewModel.kt
  15. 135
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/SelectOptionScreen.kt
  16. 116
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/StartOrderScreen.kt
  17. 123
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/SummaryScreen.kt
  18. 36
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/components/CommonUi.kt
  19. 78
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/theme/Color.kt
  20. 102
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/theme/Theme.kt
  21. 33
      examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/theme/Type.kt
  22. 9
      examples/nav_cupcake/composeApp/src/desktopMain/kotlin/main.kt
  23. 3
      examples/nav_cupcake/composeApp/src/iosMain/kotlin/MainViewController.kt
  24. 7
      examples/nav_cupcake/composeApp/src/wasmJsMain/kotlin/main.kt
  25. 12
      examples/nav_cupcake/composeApp/src/wasmJsMain/resources/index.html
  26. 19
      examples/nav_cupcake/gradle.properties
  27. 32
      examples/nav_cupcake/gradle/libs.versions.toml
  28. BIN
      examples/nav_cupcake/gradle/wrapper/gradle-wrapper.jar
  29. 7
      examples/nav_cupcake/gradle/wrapper/gradle-wrapper.properties
  30. 248
      examples/nav_cupcake/gradlew
  31. 92
      examples/nav_cupcake/gradlew.bat
  32. BIN
      examples/nav_cupcake/images/multiplatform_screenshot_dark.png
  33. BIN
      examples/nav_cupcake/images/multiplatform_screenshot_light.png
  34. 3
      examples/nav_cupcake/iosApp/Configuration/Config.xcconfig
  35. 398
      examples/nav_cupcake/iosApp/iosApp.xcodeproj/project.pbxproj
  36. 11
      examples/nav_cupcake/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
  37. 14
      examples/nav_cupcake/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
  38. BIN
      examples/nav_cupcake/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png
  39. 6
      examples/nav_cupcake/iosApp/iosApp/Assets.xcassets/Contents.json
  40. 21
      examples/nav_cupcake/iosApp/iosApp/ContentView.swift
  41. 50
      examples/nav_cupcake/iosApp/iosApp/Info.plist
  42. 6
      examples/nav_cupcake/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
  43. 10
      examples/nav_cupcake/iosApp/iosApp/iOSApp.swift
  44. 3042
      examples/nav_cupcake/kotlin-js-store/yarn.lock
  45. 21
      examples/nav_cupcake/settings.gradle.kts

18
examples/nav_cupcake/.gitignore vendored

@ -0,0 +1,18 @@
*.iml
.gradle
.kotlin
**/build/
xcuserdata
!src/**/build/
local.properties
.idea
.DS_Store
captures
.externalNativeBuild
.cxx
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata
**/xcshareddata/WorkspaceSettings.xcsettings

4
examples/nav_cupcake/README.md

@ -0,0 +1,4 @@
This is a Compose Multiplatform adaptation of [Navigate between screens with Compose](https://developer.android.com/codelabs/basic-android-kotlin-compose-navigation) codelab
![Multiplatform Screenshot - Light](images/multiplatform_screenshot_light.png)
![Multiplatform Screenshot - Dark](images/multiplatform_screenshot_dark.png)

9
examples/nav_cupcake/build.gradle.kts

@ -0,0 +1,9 @@
plugins {
// this is necessary to avoid the plugins to be loaded multiple times
// in each subproject's classloader
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.androidLibrary) apply false
alias(libs.plugins.kotlinMultiplatform) apply false
alias(libs.plugins.composeMultiplatform) apply false
alias(libs.plugins.composeCompiler) apply false
}

149
examples/nav_cupcake/composeApp/build.gradle.kts

@ -0,0 +1,149 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidApplication)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.composeCompiler)
}
kotlin {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = "composeApp"
browser {
commonWebpackConfig {
outputFileName = "composeApp.js"
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
static = (static ?: mutableListOf()).apply {
// Serve sources to debug inside browser
add(project.projectDir.path)
}
}
}
}
binaries.executable()
}
androidTarget {
compilations.all {
kotlinOptions {
jvmTarget = "11"
}
}
}
jvm("desktop")
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "ComposeApp"
isStatic = true
}
}
sourceSets {
all {
languageSettings {
optIn("androidx.compose.material3.ExperimentalMaterial3Api")
optIn("org.jetbrains.compose.resources.ExperimentalResourceApi")
}
}
val commonMain by getting
val jbMain by creating {
dependsOn(commonMain)
}
val desktopMain by getting {
dependsOn(jbMain)
}
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(jbMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val wasmJsMain by getting {
dependsOn(jbMain)
}
androidMain.dependencies {
implementation(libs.compose.ui.tooling.preview)
implementation(libs.androidx.activity.compose)
}
commonMain.dependencies {
implementation(libs.kotlinx.datetime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.navigation.compose)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)
}
}
}
android {
namespace = "org.jetbrains.nav_cupcake"
compileSdk = libs.versions.android.compileSdk.get().toInt()
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
sourceSets["main"].res.srcDirs("src/androidMain/res")
sourceSets["main"].resources.srcDirs("src/commonMain/resources")
defaultConfig {
applicationId = "org.jetbrains.nav_cupcake"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
dependencies {
debugImplementation(libs.compose.ui.tooling)
}
}
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "org.jetbrains.nav_cupcake"
packageVersion = "1.0.0"
}
}
}
compose.experimental {
web.application {}
}

20
examples/nav_cupcake/composeApp/src/androidMain/AndroidManifest.xml

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@android:style/Theme.Material.Light.NoActionBar">
<activity
android:exported="true"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

26
examples/nav_cupcake/composeApp/src/androidMain/kotlin/org/jetbrains/nav_cupcake/MainActivity.kt

@ -0,0 +1,26 @@
package org.jetbrains.nav_cupcake
import App
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
setContent {
App()
}
}
}
@Preview
@Composable
fun AppAndroidPreview() {
App()
}

3
examples/nav_cupcake/composeApp/src/androidMain/res/values/strings.xml

@ -0,0 +1,3 @@
<resources>
<string name="app_name">Cupcake</string>
</resources>

44
examples/nav_cupcake/composeApp/src/commonMain/composeResources/drawable/cupcake.xml

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Cupcake icon for the start fragment -->
<vector android:height="200dp" android:viewportHeight="413.71"
android:viewportWidth="308.15" android:width="148.96909dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#4fc3f7" android:pathData="M34.33,218.79l-27.63,-11.72l-6.7,27.68l34.33,-15.96z"/>
<path android:fillColor="#4fc3f7" android:pathData="M273.75,218.79l27.63,-11.72l6.69,27.68l-34.32,-15.96z"/>
<path android:fillColor="#4fc3f7" android:pathData="M27.22,225.91l20.35,-30.56l29.24,22.58l24.1,-27.94l27.32,24.84l25.84,-26.36l25.85,26.36l27.32,-24.84l24.1,27.94l29.24,-22.58l20.35,30.56"/>
<path android:fillColor="#03a9f4" android:pathData="M280.93,233.84l-20.35,30.57l-29.24,-22.59l-24.1,27.95l-27.32,-24.84l-25.85,26.36l-25.84,-26.36l-27.32,24.84l-24.1,-27.95l-29.24,22.59l-20.35,-30.57"/>
<path android:fillColor="#ffe0b2" android:pathData="M6.72,245.77a147.35,49.81 0,1 0,294.7 0a147.35,49.81 0,1 0,-294.7 0z"/>
<path android:fillColor="#81d4fa" android:pathData="M308.15,234.77 L289.5,219.22l-21.58,31L237.06,228l-25.52,28.14 -28.88,-24.72L155.3,257.8 128,231.41 99.07,256.13 73.54,228 42.68,250.25l-21.58,-31L0,234.75 29.3,380.43C41,424.8 268.46,424.8 281,380.43Z"/>
<path android:fillColor="#4fc3f7" android:pathData="M296.79,290a177.71,177.71 0,0 1,-14.08 22.93c-6.49,8.8 -12.95,17.78 -20.38,25.84 -7.2,7.83 -16.18,14 -24.61,20.32 -26.6,19.82 -63.2,26.79 -95.82,20.47C119.55,377.93 101.2,373 81.14,362c-27.72,-15.16 -49.54,-41.31 -65.72,-67.86 -1.86,-3.05 -3.63,-6.14 -5.34,-9.27L29.3,380.43C41,424.8 268.46,424.8 281,380.43l17.45,-93.7C297.92,287.82 297.36,288.91 296.79,290Z"/>
<path android:fillColor="#29b6f6" android:pathData="M58.88,378.74a2,2 0,0 1,-2 -1.74L42.39,263.14a2,2 0,1 1,4 -0.51L60.87,376.49a2,2 0,0 1,-1.73 2.24Z"/>
<path android:fillColor="#29b6f6" android:pathData="M104.17,385.44a2,2 0,0 1,-2 -1.92l-4.5,-116.09a2,2 0,1 1,4 -0.16l4.51,116.09a2,2 0,0 1,-1.92 2.08Z"/>
<path android:fillColor="#29b6f6" android:pathData="M252.08,378.74h-0.26a2,2 0,0 1,-1.73 -2.24L264.6,262.63a2,2 0,1 1,4 0.51L254.06,377A2,2 0,0 1,252.08 378.74Z"/>
<path android:fillColor="#29b6f6" android:pathData="M206.79,385.44h-0.08a2,2 0,0 1,-1.92 -2.08l4.5,-116.09a2,2 0,0 1,4 0.16l-4.5,116.09A2,2 0,0 1,206.79 385.44Z"/>
<path android:fillColor="#29b6f6" android:pathData="M155.18,388.79a2,2 0,0 1,-2 -2V270.14a2,2 0,0 1,4 0V386.79A2,2 0,0 1,155.18 388.79Z"/>
<path android:fillColor="#f06292" android:pathData="M174.42,0.11S186.94,47.44 104,57.92c0,0 -48.29,5.29 -33.19,37.35a21.64,21.64 0,0 1,-5.47 25.88C47.92,135.74 24.89,164.84 46.08,207a13.44,13.44 0,0 0,7.28 6.57c20.12,7.53 101.54,33.31 201.88,1.32a13.21,13.21 0,0 0,6.3 -4.34c6.57,-8.21 20.38,-31.37 -2.24,-60.91A26.16,26.16 0,0 1,254 135.12c-0.51,-9.9 -4.73,-27.17 -25.88,-39.45a13.57,13.57 0,0 1,-5.69 -16.92c4,-9.75 7.66,-24.1 2,-36.44C214.6,21.28 174.42,0.11 174.42,0.11Z"/>
<path android:fillColor="#ec407a" android:pathData="M256,204.18c-18.82,3.85 -38.66,6.32 -58,5.56a342.08,342.08 0,0 1,-54.75 -7,191.23 191.23,0 0,1 -31,-9 372.7,372.7 0,0 1,-43.43 -19.84c-9.64,-5.25 -19.52,-11.32 -28.07,-18.59 -4.75,14.09 -4.81,31.38 5.4,51.71a13.44,13.44 0,0 0,7.28 6.57c20.12,7.53 101.54,33.31 201.88,1.31a13.12,13.12 0,0 0,6.3 -4.33,48.91 48.91,0 0,0 5.89,-9.43A105.18,105.18 0,0 1,256 204.18Z"/>
<path android:fillColor="#f48fb1" android:pathData="M222.18,89c-28,24.79 -65.43,27.57 -102.41,21.79 -15.62,-2.44 -28.25,-11.48 -42.54,-4.72 -2.93,1.38 -6,4 -5.48,7.19 15.75,0.68 27.44,6.57 42.6,10.88 22.47,6.38 43.32,17.56 65.72,24.18s48,8.13 67.88,-4.07c2.46,-1.51 4.94,-3.39 5.87,-6.13a12.71,12.71 0,0 0,0.06 -6.31c-3.28,-18.77 -15,-33 -31.43,-42.81"/>
<path android:fillColor="#673ab7" android:pathData="M108.057,137.999L141.83,124.422A3.105,3.11 68.099,0 1,145.874 126.143L145.874,126.143A3.105,3.11 68.099,0 1,144.146 130.184L110.373,143.761A3.105,3.11 68.099,0 1,106.329 142.04L106.329,142.04A3.105,3.11 68.099,0 1,108.057 137.999z"/>
<path android:fillColor="#ffee58" android:pathData="M63.656,78.785L63.656,78.785A3.105,3.11 110.731,0 1,67.664 76.982L101.707,89.866A3.105,3.11 110.731,0 1,103.517 93.871L103.517,93.871A3.105,3.11 110.731,0 1,99.509 95.674L65.466,82.79A3.105,3.11 110.731,0 1,63.656 78.785z"/>
<path android:fillColor="#80deea" android:pathData="M223.715,131.605L241.916,99.937A3.121,3.116 119.885,0 1,246.172 98.784L246.172,98.784A3.121,3.116 119.885,0 1,247.318 103.042L229.118,134.71A3.121,3.116 119.885,0 1,224.862 135.864L224.862,135.864A3.121,3.116 119.885,0 1,223.715 131.605z"/>
<path android:fillColor="#8bc34a" android:pathData="M158.163,63.484L158.163,63.484A3.105,3.11 107.861,0 1,162.076 61.482L196.722,72.646A3.105,3.11 107.861,0 1,198.729 76.555L198.729,76.555A3.105,3.11 107.861,0 1,194.817 78.557L160.171,67.393A3.105,3.11 107.861,0 1,158.163 63.484z"/>
<path android:fillColor="#673ab7" android:pathData="M181.372,45.493L201.621,15.246A3.11,3.105 123.799,0 1,205.931 14.389L205.931,14.389A3.11,3.105 123.799,0 1,206.781 18.7L186.532,48.948A3.11,3.105 123.799,0 1,182.222 49.805L182.222,49.805A3.11,3.105 123.799,0 1,181.372 45.493z"/>
<path android:fillColor="#8bc34a" android:pathData="M132.014,205.938L152.263,175.691A3.11,3.105 123.799,0 1,156.574 174.834L156.574,174.834A3.11,3.105 123.799,0 1,157.424 179.145L137.175,209.393A3.11,3.105 123.799,0 1,132.864 210.25L132.864,210.25A3.11,3.105 123.799,0 1,132.014 205.938z"/>
<path android:fillColor="#ffee58" android:pathData="M203.177,166.031L203.177,166.031A3.105,3.11 104.521,0 1,206.966 163.805L242.204,172.931A3.105,3.11 104.521,0 1,244.436 176.716L244.436,176.716A3.105,3.11 104.521,0 1,240.647 178.942L205.409,169.816A3.105,3.11 104.521,0 1,203.177 166.031z"/>
<path android:fillColor="#80deea" android:pathData="M34.294,179.55L56.164,150.453A3.11,3.105 126.929,0 1,60.515 149.832L60.515,149.832A3.11,3.105 126.929,0 1,61.128 154.184L39.258,183.281A3.11,3.105 126.929,0 1,34.907 183.902L34.907,183.902A3.11,3.105 126.929,0 1,34.294 179.55z"/>
<path android:fillColor="#f48fb1" android:pathData="M174.6,0.19c5.85,5.93 5.7,15.41 2.45,23.08a51.17,51.17 0,0 1,-16 20c-22,16.43 -49.79,24.94 -77.21,24.27 -1.61,0 -3.64,-0.43 -4,-2 1.47,-2.29 4.27,-3.29 6.89,-4 12.66,-3.58 26,-5.8 38.46,-10.07C148.61,43.36 169,33.86 174.42,0"/>
</vector>

28
examples/nav_cupcake/composeApp/src/commonMain/composeResources/values/strings.xml

@ -0,0 +1,28 @@
<resources>
<string name="app_name">Cupcake</string>
<string name="order_cupcakes">Order Cupcakes</string>
<string name="one_cupcake">One Cupcake</string>
<string name="six_cupcakes">Six Cupcakes</string>
<string name="twelve_cupcakes">Twelve Cupcakes</string>
<string name="choose_flavor">Choose Flavor</string>
<string name="vanilla">Vanilla</string>
<string name="chocolate">Chocolate</string>
<string name="red_velvet">Red Velvet</string>
<string name="salted_caramel">Salted Caramel</string>
<string name="coffee">Coffee</string>
<string name="special_flavor">Special Flavor</string>
<string name="cancel">Cancel</string>
<string name="next">Next</string>
<string name="choose_pickup_date">Choose Pickup Date</string>
<string name="order_summary">Order Summary</string>
<string name="send">Send Order to Another App</string>
<string name="quantity">Quantity</string>
<string name="flavor">Flavor</string>
<string name="pickup_date">Pickup date</string>
<string name="subtotal_price">Subtotal %1$s</string>
<string name="total_price">Total %1$s</string>
<string name="new_cupcake_order">New Cupcake Order</string>
<string name="order_details">Quantity: %1$s \nFlavor: %2$s \nPickup date: %3$s \nTotal: %4$s \n\nThank you!</string>
<string name="back_button">Back</string>
<string name="cupcakes">%1$d cupcakes</string>
</resources>

10
examples/nav_cupcake/composeApp/src/commonMain/kotlin/App.kt

@ -0,0 +1,10 @@
import androidx.compose.runtime.Composable
import org.jetbrains.nav_cupcake.CupcakeApp
import org.jetbrains.nav_cupcake.ui.theme.CupcakeTheme
@Composable
fun App() {
CupcakeTheme {
CupcakeApp()
}
}

185
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/CupcakeScreen.kt

@ -0,0 +1,185 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import org.jetbrains.nav_cupcake.data.DataSource
import org.jetbrains.nav_cupcake.data.OrderUiState
import org.jetbrains.nav_cupcake.ui.OrderSummaryScreen
import org.jetbrains.nav_cupcake.ui.OrderViewModel
import org.jetbrains.nav_cupcake.ui.SelectOptionScreen
import org.jetbrains.nav_cupcake.ui.StartOrderScreen
import cupcake.composeapp.generated.resources.*
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
/**
* enum values that represent the screens in the app
*/
enum class CupcakeScreen(val title: StringResource) {
Start(title = Res.string.app_name),
Flavor(title = Res.string.choose_flavor),
Pickup(title = Res.string.choose_pickup_date),
Summary(title = Res.string.order_summary)
}
/**
* Composable that displays the topBar and displays back button if back navigation is possible.
*/
@Composable
fun CupcakeAppBar(
currentScreen: CupcakeScreen,
canNavigateBack: Boolean,
navigateUp: () -> Unit,
modifier: Modifier = Modifier
) {
TopAppBar(
title = { Text(stringResource(currentScreen.title)) },
colors = TopAppBarDefaults.mediumTopAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
),
modifier = modifier,
navigationIcon = {
if (canNavigateBack) {
IconButton(onClick = navigateUp) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = stringResource(Res.string.back_button)
)
}
}
}
)
}
@Composable
fun CupcakeApp(
viewModel: OrderViewModel = viewModel { OrderViewModel() },
navController: NavHostController = rememberNavController()
) {
// Get current back stack entry
val backStackEntry by navController.currentBackStackEntryAsState()
// Get the name of the current screen
val currentScreen = CupcakeScreen.valueOf(
backStackEntry?.destination?.route ?: CupcakeScreen.Start.name
)
Scaffold(
topBar = {
CupcakeAppBar(
currentScreen = currentScreen,
canNavigateBack = navController.previousBackStackEntry != null,
navigateUp = { navController.navigateUp() }
)
}
) { innerPadding ->
val uiState by viewModel.uiState.collectAsState()
NavHost(
navController = navController,
startDestination = CupcakeScreen.Start.name,
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(innerPadding)
) {
composable(route = CupcakeScreen.Start.name) {
StartOrderScreen(
quantityOptions = DataSource.quantityOptions,
onNextButtonClicked = {
viewModel.setQuantity(it)
navController.navigate(CupcakeScreen.Flavor.name)
},
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
)
}
composable(route = CupcakeScreen.Flavor.name) {
SelectOptionScreen(
subtotal = uiState.price,
onNextButtonClicked = { navController.navigate(CupcakeScreen.Pickup.name) },
onCancelButtonClicked = {
cancelOrderAndNavigateToStart(viewModel, navController)
},
options = DataSource.flavors.map { id -> stringResource(id) },
onSelectionChanged = { viewModel.setFlavor(it) },
modifier = Modifier.fillMaxHeight()
)
}
composable(route = CupcakeScreen.Pickup.name) {
SelectOptionScreen(
subtotal = uiState.price,
onNextButtonClicked = { navController.navigate(CupcakeScreen.Summary.name) },
onCancelButtonClicked = {
cancelOrderAndNavigateToStart(viewModel, navController)
},
options = uiState.pickupOptions,
onSelectionChanged = { viewModel.setDate(it) },
modifier = Modifier.fillMaxHeight()
)
}
composable(route = CupcakeScreen.Summary.name) {
OrderSummaryScreen(
orderUiState = uiState,
onCancelButtonClicked = {
cancelOrderAndNavigateToStart(viewModel, navController)
},
onSendButtonClicked = { subject: String, summary: String ->
shareOrder(subject = subject, summary = summary)
},
modifier = Modifier.fillMaxHeight()
)
}
}
}
}
/**
* Resets the [OrderUiState] and pops up to [CupcakeScreen.Start]
*/
private fun cancelOrderAndNavigateToStart(
viewModel: OrderViewModel,
navController: NavHostController
) {
viewModel.resetOrder()
navController.popBackStack(CupcakeScreen.Start.name, inclusive = false)
}
/**
* Creates an intent to share order details
*/
private fun shareOrder(subject: String, summary: String) {
// TODO
}

35
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/data/DataSource.kt

@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.data
import cupcake.composeapp.generated.resources.Res
import cupcake.composeapp.generated.resources.*
object DataSource {
val flavors = listOf(
Res.string.vanilla,
Res.string.red_velvet,
Res.string.chocolate,
Res.string.salted_caramel,
Res.string.coffee
)
val quantityOptions = listOf(
Pair(Res.string.one_cupcake, 1),
Pair(Res.string.six_cupcakes, 6),
Pair(Res.string.twelve_cupcakes, 12)
)
}

33
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/data/OrderUiState.kt

@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.data
/**
* Data class that represents the current UI state in terms of [quantity], [flavor],
* [dateOptions], selected pickup [date] and [price]
*/
data class OrderUiState(
/** Selected cupcake quantity (1, 6, 12) */
val quantity: Int = 0,
/** Flavor of the cupcakes in the order (such as "Chocolate", "Vanilla", etc..) */
val flavor: String = "",
/** Selected date for pickup (such as "Jan 1") */
val date: String = "",
/** Total price for the order */
val price: String = "",
/** Available pickup dates for the order*/
val pickupOptions: List<String> = listOf()
)

114
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/OrderViewModel.kt

@ -0,0 +1,114 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.ui
import androidx.lifecycle.ViewModel
import org.jetbrains.nav_cupcake.data.OrderUiState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.datetime.*
/** Price for a single cupcake */
private const val PRICE_PER_CUPCAKE = 2.00
/** Additional cost for same day pickup of an order */
private const val PRICE_FOR_SAME_DAY_PICKUP = 3.00
/**
* [OrderViewModel] holds information about a cupcake order in terms of quantity, flavor, and
* pickup date. It also knows how to calculate the total price based on these order details.
*/
class OrderViewModel : ViewModel() {
/**
* Cupcake state for this order
*/
private val _uiState = MutableStateFlow(OrderUiState(pickupOptions = pickupOptions()))
val uiState: StateFlow<OrderUiState> = _uiState.asStateFlow()
/**
* Set the quantity [numberCupcakes] of cupcakes for this order's state and update the price
*/
fun setQuantity(numberCupcakes: Int) {
_uiState.update { currentState ->
currentState.copy(
quantity = numberCupcakes,
price = calculatePrice(quantity = numberCupcakes)
)
}
}
/**
* Set the [desiredFlavor] of cupcakes for this order's state.
* Only 1 flavor can be selected for the whole order.
*/
fun setFlavor(desiredFlavor: String) {
_uiState.update { currentState ->
currentState.copy(flavor = desiredFlavor)
}
}
/**
* Set the [pickupDate] for this order's state and update the price
*/
fun setDate(pickupDate: String) {
_uiState.update { currentState ->
currentState.copy(
date = pickupDate,
price = calculatePrice(pickupDate = pickupDate)
)
}
}
/**
* Reset the order state
*/
fun resetOrder() {
_uiState.value = OrderUiState(pickupOptions = pickupOptions())
}
/**
* Returns the calculated price based on the order details.
*/
private fun calculatePrice(
quantity: Int = _uiState.value.quantity,
pickupDate: String = _uiState.value.date
): String {
var calculatedPrice = quantity * PRICE_PER_CUPCAKE
// If the user selected the first option (today) for pickup, add the surcharge
if (pickupOptions()[0] == pickupDate) {
calculatedPrice += PRICE_FOR_SAME_DAY_PICKUP
}
return "$calculatedPrice"
}
/**
* Returns a list of date options starting with the current date and the following 3 dates.
*/
private fun pickupOptions(): List<String> {
val dateOptions = mutableListOf<String>()
val now = Clock.System.now()
val timeZone = TimeZone.currentSystemDefault()
// add current date and the following 3 dates.
repeat(4) {
val day = now.plus(it, DateTimeUnit.DAY, timeZone)
dateOptions.add(day.toLocalDateTime(timeZone).date.toString())
}
return dateOptions
}
}

135
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/SelectOptionScreen.kt

@ -0,0 +1,135 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.selectable
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import org.jetbrains.nav_cupcake.ui.components.FormattedPriceLabel
import org.jetbrains.nav_cupcake.ui.theme.CupcakeTheme
import cupcake.composeapp.generated.resources.Res
import cupcake.composeapp.generated.resources.cancel
import cupcake.composeapp.generated.resources.next
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview
/**
* Composable that displays the list of items as [RadioButton] options,
* [onSelectionChanged] lambda that notifies the parent composable when a new value is selected,
* [onCancelButtonClicked] lambda that cancels the order when user clicks cancel and
* [onNextButtonClicked] lambda that triggers the navigation to next screen
*/
@Composable
fun SelectOptionScreen(
subtotal: String,
options: List<String>,
onSelectionChanged: (String) -> Unit = {},
onCancelButtonClicked: () -> Unit = {},
onNextButtonClicked: () -> Unit = {},
modifier: Modifier = Modifier
) {
var selectedValue by rememberSaveable { mutableStateOf("") }
Column(
modifier = modifier,
verticalArrangement = Arrangement.SpaceBetween
) {
Column(modifier = Modifier.padding(16.dp)) {
options.forEach { item ->
Row(
modifier = Modifier.selectable(
selected = selectedValue == item,
onClick = {
selectedValue = item
onSelectionChanged(item)
}
),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = selectedValue == item,
onClick = {
selectedValue = item
onSelectionChanged(item)
}
)
Text(item)
}
}
HorizontalDivider(
thickness = 1.dp,
modifier = Modifier.padding(bottom = 16.dp)
)
FormattedPriceLabel(
subtotal = subtotal,
modifier = Modifier
.align(Alignment.End)
.padding(
top = 16.dp,
bottom = 16.dp
)
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.Bottom
) {
OutlinedButton(
modifier = Modifier.weight(1f),
onClick = onCancelButtonClicked
) {
Text(stringResource(Res.string.cancel))
}
Button(
modifier = Modifier.weight(1f),
// the button is enabled when the user makes a selection
enabled = selectedValue.isNotEmpty(),
onClick = onNextButtonClicked
) {
Text(stringResource(Res.string.next))
}
}
}
}
@Preview
@Composable
fun SelectOptionPreview() {
CupcakeTheme {
SelectOptionScreen(
subtotal = "299.99",
options = listOf("Option 1", "Option 2", "Option 3", "Option 4"),
modifier = Modifier.fillMaxHeight()
)
}
}

116
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/StartOrderScreen.kt

@ -0,0 +1,116 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import org.jetbrains.nav_cupcake.data.DataSource
import org.jetbrains.nav_cupcake.ui.theme.CupcakeTheme
import cupcake.composeapp.generated.resources.Res
import cupcake.composeapp.generated.resources.cupcake
import cupcake.composeapp.generated.resources.order_cupcakes
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview
/**
* Composable that allows the user to select the desired cupcake quantity and expects
* [onNextButtonClicked] lambda that expects the selected quantity and triggers the navigation to
* next screen
*/
@Composable
fun StartOrderScreen(
quantityOptions: List<Pair<StringResource, Int>>,
onNextButtonClicked: (Int) -> Unit,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.SpaceBetween
) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Spacer(modifier = Modifier.height(16.dp))
Image(
painter = painterResource(Res.drawable.cupcake),
contentDescription = null,
modifier = Modifier.width(300.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(Res.string.order_cupcakes),
style = MaterialTheme.typography.headlineSmall
)
Spacer(modifier = Modifier.height(8.dp))
}
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
quantityOptions.forEach { item ->
SelectQuantityButton(
labelResource = item.first,
onClick = { onNextButtonClicked(item.second) },
modifier = Modifier.fillMaxWidth(),
)
}
}
}
}
/**
* Customizable button composable that displays the [labelResourceId]
* and triggers [onClick] lambda when this composable is clicked
*/
@Composable
fun SelectQuantityButton(
labelResource: StringResource,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Button(
onClick = onClick,
modifier = modifier.widthIn(min = 250.dp)
) {
Text(stringResource(labelResource))
}
}
@Preview
@Composable
fun StartOrderPreview() {
CupcakeTheme {
StartOrderScreen(
quantityOptions = DataSource.quantityOptions,
onNextButtonClicked = {},
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
)
}
}

123
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/SummaryScreen.kt

@ -0,0 +1,123 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.ui
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import org.jetbrains.nav_cupcake.data.OrderUiState
import org.jetbrains.nav_cupcake.ui.components.FormattedPriceLabel
import org.jetbrains.nav_cupcake.ui.theme.CupcakeTheme
import cupcake.composeapp.generated.resources.*
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview
/**
* This composable expects [orderUiState] that represents the order state, [onCancelButtonClicked]
* lambda that triggers canceling the order and passes the final order to [onSendButtonClicked]
* lambda
*/
@Composable
fun OrderSummaryScreen(
orderUiState: OrderUiState,
onCancelButtonClicked: () -> Unit,
onSendButtonClicked: (String, String) -> Unit,
modifier: Modifier = Modifier
) {
val numberOfCupcakes = stringResource(
Res.string.cupcakes, // TODO: plurals
orderUiState.quantity
)
//Load and format a string resource with the parameters.
val orderSummary = stringResource(
Res.string.order_details,
numberOfCupcakes,
orderUiState.flavor,
orderUiState.date,
orderUiState.quantity
)
val newOrder = stringResource(Res.string.new_cupcake_order)
//Create a list of order summary to display
val items = listOf(
// Summary line 1: display selected quantity
Pair(stringResource(Res.string.quantity), numberOfCupcakes),
// Summary line 2: display selected flavor
Pair(stringResource(Res.string.flavor), orderUiState.flavor),
// Summary line 3: display selected pickup date
Pair(stringResource(Res.string.pickup_date), orderUiState.date)
)
Column(
modifier = modifier,
verticalArrangement = Arrangement.SpaceBetween
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items.forEach { item ->
Text(item.first.uppercase())
Text(text = item.second, fontWeight = FontWeight.Bold)
HorizontalDivider(thickness = 1.dp)
}
Spacer(modifier = Modifier.height(8.dp))
FormattedPriceLabel(
subtotal = orderUiState.price,
modifier = Modifier.align(Alignment.End)
)
}
Row(
modifier = Modifier.padding(16.dp)
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { onSendButtonClicked(newOrder, orderSummary) }
) {
Text(stringResource(Res.string.send))
}
OutlinedButton(
modifier = Modifier.fillMaxWidth(),
onClick = onCancelButtonClicked
) {
Text(stringResource(Res.string.cancel))
}
}
}
}
}
@Preview
@Composable
fun OrderSummaryPreview() {
CupcakeTheme {
OrderSummaryScreen(
orderUiState = OrderUiState(0, "Test", "Test", "$300.00"),
onSendButtonClicked = { subject: String, summary: String -> },
onCancelButtonClicked = {},
modifier = Modifier.fillMaxHeight()
)
}
}

36
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/components/CommonUi.kt

@ -0,0 +1,36 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.ui.components
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import cupcake.composeapp.generated.resources.Res
import cupcake.composeapp.generated.resources.subtotal_price
import org.jetbrains.compose.resources.stringResource
/**
* Composable that displays formatted [price] that will be formatted and displayed on screen
*/
@Composable
fun FormattedPriceLabel(subtotal: String, modifier: Modifier = Modifier) {
Text(
text = stringResource(Res.string.subtotal_price, subtotal),
modifier = modifier,
style = MaterialTheme.typography.headlineSmall
)
}

78
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/theme/Color.kt

@ -0,0 +1,78 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.ui.theme
import androidx.compose.ui.graphics.Color
val md_theme_light_primary = Color(0xFF984062)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFFFD9E2)
val md_theme_light_onPrimaryContainer = Color(0xFF3E001E)
val md_theme_light_secondary = Color(0xFF74565F)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFFFD9E2)
val md_theme_light_onSecondaryContainer = Color(0xFF2B151C)
val md_theme_light_tertiary = Color(0xFF7C5635)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFFFDCC2)
val md_theme_light_onTertiaryContainer = Color(0xFF2E1500)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF201A1B)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF201A1B)
val md_theme_light_surfaceVariant = Color(0xFFF2DDE2)
val md_theme_light_onSurfaceVariant = Color(0xFF514347)
val md_theme_light_outline = Color(0xFF837377)
val md_theme_light_inverseOnSurface = Color(0xFFFAEEEF)
val md_theme_light_inverseSurface = Color(0xFF352F30)
val md_theme_light_inversePrimary = Color(0xFFFFB0C9)
val md_theme_light_surfaceTint = Color(0xFF984062)
val md_theme_light_outlineVariant = Color(0xFFD5C2C6)
val md_theme_light_scrim = Color(0xFF000000)
val md_theme_dark_primary = Color(0xFFFFB0C9)
val md_theme_dark_onPrimary = Color(0xFF5E1133)
val md_theme_dark_primaryContainer = Color(0xFF7B294A)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFD9E2)
val md_theme_dark_secondary = Color(0xFFE2BDC7)
val md_theme_dark_onSecondary = Color(0xFF422931)
val md_theme_dark_secondaryContainer = Color(0xFF5A3F47)
val md_theme_dark_onSecondaryContainer = Color(0xFFFFD9E2)
val md_theme_dark_tertiary = Color(0xFFEFBD94)
val md_theme_dark_onTertiary = Color(0xFF48290C)
val md_theme_dark_tertiaryContainer = Color(0xFF623F20)
val md_theme_dark_onTertiaryContainer = Color(0xFFFFDCC2)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF201A1B)
val md_theme_dark_onBackground = Color(0xFFEBE0E1)
val md_theme_dark_surface = Color(0xFF201A1B)
val md_theme_dark_onSurface = Color(0xFFEBE0E1)
val md_theme_dark_surfaceVariant = Color(0xFF514347)
val md_theme_dark_onSurfaceVariant = Color(0xFFD5C2C6)
val md_theme_dark_outline = Color(0xFF9E8C90)
val md_theme_dark_inverseOnSurface = Color(0xFF201A1B)
val md_theme_dark_inverseSurface = Color(0xFFEBE0E1)
val md_theme_dark_inversePrimary = Color(0xFF984062)
val md_theme_dark_surfaceTint = Color(0xFFFFB0C9)
val md_theme_dark_outlineVariant = Color(0xFF514347)
val md_theme_dark_scrim = Color(0xFF000000)

102
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/theme/Theme.kt

@ -0,0 +1,102 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
private val LightColors = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
private val DarkColors = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
@Composable
fun CupcakeTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colorScheme = when {
darkTheme -> DarkColors
else -> LightColors
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

33
examples/nav_cupcake/composeApp/src/commonMain/kotlin/org/jetbrains/nav_cupcake/ui/theme/Type.kt

@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.nav_cupcake.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
)

9
examples/nav_cupcake/composeApp/src/desktopMain/kotlin/main.kt

@ -0,0 +1,9 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
fun main() = application {
Window(onCloseRequest = ::exitApplication, title = "Cupcake") {
App()
}
}

3
examples/nav_cupcake/composeApp/src/iosMain/kotlin/MainViewController.kt

@ -0,0 +1,3 @@
import androidx.compose.ui.window.ComposeUIViewController
fun MainViewController() = ComposeUIViewController { App() }

7
examples/nav_cupcake/composeApp/src/wasmJsMain/kotlin/main.kt

@ -0,0 +1,7 @@
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.window.CanvasBasedWindow
@OptIn(ExperimentalComposeUiApi::class)
fun main() {
CanvasBasedWindow(canvasElementId = "ComposeTarget") { App() }
}

12
examples/nav_cupcake/composeApp/src/wasmJsMain/resources/index.html

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Compose App</title>
<script type="application/javascript" src="skiko.js"></script>
<script type="application/javascript" src="composeApp.js"></script>
</head>
<body>
<canvas id="ComposeTarget"></canvas>
</body>
</html>

19
examples/nav_cupcake/gradle.properties

@ -0,0 +1,19 @@
kotlin.code.style=official
#Gradle
org.gradle.jvmargs=-Xmx4G -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx4G"
#Android
android.nonTransitiveRClass=true
android.useAndroidX=true
#Compose
org.jetbrains.compose.experimental.wasm.enabled=true
#MPP
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.mpp.enableCInteropCommonization=true
#Development
development=true

32
examples/nav_cupcake/gradle/libs.versions.toml

@ -0,0 +1,32 @@
[versions]
agp = "8.2.0"
android-compileSdk = "34"
android-minSdk = "24"
android-targetSdk = "34"
androidx-activity = "1.8.2"
androidx-lifecycle = "2.8.0-rc03"
androidx-navigation = "2.7.0-alpha06"
compose-android = "1.6.7"
compose-multiplatform = "1.6.10-rc03"
junit = "4.13.2"
kotlin = "2.0.0-RC3"
kotlinx-datetime = "0.6.0"
[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
androidx-lifecycle-runtime-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
androidx-navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "androidx-navigation" }
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose-android" }
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose-android" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

BIN
examples/nav_cupcake/gradle/wrapper/gradle-wrapper.jar vendored

Binary file not shown.

7
examples/nav_cupcake/gradle/wrapper/gradle-wrapper.properties vendored

@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

248
examples/nav_cupcake/gradlew vendored

@ -0,0 +1,248 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
examples/nav_cupcake/gradlew.bat vendored

@ -0,0 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

BIN
examples/nav_cupcake/images/multiplatform_screenshot_dark.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
examples/nav_cupcake/images/multiplatform_screenshot_light.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

3
examples/nav_cupcake/iosApp/Configuration/Config.xcconfig

@ -0,0 +1,3 @@
TEAM_ID=
BUNDLE_ID=org.jetbrains.nav_cupcake.Cupcake
APP_NAME=Cupcake

398
examples/nav_cupcake/iosApp/iosApp.xcodeproj/project.pbxproj

@ -0,0 +1,398 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B92378962B6B1156000C7307 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
058557D7273AAEEB004C7B11 /* Preview Content */ = {
isa = PBXGroup;
children = (
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
42799AB246E5F90AF97AA0EF /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
7555FF72242A565900829871 = {
isa = PBXGroup;
children = (
AB1DB47929225F7C00F7AF9C /* Configuration */,
7555FF7D242A565900829871 /* iosApp */,
7555FF7C242A565900829871 /* Products */,
42799AB246E5F90AF97AA0EF /* Frameworks */,
);
sourceTree = "<group>";
};
7555FF7C242A565900829871 /* Products */ = {
isa = PBXGroup;
children = (
7555FF7B242A565900829871 /* iosApp.app */,
);
name = Products;
sourceTree = "<group>";
};
7555FF7D242A565900829871 /* iosApp */ = {
isa = PBXGroup;
children = (
058557BA273AAA24004C7B11 /* Assets.xcassets */,
7555FF82242A565900829871 /* ContentView.swift */,
7555FF8C242A565B00829871 /* Info.plist */,
2152FB032600AC8F00CF470E /* iOSApp.swift */,
058557D7273AAEEB004C7B11 /* Preview Content */,
);
path = iosApp;
sourceTree = "<group>";
};
AB1DB47929225F7C00F7AF9C /* Configuration */ = {
isa = PBXGroup;
children = (
AB3632DC29227652001CCB65 /* Config.xcconfig */,
);
path = Configuration;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
7555FF7A242A565900829871 /* iosApp */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
buildPhases = (
F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */,
7555FF77242A565900829871 /* Sources */,
B92378962B6B1156000C7307 /* Frameworks */,
7555FF79242A565900829871 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = iosApp;
packageProductDependencies = (
);
productName = iosApp;
productReference = 7555FF7B242A565900829871 /* iosApp.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
7555FF73242A565900829871 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1130;
LastUpgradeCheck = 1130;
ORGANIZATIONNAME = orgName;
TargetAttributes = {
7555FF7A242A565900829871 = {
CreatedOnToolsVersion = 11.3.1;
};
};
};
buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */;
compatibilityVersion = "Xcode 12.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 7555FF72242A565900829871;
packageReferences = (
);
productRefGroup = 7555FF7C242A565900829871 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
7555FF7A242A565900829871 /* iosApp */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
7555FF79242A565900829871 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Compile Kotlin Framework";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
7555FF77242A565900829871 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
7555FF83242A565900829871 /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
7555FFA3242A565B00829871 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
7555FFA4242A565B00829871 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
7555FFA6242A565B00829871 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = "${TEAM_ID}";
ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
);
INFOPLIST_FILE = iosApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-framework",
composeApp,
);
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
7555FFA7242A565B00829871 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = "${TEAM_ID}";
ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
);
INFOPLIST_FILE = iosApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-framework",
composeApp,
);
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7555FFA3242A565B00829871 /* Debug */,
7555FFA4242A565B00829871 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7555FFA6242A565B00829871 /* Debug */,
7555FFA7242A565B00829871 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 7555FF73242A565900829871 /* Project object */;
}

11
examples/nav_cupcake/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

14
examples/nav_cupcake/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json

@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "app-icon-1024.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

BIN
examples/nav_cupcake/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

6
examples/nav_cupcake/iosApp/iosApp/Assets.xcassets/Contents.json

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

21
examples/nav_cupcake/iosApp/iosApp/ContentView.swift

@ -0,0 +1,21 @@
import UIKit
import SwiftUI
import ComposeApp
struct ComposeView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
MainViewControllerKt.MainViewController()
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
struct ContentView: View {
var body: some View {
ComposeView()
.ignoresSafeArea(.all) // Compose has own insets handler
}
}

50
examples/nav_cupcake/iosApp/iosApp/Info.plist

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
</dict>
<key>UILaunchScreen</key>
<dict/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

6
examples/nav_cupcake/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

10
examples/nav_cupcake/iosApp/iosApp/iOSApp.swift

@ -0,0 +1,10 @@
import SwiftUI
@main
struct iOSApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

3042
examples/nav_cupcake/kotlin-js-store/yarn.lock

File diff suppressed because it is too large Load Diff

21
examples/nav_cupcake/settings.gradle.kts

@ -0,0 +1,21 @@
rootProject.name = "Cupcake"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement {
repositories {
google()
gradlePluginPortal()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
}
include(":composeApp")
Loading…
Cancel
Save