Skip to content

Commit

Permalink
Merge pull request cl3m#9 from cl3m/compose-jb
Browse files Browse the repository at this point in the history
JetBrains Compose version
  • Loading branch information
cl3m authored Nov 18, 2022
2 parents daa474d + beecaeb commit 28336a8
Show file tree
Hide file tree
Showing 287 changed files with 1,026 additions and 16,483 deletions.
161 changes: 24 additions & 137 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,176 +1,63 @@
[![Kotlin Multiplatform](https://img.shields.io/static/v1?logo=Kotlin&&logoColor=3c94cf&label=&message=Kotlin%20Multiplatform&color=555)](https://kotlinlang.org/docs/reference/multiplatform.html)

:warning: This PoC compile on iOS and Android but I've remove some functionality because Compose changed too much since I started it. I will update it using the compose compiler when it will be possible. There are some hope on the way :
- [Redwood Compose](https://github.com/cashapp/redwood) by Cash App. [Native UI with multiplatform Compose](https://jakewharton.com/native-ui-with-multiplatform-compose/). Use the compose compiler and native iOS component.
- [Compose Multiplatform by JetBrains](https://github.com/JetBrains/compose-jb) started native support via Skia ([Skiko](https://github.com/JetBrains/skiko)), you can have a look at the [sample](https://github.com/JetBrains/compose-jb/tree/master/experimental/examples/falling-balls-mpp) but it is still very early. Some of the demos in this repo works but there is still many broken functionality. Touchlab made a demo for [Droidcon NYC App](https://touchlab.co/droidcon-nyc-ios-app-with-compose/) It will use the compose compiler but not native component.
- [Platform-Kit by IceRock](https://github.com/Alex009/compose-jb/tree/platform-kit-sample/examples/common-platform-uikit) based on Compose Multiplatform by JetBrains, add support via UIKit, thus use the compose compiler and native iOS component.

I've started to experiment with Compose Native, you can have a look at https://github.com/cl3m/kmp-redux/tree/skiko on iOS two views are SwiftUI and two views are Compose (ComposeSpaceView & ComposeCounterView) and share the same store.

# Multiplatform Compose

A Kotlin library to use Jetpack Compose in Android and iOS. Allow to write UI for both in Kotlin. Still experimental as many compose features are not yet available.
A demo to show usage of Jetbrains Compose in Android and iOS. Originally a Jetpack Compose implementation with native view and yoga for iOS.

## Table of contents

- [Requirements](#requirements)
- [Installation](#installation)
- [Usage](#usage)
- [Known issues](#known-issues)
- [Libraries](#libraries)
- [Demos](#demos)
- [Troubleshooting](#troubleshooting)
- [Roadmap](#roadmap)
- [Contributing](#contributing)
- [Sponsors](#sponsors)
- [Alternatives](#alternatives)
- [License](#license)

## Requirements

- Android Studio Canary
- cocoapods (gem install cocoapods)
- cocoapods-generate (gem install cocoapods-generate)

## Installation

The library is not yet published to Maven Central as it is still experimental.

## Usage

The simpliest code is :
## Libraries

```kotlin
@Composable
fun Content(resources: Resources) {
Text("Hello world!")
}
```
- kotlinx coroutines
- ktor
- Jetbrains Compose (uikit experimental)
- [PreCompose](https://github.com/Tlaster/PreCompose) (for navigation)

A better start would be :
## Demos

```kotlin
@Composable
fun Content(resources: Resources) {
HelloPlatform()
}

@Composable
fun HelloPlatform() {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally) {
Text("Hello, ${Platform().platform}!")
}
}
```
![Hello Platform Screenshot](https://github.com/cl3m/multiplatform-compose/blob/develop/screenshots/HelloPlatform.png?raw=true)
Run the app to see a demo of compose on ios.

More advance sample are in the [demos](https://github.com/cl3m/multiplatform-compose/tree/develop/test/src/commonMain/kotlin/com/rouge41/kmm/compose/test/demos) directory of the test library.
![Demos](https://github.com/cl3m/multiplatform-compose/blob/compose-jb/screenshots/Demos.png?raw=true)

#### Image
#### AsyncImage

The image composable allow url loading

```kotlin
Image(url = "https://loremflickr.com/320/240/ocean", modifier = Modifier.preferredSize(200.dp))
```

### iOS Composables

#### HumanAppearance

Allow iOS styling such as font or global tintColor.

```kotlin
HumanAppearance(tintColor: Color, backgroundColor: Color?, style: TextStyle) {
// ...
}
AsyncImage(url = "https://loremflickr.com/320/240/ocean", modifier = Modifier.size(200.dp))
```

#### SafeArea

Add safe area insets to the view, works in root, TabView and NavigationView

```kotlin
SafeArea {
// ...
}
```

![Safe Area Screenshot](https://github.com/cl3m/multiplatform-compose/blob/develop/screenshots/Layout.png?raw=true)
*Layout without the safe area, with the safe area and on android*

#### TabView

UITabBarController for Compose

```kotlin
TabView {
Tab(title = "First", image = UIImage.systemImageNamed("a.circle.fill")) {
// ...
}
Tab(title = "Second", image = UIImage.systemImageNamed("a.circle.fill")) {
// ...
}
}
```

#### NavigationView

UINavigationController for Compose, renamed to NavHost but as additional parameter title, leadingButton and trailingButton in composable
SafeArea.current to get PaddingValues.

```kotlin
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "first") {
composable("first", title = "First", trailingButton = Button(onClick = {}) { Text ("Edit") }) {
SafeArea {
Button(onClick = { navController.navigate("second") }) {
// ...
}
}
}
composable("second", title = "Second") {
SafeArea {
// ...
}
}
}
```
#### DarkMode

## Known issues
DarkMode.current to fix dark mode on iOS.

- Jetpack Compose require Android Studio Canary and an alpha build of Gradle. There is some workaround in _build.gradle.kts_ to make it work (testApi).
- Jetpack Compose is not supported in Kotlin Multiplatform Mobile library ([KT-38694](https://youtrack.jetbrains.com/issue/KT-38694)). Unfortunatly, the workaround was messing with actual/expect function and prevent the use of expect function with default value. The library now use @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") to workaround this problem. If you use an expect function with default value and without the suppress, you will get an error function not found or _java.lang.IllegalStateException: 2. expected value parameter count to be higher_.
- Jetpack Compose dependencies can not be used in commonMain because they have a dependency on kotlinx-coroutines-android.
- Android Studio does not autocomplete cocoapods imported library in iosMain. Thus it is in iosx64Main and symlinked to iosArm64Main.
- Images needs currently to be included in the android and ios resources separately.
- Navigation is currently not shared in the library
- Latest Android Studio Canary shows many undefined error but everthing compile fine. It was not the case before.

## Troubleshooting

### cinteropYogaKitIosArm64 FAILED YogaKit module not found
### e: java.lang.IllegalStateException: No file for ***

You did not read the requirements. Install cocoapods-generate "gem install cocoapods-generate", invalid cache and restart Android Studio
Compose function and CompositonLocal have to be internal and not exposed to iOS module.

## Roadmap
## Alternatives

- More Jetpack Compose feature support
- Better images/resources support
- UI Test with Github Actions
- Performance improvement/optimisation
- Use Compose compiler and runtime on iOS
- [Compose Multiplatform by JetBrains](https://github.com/JetBrains/compose-jb) started native support via Skia ([Skiko](https://github.com/JetBrains/skiko)), you can have a look at the [sample](https://github.com/JetBrains/compose-jb/tree/master/experimental/examples/falling-balls-mpp). Touchlab made a demo for [Droidcon NYC App](https://touchlab.co/droidcon-nyc-ios-app-with-compose/) It will use the compose compiler but not native component.

## Sponsors

No one yet, be the first [sponsor](https://github.com/sponsors/cl3m)!

## Contributing
- [Redwood Compose](https://github.com/cashapp/redwood) by Cash App. [Native UI with multiplatform Compose](https://jakewharton.com/native-ui-with-multiplatform-compose/). Use the compose compiler and native iOS component.

All development (both new features and bug fixes) is performed in the `develop` branch. This way `master` always contains the sources of the most recently released version. Use git-flow if possible.
- [Platform-Kit by IceRock](https://github.com/Alex009/compose-jb/tree/platform-kit-sample/examples/common-platform-uikit) based on Compose Multiplatform by JetBrains, add support via UIKit, thus use the compose compiler and native iOS component.

You can start a PR with incomplete implementation to shows what you are working on. Please send PRs with bug fixes or new features to the `develop` branch. Documentation fixes in the markdown files are an exception to this rule. They are updated directly in `master`.
- [multiplatform-compose](https://github.com/cl3m/multiplatform-compose/tree/yoga) This original repo, an experiment with native view and yoga for layout. You can also have a look at [kmp-redux](https://github.com/cl3m/kmp-redux/tree/skiko) that show Compose integration with SwiftUI.

The `develop` branch is pushed to `master` on release.

## License

Expand Down
19 changes: 8 additions & 11 deletions androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ plugins {
}

android {
compileSdkVersion(AndroidSdk.compile)
compileSdk = AndroidSdk.compile
defaultConfig {
applicationId = "com.rouge41.kmm.compose.androidApp"
minSdkVersion(AndroidSdk.min)
targetSdkVersion(AndroidSdk.target)
minSdk = AndroidSdk.min
targetSdk = AndroidSdk.target
versionCode = 1
versionName = "1.0"
}
Expand All @@ -28,21 +28,18 @@ android {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = Version.compose
kotlinCompilerExtensionVersion = Version.compose_compiler
}
}

dependencies {
implementation(Android.appcompact)
implementation(Android.material)

implementation(project(":multiplatform-compose"))
implementation(project(":test"))

implementation(Android.appcompat)
implementation(Android.material)

implementation(Compose.runtime)
implementation(Compose.ui)
implementation(Compose.foundationLayout)
implementation(Compose.material)
implementation(Compose.runtimeLiveData)
implementation(Compose.navigation)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,22 @@ package com.rouge41.kmm.compose.androidApp

import android.graphics.Color
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.ui.platform.ComposeView
import com.rouge41.kmm.compose.test.Content
import com.rouge41.kmm.compose.test.Resources
import com.rouge41.kmm.compose.Android
import com.rouge41.kmm.compose.RootView
import moe.tlaster.precompose.lifecycle.PreComposeActivity
import moe.tlaster.precompose.lifecycle.setContent

class MainActivity : AppCompatActivity() {
class MainActivity : PreComposeActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

//has to be set in code or in theme
// has to be set in code or in theme
window.decorView.setBackgroundColor(Color.WHITE)
window.statusBarColor = Color.parseColor("#cc7000")
//window.statusBarColor = Color.WHITE
//window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR

val cv: ComposeView = findViewById(R.id.compose_view)
val resources = Resources(logo = R.drawable.logo)

cv.setContent {
Content(resources)
Android.context = this
setContent {
RootView()
}
}
}
16 changes: 0 additions & 16 deletions androidApp/src/main/res/layout/activity_main.xml

This file was deleted.

10 changes: 7 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
buildscript {
repositories {
gradlePluginPortal()
jcenter()
google()
jcenter()
mavenCentral()
maven("https://jitpack.io")
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${Version.kotlin}")
classpath("com.android.tools.build:gradle:7.1.2")
classpath(Kotlin.gradle)
classpath(Android.gradle)
classpath(Kotlin.serialization)
}
}

Expand All @@ -16,5 +19,6 @@ allprojects {
google()
jcenter()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
}
6 changes: 1 addition & 5 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
repositories {
jcenter()
mavenCentral()
}

plugins {
`kotlin-dsl`
}

kotlinDslPluginOptions {
experimentalWarning.set(false)
}
Loading

0 comments on commit 28336a8

Please sign in to comment.