# KMP Compose Sample

English | [简体中文](./README-zh_CN.md)

**Version Information**
| Component | Version |
| --- | --- |
| Kotlin | 2.2.21-ohos-01 |
| Compose Multiplatform | 1.9.2-eazytec-001 |
| Gradle | 8.14.3 |
| Android Gradle Plugin | 8.11.2 |
| Android SDK | Compile 36 / Min 24 / Target 36 |

A sample project using Kotlin Multiplatform + Compose Multiplatform, targeting Android and iOS. This project demonstrates component navigation and property showcase grouped by library, including:
- `org.jetbrains.compose.ui:ui`
- `org.jetbrains.compose.foundation:foundation`
- `org.jetbrains.compose.material:material`
- `org.jetbrains.compose.material3:material3`

**The home page displays Groups -> Component List. Clicking a component navigates to its property demo page, showcasing its main properties and interactions.**

**Features**
- Grouped Navigation: The home page lists component/plugin names by four groups.
- Detailed Demos: Enter a component page to adjust properties via controls and preview effects in real-time.
- Cross-Platform: Shared UI code, running synchronously on Android and iOS with a single Compose codebase.
- Extensible: Support adding more component demo pages to each group; Material group is organized in a separate directory.

**Directory Structure**
- `composeApp/src/commonMain/kotlin/com/example/kmptpc_compose_sample/`
  - `App.kt`: App entry point, handling group lists and page navigation.
  - `ComponentDemos.kt`: Home page list, routing for component demo pages, and some demo implementations.
  - `navigation/Navigation.kt`: Group models, component manifest, page models (`Page`).
  - `demos/material/`: Demo pages for the Material group (recommended: one file per component).
- `composeApp/src/androidMain/kotlin/com/example/kmptpc_compose_sample/MainActivity.kt`: Android entry point.
- `composeApp/src/iosMain/kotlin/com/example/kmptpc_compose_sample/MainViewController.kt`: iOS entry controller.
- `iosApp/iosApp/ContentView.swift`: Bridges `MainViewController()` in SwiftUI.

**Component Demo Navigation**
- Group titles use library coordinate strings (e.g., `org.jetbrains.compose.ui:ui`).
- Clicking a component list item navigates to the demo page, which includes:
  - Common property controls (enabled state, size, color, shape, shadow, etc.)
  - Interactive examples (click counters, scrolling lists, etc.)
- Material group includes but is not limited to the following demo pages (directory: `demos/material/`):
  - `AlertDialogSample.kt`
  - `AppBarSamples.kt`
  - `BackdropScaffoldSamples.kt`
  - `BadgeSamples.kt`
  - `BottomNavigationSamples.kt`
  - `BottomSheetScaffoldSamples.kt`
  - `ButtonSamples.kt`
  - `CardSamples.kt`
  - `ChipSamples.kt`
  - `ContentAlphaSamples.kt`
  - `DrawerSamples.kt`
  - `ElevationSamples.kt`
  - `ExposedDropdownMenuSamples.kt`
  - `FloatingActionButtonSamples.kt`
  - `IconButtonSamples.kt`
  - `ListSamples.kt`
  - `MenuSamples.kt`
  - `ModalBottomSheetSamples.kt`
  - `NavigationRailSample.kt`
  - `ProgressIndicatorSamples.kt`
  - `PullRefreshSamples.kt`
  - `ScaffoldSamples.kt`
  - `SelectionControlsSamples.kt`
  - `SliderSample.kt`
  - `SurfaceSamples.kt`
  - `SwipeableSamples.kt`
  - `SwipeToDismissSamples.kt`
  - `TabSamples.kt`
  - `TextFieldSamples.kt`
  - `TextSamples.kt`
  - `ThemeSamples.kt`

You can supplement/refine the "full property effects" for each demo page as needed.

**Dependencies**
- Compose Runtime/Foundation/UI/Material3: Included in `commonMain` dependencies in `composeApp/build.gradle.kts`.
- Material (Legacy Material Components): Add `implementation(compose.material)` to `commonMain`.
- Others: AndroidX Lifecycle, etc., included as needed.

**Build & Run**
- Android (Recommended: Run in Android Studio or via command line):
  ```bash
  ./gradlew :composeApp:assembleDebug
  ```
- Windows:
  ```shell
  .\gradlew.bat :composeApp:assembleDebug
  ```

- iOS Build & Run
  Use the run configuration in the IDE toolbar to build and run the iOS development version, or open the [/iosApp](./iosApp) directory in Xcode and run.

- HarmonyOS Build & Run
  1. Compile and publish Shared Library to the HarmonyOS project directory:
     ```bash
     ./gradlew :composeApp:publishDebugBinariesToHarmonyApp
     ```
  2. Open the `harmonyApp` directory with DevEco Studio.
  3. Sync the project in DevEco Studio and run on a HarmonyOS device or emulator.

---

Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html)…


## Material3 Naming Conventions & Common Errors

- Naming Conventions
  - All Material3 sample functions are uniformly named `XXXSamplesM3()` to avoid overload resolution ambiguity with Material (M2) functions of the same name.
  - If you need to keep the same name, use import aliases at the call site:
    - `import com.example...demos.material.AlertDialogSamples as AlertDialogSamplesM2`
    - `import com.example...demos.material3.AlertDialogSamples as AlertDialogSamplesM3`

- Necessary Imports
  - `import androidx.compose.ui.Modifier`
  - `import androidx.compose.ui.unit.dp` (when size units are needed)
  - `import androidx.compose.ui.unit.sp` (when font size units are needed)
  - `import androidx.compose.foundation.layout.*` (when `Row/Column/Box/padding/size/Arrangement` etc. are needed)
  - If using alignment: `import androidx.compose.ui.Alignment`

- Opt-in for Experimental APIs
  - Some components in Material3 (`ModalBottomSheet`, `DatePicker`, `TimePicker`, `SearchBar`, etc.) require:
    - Adding at the top of the file: `@file:OptIn(androidx.compose.material3.ExperimentalMaterial3Api::class)`

- Gestures & Scrolling
  - Material (M2) can use `SwipeToDismiss`; Material3 version can use `SwipeToDismissBox` (depending on version).
  - Basic gesture layer replacement: `AnchoredDraggableState` + `anchoredDraggable` (foundation), avoiding the removed `swipeable` series.

## Directory Structure (Example)
- `demos/ui/*`: UI group samples (e.g., `BoxDemo.kt`)
- `demos/foundation/*`: Foundation group samples (e.g., `BasicTextDemo.kt`, `LazyColumnDemo.kt`)
- `demos/material/*`: Material (M2) samples
- `demos/material3/*`: Material3 samples (uniformly named `XXXSamplesM3()`)

## Navigation & Display
- The home page displays the `componentDemos` list from `navigation/Navigation.kt` by group.
- `DemoScreen(demo: ComponentDemo)` routes to the corresponding sample based on `demo.id`.
- All Material3 samples are integrated with unified titles and routing.

## Common Errors
- `Unresolved reference 'Modifier'/'dp'/'sp'`: Missing necessary imports, refer to above.
- `SmallTopAppBar` unresolved: Use `TopAppBar` or other M3 top bar components.
- Experimental APIs (`DatePicker`, `TimePicker`, `ModalBottomSheet`, `SearchBar`, etc.): Add at the top of the file
  - `@file:OptIn(androidx.compose.material3.ExperimentalMaterial3Api::class)`

## HarmonyOS Common Issues & Solutions

### 1. Runtime Crash: `ArkUIViewController_setId` or `Not mapped` Error

**Symptom**:
App crashes immediately upon launch or navigation to a Compose page, with logs containing `ArkUIViewController_setId`, `Not mapped`, or `libComposeApp.so` related errors.

**Cause**:
1.  **Symbol Resolution Error**: The `napi_init.cpp` in the Native layer failed to correctly load Compose's initialization function `androidx_compose_ui_arkui_init`.
2.  **Controller Initialization Timing**: Attempting to create `ArkUIViewController` before the Native module is fully loaded, or not initializing it correctly in the `aboutToAppear` lifecycle.
3.  **Missing Library Name**: Correct `libraryName` was not specified in the ArkTS `Compose` component.

**Solution**:

1.  **Fix `napi_init.cpp`**:
    Ensure the dynamic symbol lookup includes a fallback mechanism (compatible with different versions of symbol names) and uses `dlsym(RTLD_DEFAULT, ...)`.
    ```cpp
    // entry/src/main/cpp/napi_init.cpp
    typedef void (*init_fn)(napi_env, napi_value);
    
    static napi_value Init(napi_env env, napi_value exports) {
        // Try loading new version symbol
        init_fn fn = reinterpret_cast<init_fn>(dlsym(RTLD_DEFAULT, "androidx_compose_ui_arkui_utils_init"));
        if (fn == nullptr) {
            // Fallback to old version symbol
            fn = reinterpret_cast<init_fn>(dlsym(RTLD_DEFAULT, "androidx_compose_ui_arkui_init"));
        }
        if (fn != nullptr) {
            fn(env, exports);
        }
        // ...
    }
    ```

2.  **Correct Controller Initialization (`Index.ets`)**:
    Check `nativeApi` and initialize the Controller in `aboutToAppear`, adding exception handling.
    ```typescript
    // entry/src/main/ets/pages/Index.ets
    aboutToAppear() {
      if (nativeApi) {
        try {
          this.controller = nativeApi.MainArkUIViewController();
        } catch (e) {
          hilog.error(DOMAIN, 'Compose', 'Controller creation failed: %{public}s', JSON.stringify(e));
        }
      }
    }
    ```

3.  **Specify Library Name**:
    Explicitly pass `libraryName` (usually `'entry'` or your dynamic library name) when using the `Compose` component.
    ```typescript
    Compose({
      controller: this.controller,
      libraryName: 'entry', // Must be specified
      onBackPressed: () => false
    })
    ```

### 2. Build Error: `input file does not exist` (:cinteropResourceOhosArm64)

**Cause**: The `resource.def` file location does not match the path configured in `build.gradle.kts`.
**Solution**: Ensure the file is located at `src/ohosArm64Main/cinterop/resource.def`, or adjust the `defFile(file(...))` path configuration.

### 3. Dependency Conflict: Variant mismatch / 403 Forbidden

**Cause**: Some AndroidX libraries (like `lifecycle`, `savedstate`) are not fully adapted to the OHOS target, causing Gradle to pull incorrect artifacts or face permission issues during resolution.
**Solution**: Exclude conflicting groups in the `ohos` configuration in `build.gradle.kts`:
```kotlin
configurations.all {
    if (name.contains("ohos", ignoreCase = true)) {
        exclude(group = "org.jetbrains.androidx.lifecycle")
        exclude(group = "org.jetbrains.androidx.savedstate")
    }
}
```

## Gradle Build Common Issues & Pitfalls Guide

### 1. `Unresolved reference` Error
**Symptom**: `build.gradle.kts` reports `Unresolved reference: libs.xxx`.
**Cause**: The corresponding library or plugin alias is not defined in `libs.versions.toml`, or the alias spelling is inconsistent (Gradle converts `-` in TOML to `.`).
**Solution**:
- Check the `[libraries]` and `[plugins]` blocks in `libs.versions.toml`.
- Confirm alias spelling. For example, `compose-multiplatform-export` should be referenced as `libs.compose.multiplatform.export` in Kotlin DSL.

### 2. Skiko Version Incompatibility
**Symptom**: OHOS runtime crash or rendering exception, indicating Skiko version issues.
**Cause**: The default Skiko version depended on by Compose Multiplatform may not be adapted to the specific OHOS version.
**Solution**:
Force a specific Skiko version in `build.gradle.kts`:
```kotlin
resolutionStrategy {
    eachDependency {
        if (requested.group == "org.jetbrains.skiko" && requested.name == "skiko") {
             if (configName.contains("ohos", ignoreCase = true)) {
                 useVersion("0.9.22.2-ohos-001") // Use adapted version
             }
        }
    }
}
```

### 3. Dependency Conflict: `androidx.collection`
**Symptom**: Build failure indicating dependency version conflict.
**Cause**: Different libraries depend on different versions of `androidx.collection`.
**Solution**:
Force a specific version and exclude conflicting modules:
```kotlin
configurations.all {
    resolutionStrategy {
        eachDependency {
            if (requested.group == "androidx.collection" && requested.name == "collection") {
                useVersion(libs.versions.androidx.collection.get())
            }
        }
    }
    exclude(group = "androidx.collection", module = "collection-jvm")
}
```
