├── .gitattributes
├── composeApp
├── src
│ ├── androidMain
│ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── santimattius
│ │ │ │ └── kmp
│ │ │ │ ├── delivery
│ │ │ │ └── features
│ │ │ │ │ ├── home
│ │ │ │ │ └── HomeScreen.android.kt
│ │ │ │ │ └── map
│ │ │ │ │ └── MapScreen.android.kt
│ │ │ │ └── android
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── commonMain
│ │ ├── composeResources
│ │ │ ├── values
│ │ │ │ └── values.xml
│ │ │ ├── drawable
│ │ │ │ └── compose-multiplatform.xml
│ │ │ └── files
│ │ │ │ └── vendors.json
│ │ └── kotlin
│ │ │ ├── com
│ │ │ └── santimattius
│ │ │ │ └── kmp
│ │ │ │ └── delivery
│ │ │ │ ├── di
│ │ │ │ ├── AppQualifiers.kt
│ │ │ │ └── Dependencies.kt
│ │ │ │ ├── core
│ │ │ │ ├── data
│ │ │ │ │ ├── sources
│ │ │ │ │ │ ├── VendorRemoteDataSource.kt
│ │ │ │ │ │ ├── VendorLocalDataSource.kt
│ │ │ │ │ │ ├── RequestBuilder.kt
│ │ │ │ │ │ ├── local
│ │ │ │ │ │ │ └── InMemoryVendorLocalDataSource.kt
│ │ │ │ │ │ ├── VendorMockRemoteDataSource.kt
│ │ │ │ │ │ └── ktor
│ │ │ │ │ │ │ └── KtorVendorRemoteDataSource.kt
│ │ │ │ │ ├── VendorsResponse.kt
│ │ │ │ │ ├── ModelMapping.kt
│ │ │ │ │ ├── VendorsBody.kt
│ │ │ │ │ └── VendorsRepository.kt
│ │ │ │ ├── ui
│ │ │ │ │ ├── themes
│ │ │ │ │ │ ├── Color.kt
│ │ │ │ │ │ ├── Type.kt
│ │ │ │ │ │ └── Theme.kt
│ │ │ │ │ └── components
│ │ │ │ │ │ ├── Center.kt
│ │ │ │ │ │ ├── AppBar.kt
│ │ │ │ │ │ ├── LottieLoader.kt
│ │ │ │ │ │ ├── NetworkImage.kt
│ │ │ │ │ │ ├── CircularAvatar.kt
│ │ │ │ │ │ └── SearchAppBar.kt
│ │ │ │ ├── domain
│ │ │ │ │ └── Vendor.kt
│ │ │ │ └── network
│ │ │ │ │ └── Client.kt
│ │ │ │ ├── features
│ │ │ │ ├── splash
│ │ │ │ │ └── SplashScreen.kt
│ │ │ │ ├── map
│ │ │ │ │ ├── MapViewModel.kt
│ │ │ │ │ └── MapScreen.kt
│ │ │ │ └── home
│ │ │ │ │ ├── HomeScreenViewModel.kt
│ │ │ │ │ └── HomeScreen.kt
│ │ │ │ └── MainApplication.kt
│ │ │ └── App.kt
│ └── iosMain
│ │ └── kotlin
│ │ ├── NativeViewFactory.kt
│ │ ├── MainViewController.kt
│ │ └── com
│ │ └── santimattius
│ │ └── kmp
│ │ └── delivery
│ │ └── features
│ │ ├── map
│ │ └── MapScreen.ios.kt
│ │ └── home
│ │ └── HomeScreen.ios.kt
└── build.gradle.kts
├── iosApp
├── iosApp
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── app-icon-1024.png
│ │ │ └── Contents.json
│ │ └── AccentColor.colorset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── iOSApp.swift
│ ├── ContentView.swift
│ ├── NativeMapView.swift
│ ├── Info.plist
│ └── NativeVendorRow.swift
├── Configuration
│ └── Config.xcconfig
└── iosApp.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── gradle.properties
├── .gitignore
├── .fleet
└── receipt.json
├── settings.gradle.kts
├── README.md
├── gradlew.bat
└── gradlew
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Delivery
3 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/values/values.xml:
--------------------------------------------------------------------------------
1 |
2 | Delivery
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/iosApp/Configuration/Config.xcconfig:
--------------------------------------------------------------------------------
1 | TEAM_ID=
2 | BUNDLE_ID=com.santimattius.kmp.compose.skeleton.kmp-compose-gradle-skeleton
3 | APP_NAME=kmp-compose-gradle-skeleton
--------------------------------------------------------------------------------
/iosApp/iosApp/iOSApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct iOSApp: App {
5 | var body: some Scene {
6 | WindowGroup {
7 | ContentView()
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/santimattius/cmp-delivery-application/HEAD/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/di/AppQualifiers.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.di
2 |
3 | enum class AppQualifiers {
4 | Client,
5 | BaseUrl,
6 | FileSource,
7 | RemoteSource
8 | }
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/sources/VendorRemoteDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data.sources
2 |
3 | import com.santimattius.kmp.delivery.core.domain.VendorResult
4 |
5 | interface VendorRemoteDataSource {
6 |
7 | suspend fun getVendors(): Result
8 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "app-icon-1024.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/com/santimattius/kmp/delivery/features/home/HomeScreen.android.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.features.home
2 |
3 | import androidx.compose.runtime.Composable
4 | import com.santimattius.kmp.delivery.core.domain.Vendor
5 |
6 | @Composable
7 | actual fun NativeVendorItem(vendor: Vendor) {
8 | VendorRowItem(vendor = vendor)
9 | }
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/NativeViewFactory.kt:
--------------------------------------------------------------------------------
1 | import com.santimattius.kmp.delivery.core.domain.Vendor
2 | import platform.UIKit.UIViewController
3 |
4 | interface NativeViewFactory {
5 |
6 | fun createMapView(
7 | vendors: List,
8 | onItemClick: (Vendor) -> Unit
9 | ): UIViewController
10 |
11 | fun createVendorRow(vendor: Vendor): UIViewController
12 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
2 |
3 | #Gradle
4 | org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
5 |
6 |
7 | #Android
8 | android.nonTransitiveRClass=true
9 | android.useAndroidX=true
10 |
11 | #MPP
12 | kotlin.mpp.androidSourceSetLayoutVersion=2
13 | kotlin.mpp.enableCInteropCommonization=true
14 |
15 | #Development
16 | development=true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | **/build/
4 | xcuserdata
5 | !src/**/build/
6 | local.properties
7 | .idea
8 | .DS_Store
9 | captures
10 | .externalNativeBuild
11 | .cxx
12 | *.xcodeproj/*
13 | !*.xcodeproj/project.pbxproj
14 | !*.xcodeproj/xcshareddata/
15 | !*.xcodeproj/project.xcworkspace/
16 | !*.xcworkspace/contents.xcworkspacedata
17 | **/xcshareddata/WorkspaceSettings.xcsettings
18 | /.kotlin/
19 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/ui/themes/Color.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.entertainment.core.ui.themes
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/.fleet/receipt.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec": {
3 | "template_id": "kmt",
4 | "targets": {
5 | "android": {
6 | "ui": [
7 | "compose"
8 | ]
9 | },
10 | "ios": {
11 | "ui": [
12 | "compose"
13 | ]
14 | }
15 | }
16 | },
17 | "timestamp": "2023-12-12T11:51:15.531908775Z"
18 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/sources/VendorLocalDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data.sources
2 |
3 | import com.santimattius.kmp.delivery.core.domain.Vendor
4 | import com.santimattius.kmp.delivery.core.domain.VendorResult
5 |
6 | interface VendorLocalDataSource {
7 |
8 | suspend fun getVendors(): Result
9 |
10 | suspend fun save(vendors: List)
11 | }
12 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "cmp-delivery-application"
2 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
3 |
4 | pluginManagement {
5 | repositories {
6 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
7 | google()
8 | gradlePluginPortal()
9 | mavenCentral()
10 | }
11 | }
12 |
13 | dependencyResolutionManagement {
14 | repositories {
15 | google()
16 | mavenCentral()
17 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
18 | }
19 | }
20 |
21 | include(":composeApp")
22 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/MainViewController.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.runtime.CompositionLocalProvider
2 | import androidx.compose.runtime.staticCompositionLocalOf
3 | import androidx.compose.ui.window.ComposeUIViewController
4 |
5 | val LocalNativeViewFactory = staticCompositionLocalOf {
6 | error("LocalNativeViewFactory not provided")
7 | }
8 |
9 | fun MainViewController(
10 | nativeViewFactory: NativeViewFactory
11 | ) = ComposeUIViewController {
12 | CompositionLocalProvider(LocalNativeViewFactory provides nativeViewFactory) {
13 | App()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/ui/themes/Type.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.entertainment.core.ui.themes
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | val Typography = Typography(
10 | bodyLarge = TextStyle(
11 | fontFamily = FontFamily.Default,
12 | fontWeight = FontWeight.Normal,
13 | fontSize = 16.sp,
14 | lineHeight = 24.sp,
15 | letterSpacing = 0.5.sp
16 | )
17 | )
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/App.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.runtime.Composable
2 | import coil3.ImageLoader
3 | import coil3.compose.setSingletonImageLoaderFactory
4 | import coil3.network.ktor3.KtorNetworkFetcherFactory
5 | import com.santimattius.kmp.delivery.MainApplication
6 | import com.santimattius.kmp.delivery.core.ui.themes.AppTheme
7 |
8 | @Composable
9 | fun App() {
10 | setSingletonImageLoaderFactory { context ->
11 | ImageLoader.Builder(context)
12 | .components {
13 | add(KtorNetworkFetcherFactory())
14 | }
15 | .build()
16 | }
17 | AppTheme {
18 | MainApplication()
19 | }
20 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/sources/RequestBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data.sources
2 |
3 | import com.santimattius.kmp.delivery.core.data.Offer
4 | import com.santimattius.kmp.delivery.core.data.Point
5 | import com.santimattius.kmp.delivery.core.data.VendorsBody
6 |
7 | object RequestBuilder {
8 |
9 | fun makeDefaultBody(): VendorsBody {
10 | return VendorsBody(
11 | businessTypes = listOf("RESTAURANT"),
12 | countryID = 1,
13 | point = Point(-34.90111, -56.16453),
14 | sort = "RANKING",
15 | offer = Offer(occasions = listOf("DELIVERY", "PICKUP"))
16 | )
17 | }
18 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/VendorsResponse.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class VendorDto(
7 | val id: Long,
8 | val name: String,
9 | val logo: String,
10 | val location: Location,
11 | val isNew: Boolean,
12 | val isExclusive: Boolean,
13 | val deliveryTime: String,
14 | val deliveryFee: Double,
15 | val rating: Double,
16 | val headerImage: String,
17 | val categories: List
18 | ) {
19 | val isFavorite: Boolean = false
20 | }
21 |
22 | @Serializable
23 | data class Location(
24 | val lat: Double,
25 | val lng: Double
26 | )
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/domain/Vendor.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.domain
2 |
3 | data class Vendor(
4 | val id: Long,
5 | val name: String,
6 | val logo: String,
7 | val location: Location,
8 | val isNew: Boolean,
9 | val isExclusive: Boolean,
10 | val deliveryTime: String,
11 | val deliveryFee: Double,
12 | val rating: Double,
13 | val headerImage: String,
14 | val categories: List
15 | ) {
16 | val isFavorite: Boolean = false
17 | }
18 |
19 | data class Location(
20 | val lat: Double,
21 | val lng: Double
22 | )
23 |
24 | data class VendorResult(
25 | val total: Long,
26 | val vendors: List
27 | )
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/ModelMapping.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data
2 |
3 | import com.santimattius.kmp.delivery.core.data.VendorDto
4 | import com.santimattius.kmp.delivery.core.domain.Location
5 | import com.santimattius.kmp.delivery.core.domain.Vendor
6 |
7 | fun List.asDomains(): List {
8 | return map { it.asDomain() }
9 | }
10 |
11 | private fun VendorDto.asDomain(): Vendor {
12 | return Vendor(
13 | id = id,
14 | name = name,
15 | logo = logo,
16 | deliveryTime = deliveryTime,
17 | deliveryFee = deliveryFee,
18 | location = Location(location.lat, location.lng),
19 | rating = rating,
20 | isExclusive = isExclusive,
21 | isNew = isNew,
22 | headerImage = headerImage,
23 | categories = categories,
24 | )
25 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/VendorsBody.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data
2 |
3 | // To parse the JSON, install kotlin's serialization plugin and do:
4 | //
5 | // val json = Json { allowStructuredMapKeys = true }
6 | // val vendorsBody = json.parse(VendorsBody.serializer(), jsonString)
7 | import kotlinx.serialization.SerialName
8 | import kotlinx.serialization.Serializable
9 | @Serializable
10 | data class VendorsBody (
11 | val filters: List = emptyList(),
12 | val businessTypes: List,
13 | @SerialName("countryId")
14 | val countryID: Long,
15 | val point: Point,
16 | val sort: String,
17 | val offer: Offer
18 | )
19 |
20 | @Serializable
21 | data class Offer (
22 | val occasions: List
23 | )
24 |
25 | @Serializable
26 | data class Point (
27 | val latitude: Double,
28 | val longitude: Double
29 | )
30 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/ui/components/Center.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.ui.components
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.fillMaxSize
5 | import androidx.compose.material3.CircularProgressIndicator
6 | import androidx.compose.material3.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 |
11 | @Composable
12 | private fun Center(content: @Composable () -> Unit) {
13 | Box(
14 | modifier = Modifier.fillMaxSize(),
15 | contentAlignment = Alignment.Center
16 | ) {
17 | content()
18 | }
19 | }
20 |
21 | @Composable
22 | fun LoadingIndicator() {
23 | Center {
24 | CircularProgressIndicator()
25 | }
26 | }
27 |
28 | @Composable
29 | fun ErrorView(message: String) {
30 | Center { Text(message) }
31 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/com/santimattius/kmp/delivery/features/map/MapScreen.android.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.features.map
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.fillMaxSize
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Alignment
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.unit.dp
10 | import com.santimattius.kmp.delivery.core.domain.Vendor
11 | import com.santimattius.kmp.delivery.core.ui.components.LottieLoader
12 |
13 | @Composable
14 | actual fun MapView(
15 | vendors: List,
16 | onItemClick: (Vendor) -> Unit,
17 | modifier: Modifier,
18 | ) {
19 | Box(
20 | modifier = modifier.fillMaxSize(),
21 | contentAlignment = Alignment.Center
22 | ) {
23 | LottieLoader(
24 | resource = "files/avocado.json",
25 | contentDescription = "avocado"
26 | )
27 | }
28 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/sources/local/InMemoryVendorLocalDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data.sources.local
2 |
3 | import com.santimattius.kmp.delivery.core.data.sources.VendorLocalDataSource
4 | import com.santimattius.kmp.delivery.core.domain.Vendor
5 | import com.santimattius.kmp.delivery.core.domain.VendorResult
6 | import kotlinx.coroutines.sync.Mutex
7 | import kotlinx.coroutines.sync.withLock
8 |
9 | class InMemoryVendorLocalDataSource : VendorLocalDataSource {
10 | private val mutex = Mutex()
11 | private val vendors = mutableListOf()
12 |
13 | override suspend fun getVendors(): Result {
14 | return mutex.withLock {
15 | Result.success(VendorResult(total = 0L, vendors = vendors))
16 | }
17 | }
18 |
19 | override suspend fun save(vendors: List) {
20 | mutex.withLock {
21 | this.vendors.clear()
22 | this.vendors.addAll(vendors)
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/sources/VendorMockRemoteDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data.sources
2 |
3 | import cmp_delivery_application.composeapp.generated.resources.Res
4 | import com.santimattius.kmp.delivery.core.data.VendorDto
5 | import com.santimattius.kmp.delivery.core.data.asDomains
6 | import com.santimattius.kmp.delivery.core.domain.VendorResult
7 | import com.santimattius.kmp.delivery.core.network.decodeFromString
8 | import kotlinx.coroutines.delay
9 | import org.jetbrains.compose.resources.ExperimentalResourceApi
10 |
11 | class VendorMockRemoteDataSource : VendorRemoteDataSource {
12 | @OptIn(ExperimentalResourceApi::class)
13 | override suspend fun getVendors(): Result = runCatching {
14 | delay(1000L)
15 | val jsonStr = Res.readBytes("files/vendors.json").decodeToString()
16 | val response = decodeFromString>(jsonStr)
17 | VendorResult(response.size.toLong(), response.asDomains())
18 | }
19 | }
--------------------------------------------------------------------------------
/iosApp/iosApp/ContentView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import SwiftUI
3 | import ComposeApp
4 |
5 | struct ComposeView: UIViewControllerRepresentable {
6 | func makeUIViewController(context: Context) -> UIViewController {
7 | MainViewControllerKt.MainViewController(nativeViewFactory: IOSNativeView.shared)
8 | }
9 |
10 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
11 | }
12 |
13 | class IOSNativeView : NativeViewFactory{
14 |
15 | static let shared = IOSNativeView()
16 |
17 | func createMapView(vendors: [Vendor], onItemClick: @escaping (Vendor) -> Void) -> UIViewController {
18 | let map = NativeMapView(places: vendors, onTap: onItemClick)
19 | return UIHostingController(rootView: map)
20 | }
21 |
22 | func createVendorRow(vendor: Vendor) -> UIViewController {
23 | return UIHostingController(rootView: NativeVendorRow(vendor: vendor))
24 | }
25 | }
26 |
27 | struct ContentView: View {
28 |
29 | var body: some View {
30 | ComposeView()
31 | }
32 | }
33 |
34 |
35 |
--------------------------------------------------------------------------------
/iosApp/iosApp/NativeMapView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NativeMapView.swift
3 | // iosApp
4 | //
5 | // Created by Santiago Mattiauda on 26/5/24.
6 | // Copyright © 2024 orgName. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import MapKit
11 | import ComposeApp
12 |
13 | struct NativeMapView: View {
14 |
15 | var places = [Vendor]()
16 | var onTap:(Vendor)->Void = {_ in}
17 |
18 | @State private var region = MKCoordinateRegion(
19 | center: CLLocationCoordinate2D(latitude: -34.90111, longitude: -56.16453),
20 | span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
21 | )
22 | var body: some View {
23 | Map(coordinateRegion: $region,showsUserLocation: true, annotationItems: places, annotationContent: { item in
24 | MapAnnotation(coordinate: CLLocationCoordinate2D(latitude: item.location.lat, longitude: item.location.lng)){
25 | Image(systemName: "house.circle.fill")
26 | .font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/)
27 | .foregroundColor(.red).onTapGesture {
28 | onTap(item)
29 | }
30 | }
31 | })
32 | }
33 | }
34 |
35 | extension Vendor:Identifiable {}
36 |
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/com/santimattius/kmp/delivery/features/map/MapScreen.ios.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.features.map
2 |
3 | import LocalNativeViewFactory
4 | import androidx.compose.foundation.border
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.graphics.Color
9 | import androidx.compose.ui.unit.dp
10 | import androidx.compose.ui.viewinterop.UIKitInteropProperties
11 | import androidx.compose.ui.viewinterop.UIKitViewController
12 | import com.santimattius.kmp.delivery.core.domain.Vendor
13 |
14 | @Composable
15 | actual fun MapView(
16 | vendors: List,
17 | onItemClick: (Vendor) -> Unit,
18 | modifier: Modifier
19 | ) {
20 |
21 | val nativeViewFactory = LocalNativeViewFactory.current
22 | UIKitViewController(
23 | factory = { nativeViewFactory.createMapView(vendors, onItemClick) },
24 | modifier = Modifier
25 | .fillMaxSize()
26 | .border(1.dp, Color.Black),
27 | update = {},
28 | properties = UIKitInteropProperties(
29 | isInteractive = true,
30 | isNativeAccessibilityEnabled = true
31 | )
32 | )
33 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/sources/ktor/KtorVendorRemoteDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data.sources.ktor
2 |
3 | import com.santimattius.kmp.delivery.core.data.VendorDto
4 | import com.santimattius.kmp.delivery.core.data.asDomains
5 | import com.santimattius.kmp.delivery.core.data.sources.VendorRemoteDataSource
6 | import com.santimattius.kmp.delivery.core.domain.VendorResult
7 | import com.santimattius.kmp.entertainment.BuildConfig
8 | import io.ktor.client.HttpClient
9 | import io.ktor.client.call.body
10 | import io.ktor.client.request.get
11 | import io.ktor.client.request.headers
12 |
13 | class KtorVendorRemoteDataSource(
14 | private val client: HttpClient,
15 | ) : VendorRemoteDataSource {
16 | override suspend fun getVendors(): Result = runCatching {
17 | val response = client.get("/restaurants") {
18 | headers {
19 | append(AVOCODE, BuildConfig.apiKey)
20 | }
21 | }
22 | val body = response.body>()
23 | VendorResult(
24 | total = body.size.toLong(),
25 | vendors = body.asDomains()
26 | )
27 | }
28 |
29 | companion object {
30 | private const val AVOCODE = "avocode"
31 | }
32 | }
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/com/santimattius/kmp/delivery/features/home/HomeScreen.ios.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.features.home
2 |
3 | import LocalNativeViewFactory
4 | import androidx.compose.foundation.layout.fillMaxSize
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.height
7 | import androidx.compose.foundation.layout.wrapContentSize
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.interop.UIKitViewController
11 | import androidx.compose.ui.unit.dp
12 | import androidx.compose.ui.viewinterop.UIKitInteropProperties
13 | import androidx.compose.ui.viewinterop.UIKitViewController
14 | import com.santimattius.kmp.delivery.core.domain.Vendor
15 | import kotlinx.cinterop.ExperimentalForeignApi
16 |
17 | @Composable
18 | actual fun NativeVendorItem(vendor: Vendor) {
19 | val nativeViewFactory = LocalNativeViewFactory.current
20 | UIKitViewController(
21 | factory = { nativeViewFactory.createVendorRow(vendor) },
22 | modifier = Modifier.height(100.dp).fillMaxWidth(),
23 | update = {},
24 | properties = UIKitInteropProperties(
25 | isInteractive = true,
26 | isNativeAccessibilityEnabled = true
27 | )
28 | )
29 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/network/Client.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.network
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
5 | import io.ktor.client.plugins.defaultRequest
6 | import io.ktor.client.plugins.logging.DEFAULT
7 | import io.ktor.client.plugins.logging.LogLevel
8 | import io.ktor.client.plugins.logging.Logger
9 | import io.ktor.client.plugins.logging.Logging
10 | import io.ktor.http.ContentType
11 | import io.ktor.http.contentType
12 | import io.ktor.serialization.kotlinx.json.json
13 | import kotlinx.serialization.json.Json
14 |
15 | internal fun ktorHttpClient(baseUrl: String) = HttpClient {
16 |
17 | install(ContentNegotiation) {
18 | json(json)
19 | }
20 | install(Logging) {
21 | logger = Logger.DEFAULT
22 | level = LogLevel.ALL
23 | }
24 |
25 | defaultRequest {
26 | url(baseUrl)
27 | contentType(ContentType.Application.Json)
28 | }
29 | }
30 |
31 | val json = Json {
32 | prettyPrint = true
33 | isLenient = true
34 | ignoreUnknownKeys = true
35 | allowStructuredMapKeys = true
36 | }
37 |
38 | inline fun decodeFromString(jsonStr: String): T {
39 | return json.decodeFromString(jsonStr)
40 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/data/VendorsRepository.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.data
2 |
3 | import com.santimattius.kmp.delivery.core.data.sources.VendorLocalDataSource
4 | import com.santimattius.kmp.delivery.core.data.sources.VendorRemoteDataSource
5 | import com.santimattius.kmp.delivery.core.domain.VendorResult
6 |
7 | class VendorsRepository(
8 | private val remoteDataSource: VendorRemoteDataSource,
9 | private val localDataSource: VendorLocalDataSource
10 | ) {
11 |
12 | suspend fun getVendors(): Result {
13 | val result = localDataSource.getVendors().fold(
14 | onSuccess = {
15 | if (it.vendors.isEmpty()) {
16 | Result.failure(Throwable("no vendors"))
17 | } else {
18 | Result.success(it)
19 | }
20 | },
21 | onFailure = { Result.failure(Throwable(it.message)) },
22 | )
23 | if (result.isSuccess) {
24 | return result
25 | }
26 | return remoteDataSource.getVendors().fold(
27 | onSuccess = {
28 | localDataSource.save(it.vendors)
29 | Result.success(it)
30 | },
31 | onFailure = { Result.failure(Throwable(it.message)) },
32 | )
33 | }
34 | }
35 |
36 |
37 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/ui/components/AppBar.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.ui.components
2 |
3 | import androidx.compose.foundation.layout.RowScope
4 | import androidx.compose.material3.ExperimentalMaterial3Api
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.material3.Text
7 | import androidx.compose.material3.TopAppBar
8 | import androidx.compose.material3.TopAppBarDefaults
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.graphics.Color
11 |
12 |
13 | @OptIn(ExperimentalMaterial3Api::class)
14 | @Composable
15 | fun AppBar(
16 | title: String = "",
17 | navigationIcon: @Composable () -> Unit = { },
18 | containerColor: Color = MaterialTheme.colorScheme.primary,
19 | titleContentColor: Color = MaterialTheme.colorScheme.onPrimary,
20 | actions: @Composable RowScope.() -> Unit = {},
21 | ) {
22 | TopAppBar(
23 | title = { Text(text = title) },
24 | navigationIcon = navigationIcon,
25 | colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
26 | containerColor = containerColor,
27 | titleContentColor = titleContentColor,
28 | navigationIconContentColor = titleContentColor,
29 | actionIconContentColor = titleContentColor,
30 | ),
31 | actions = actions
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/ui/components/LottieLoader.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.ui.components
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.getValue
6 | import androidx.compose.ui.Modifier
7 | import cmp_delivery_application.composeapp.generated.resources.Res
8 | import io.github.alexzhirkevich.compottie.LottieCompositionSpec
9 | import io.github.alexzhirkevich.compottie.animateLottieCompositionAsState
10 | import io.github.alexzhirkevich.compottie.rememberLottieComposition
11 | import io.github.alexzhirkevich.compottie.rememberLottiePainter
12 | import org.jetbrains.compose.resources.ExperimentalResourceApi
13 |
14 | @OptIn(ExperimentalResourceApi::class)
15 | @Composable
16 | fun LottieLoader(
17 | resource: String,
18 | contentDescription: String?,
19 | modifier: Modifier = Modifier
20 | ) {
21 | val composition by rememberLottieComposition {
22 | LottieCompositionSpec.JsonString(
23 | Res.readBytes(resource).decodeToString()
24 | )
25 | }
26 | val progress by animateLottieCompositionAsState(composition, iterations = 1000)
27 |
28 | Image(
29 | modifier = modifier,
30 | painter = rememberLottiePainter(
31 | composition = composition,
32 | progress = { progress },
33 | ),
34 | contentDescription = contentDescription
35 | )
36 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/com/santimattius/kmp/android/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.android
2 |
3 | import App
4 | import android.app.Activity
5 | import android.os.Bundle
6 | import androidx.activity.ComponentActivity
7 | import androidx.activity.compose.setContent
8 | import androidx.compose.material3.MaterialTheme
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.SideEffect
11 | import androidx.compose.ui.graphics.toArgb
12 | import androidx.compose.ui.platform.LocalView
13 | import androidx.compose.ui.tooling.preview.Preview
14 | import androidx.core.view.WindowCompat
15 |
16 | class MainActivity : ComponentActivity() {
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 | setContent {
20 | App()
21 | StatusBarColor()
22 | }
23 | }
24 |
25 |
26 | }
27 |
28 | @Composable
29 | private fun StatusBarColor() {
30 | val view = LocalView.current
31 | val colorScheme = MaterialTheme.colorScheme
32 | if (!view.isInEditMode) {
33 | SideEffect {
34 | val window = (view.context as Activity).window
35 | window.statusBarColor = colorScheme.background.copy(alpha = 0.8f).toArgb()
36 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = true
37 | }
38 | }
39 |
40 | }
41 |
42 | @Preview
43 | @Composable
44 | fun AppAndroidPreview() {
45 | App()
46 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/ui/components/NetworkImage.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.ui.components
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.size
5 | import androidx.compose.material3.CircularProgressIndicator
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.layout.ContentScale
11 | import androidx.compose.ui.unit.dp
12 | import coil3.compose.LocalPlatformContext
13 | import coil3.compose.SubcomposeAsyncImage
14 | import coil3.request.ImageRequest
15 |
16 |
17 | @Composable
18 | internal fun NetworkImage(
19 | imageUrl: String,
20 | modifier: Modifier = Modifier,
21 | contentScale: ContentScale,
22 | contentDescription: String? = null,
23 | ) {
24 | SubcomposeAsyncImage(
25 | model = ImageRequest.Builder(LocalPlatformContext.current)
26 | .data(imageUrl).build(),
27 | loading = {
28 | Box(contentAlignment = Alignment.Center) {
29 | CircularProgressIndicator(
30 | color = MaterialTheme.colorScheme.secondary,
31 | modifier = Modifier.size(32.dp)
32 | )
33 | }
34 | },
35 | contentDescription = contentDescription,
36 | contentScale = contentScale,
37 | modifier = modifier
38 | )
39 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/ui/components/CircularAvatar.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.ui.components
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.draw.clip
11 | import androidx.compose.ui.graphics.RectangleShape
12 | import androidx.compose.ui.layout.ContentScale
13 | import androidx.compose.ui.unit.Dp
14 | import androidx.compose.ui.unit.dp
15 | import coil3.compose.AsyncImage
16 | import coil3.compose.LocalPlatformContext
17 | import coil3.request.ImageRequest
18 |
19 | @Composable
20 | fun CircularAvatar(
21 | image: String,
22 | contentDescription: String,
23 | modifier: Modifier = Modifier,
24 | size: Dp = 40.dp
25 | ) {
26 | Box(
27 | modifier = modifier
28 | .size(size)
29 | .background(color = MaterialTheme.colorScheme.surface, shape = RectangleShape)
30 | .clip(RectangleShape),
31 | contentAlignment = Alignment.Center
32 | ) {
33 | AsyncImage(
34 | model = ImageRequest.Builder(LocalPlatformContext.current)
35 | .data(image).build(),
36 | contentDescription = contentDescription,
37 | contentScale = ContentScale.Crop,
38 | modifier = Modifier.size(size),
39 | )
40 | }
41 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/features/splash/SplashScreen.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.features.splash
2 |
3 | import androidx.compose.animation.core.Animatable
4 | import androidx.compose.animation.core.Spring
5 | import androidx.compose.animation.core.spring
6 | import androidx.compose.foundation.layout.Box
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.size
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.LaunchedEffect
11 | import androidx.compose.runtime.remember
12 | import androidx.compose.ui.Alignment
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.unit.dp
15 | import com.santimattius.kmp.delivery.core.ui.components.LottieLoader
16 | import kotlinx.coroutines.delay
17 |
18 | @Composable
19 | fun SplashScreen(navigate: () -> Unit) {
20 | val scale = remember {
21 | Animatable(0f)
22 | }
23 |
24 | LaunchedEffect(key1 = true) {
25 | scale.animateTo(
26 | targetValue = 0.7f,
27 | animationSpec = spring(
28 | dampingRatio = Spring.DampingRatioHighBouncy,
29 | stiffness = 1000f
30 | )
31 | )
32 | delay(1000L)
33 | navigate()
34 | }
35 |
36 | Box(
37 | contentAlignment = Alignment.Center,
38 | modifier = Modifier.fillMaxSize()
39 | ) {
40 | LottieLoader(
41 | modifier = Modifier.size(100.dp),
42 | resource = "files/splash.json",
43 | contentDescription = "Splash screen"
44 | )
45 | }
46 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/features/map/MapViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.features.map
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.santimattius.kmp.delivery.core.data.VendorsRepository
6 | import com.santimattius.kmp.delivery.core.domain.Vendor
7 | import kotlinx.coroutines.flow.MutableStateFlow
8 | import kotlinx.coroutines.flow.SharingStarted
9 | import kotlinx.coroutines.flow.onStart
10 | import kotlinx.coroutines.flow.stateIn
11 | import kotlinx.coroutines.flow.update
12 | import kotlinx.coroutines.launch
13 |
14 | data class MapUiState(
15 | val isLoading: Boolean = false,
16 | val hasError: Boolean = false,
17 | val data: List = emptyList(),
18 | )
19 |
20 | class MapViewModel(
21 | private val repository: VendorsRepository,
22 | ) : ViewModel() {
23 |
24 | private val _state = MutableStateFlow(MapUiState())
25 | val state = _state
26 | .onStart {
27 | loadVendors()
28 | }.stateIn(
29 | scope = viewModelScope,
30 | started = SharingStarted.WhileSubscribed(5_000),
31 | initialValue = MapUiState(isLoading = true)
32 | )
33 |
34 | private fun loadVendors() {
35 | viewModelScope.launch {
36 | repository.getVendors().onSuccess { result ->
37 | _state.update {
38 | it.copy(isLoading = false, data = result.vendors)
39 | }
40 | }.onFailure {
41 | _state.update { it.copy(isLoading = false, hasError = true) }
42 | }
43 | }
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/core/ui/themes/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery.core.ui.themes
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.darkColorScheme
6 | import androidx.compose.material3.lightColorScheme
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.graphics.Color
9 | import com.santimattius.kmp.entertainment.core.ui.themes.Pink40
10 | import com.santimattius.kmp.entertainment.core.ui.themes.Pink80
11 | import com.santimattius.kmp.entertainment.core.ui.themes.Purple40
12 | import com.santimattius.kmp.entertainment.core.ui.themes.Purple80
13 | import com.santimattius.kmp.entertainment.core.ui.themes.PurpleGrey40
14 | import com.santimattius.kmp.entertainment.core.ui.themes.PurpleGrey80
15 | import com.santimattius.kmp.entertainment.core.ui.themes.Typography
16 |
17 | private val DarkColorScheme = darkColorScheme(
18 | primary = Purple80,
19 | secondary = PurpleGrey80,
20 | tertiary = Pink80
21 | )
22 |
23 | private val LightColorScheme = lightColorScheme(
24 | primary = Purple40,
25 | secondary = PurpleGrey40,
26 | tertiary = Pink40,
27 | onPrimary = Color.White,
28 | background = Color.White,
29 | surfaceContainer = Color.White,
30 | surface = Color.White,
31 | )
32 |
33 | @Composable
34 | fun AppTheme(
35 | darkTheme: Boolean = isSystemInDarkTheme(),
36 | content: @Composable () -> Unit,
37 | ) {
38 | val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
39 | MaterialTheme(
40 | colorScheme = colorScheme,
41 | typography = Typography,
42 | content = content
43 | )
44 | }
--------------------------------------------------------------------------------
/iosApp/iosApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | CADisableMinimumFrameDurationOnPhone
24 |
25 | UIApplicationSceneManifest
26 |
27 | UIApplicationSupportsMultipleScenes
28 |
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/com/santimattius/kmp/delivery/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.santimattius.kmp.delivery
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.navigation.NavHostController
6 | import androidx.navigation.compose.NavHost
7 | import androidx.navigation.compose.composable
8 | import androidx.navigation.compose.rememberNavController
9 | import com.santimattius.kmp.delivery.di.applicationModules
10 | import com.santimattius.kmp.delivery.features.home.HomeScreen
11 | import com.santimattius.kmp.delivery.features.map.MapScreen
12 | import com.santimattius.kmp.delivery.features.splash.SplashScreen
13 | import kotlinx.serialization.Serializable
14 | import org.koin.compose.KoinApplication
15 |
16 | @Composable
17 | fun MainApplication() {
18 | KoinApplication(application = {
19 | modules(applicationModules())
20 | }) {
21 | AppNavigation()
22 | }
23 | }
24 |
25 | @Composable
26 | fun AppNavigation(
27 | modifier: Modifier = Modifier,
28 | navController: NavHostController = rememberNavController(),
29 | ) {
30 |
31 | NavHost(
32 | modifier = modifier,
33 | navController = navController,
34 | startDestination = Splash
35 | ) {
36 | composable {
37 | SplashScreen {
38 | navController.popBackStack()
39 | navController.navigate(Home)
40 | }
41 | }
42 | composable {
43 | HomeScreen {
44 | navController.navigate(Map)
45 | }
46 | }
47 | composable