KmpLibrary

KMPTestApp - Demo Application

KMPTestApp is a comprehensive demonstration application that showcases all the features and capabilities of the SweetMeSoft KMP Library. It serves as both a testing ground and a practical example of how to implement the library components in a real-world application.

Overview

This application demonstrates:

Features Demonstrated

UI Components

Maps Integration

Utilities

Project Structure

kmptestapp/
├── src/
│   ├── androidMain/
│   │   └── kotlin/
│   │       └── com/sweetmesoft/kmptestapp/
│   │           └── MainActivity.kt
│   ├── commonMain/
│   │   └── kotlin/
│   │       └── com/sweetmesoft/kmptestapp/
│   │           ├── App.kt                    # Main entry point
│   │           ├── PhotoProfileRequest.kt    # Data model
│   │           ├── screens/
│   │           │   ├── about/
│   │           │   │   └── AboutScreen.kt    # About screen
│   │           │   ├── main/
│           │   │   ├── MainScreen.kt     # Main screen (Grid menu)
│           │   │   └── MainViewModel.kt  # Main ViewModel
│           │   ├── maps/
│           │   │   └── MapScreen.kt      # Maps demonstration
│           │   ├── dialogs/
│           │   │   └── DialogsScreen.kt  # Dialogs & Alerts
│           │   ├── pickers/
│           │   │   └── PickersScreen.kt  # Date/Time Pickers
│           │   ├── controls/
│           │   │   └── ControlsScreen.kt # UI Controls
│           │   └── splash/
│   │           │       └── SplashScreen.kt   # Splash screen
│   │           └── theme/
│   │               ├── Color.kt              # Theme colors
│   │               └── Theme.kt              # Theme configuration
│   └── iosMain/
│       └── kotlin/
│           └── com/sweetmesoft/kmptestapp/
│               └── MainViewController.kt
└── build.gradle.kts

Getting Started

Prerequisites

  1. Android Studio (latest stable version)
  2. Xcode (for iOS development)
  3. JDK 17 or higher
  4. Kotlin Multiplatform Mobile plugin

Installation

  1. Clone the repository:
    git clone https://github.com/erickvelasco11/KmpLibrary.git
    cd KmpLibrary
    
  2. Open in Android Studio:
    • Open Android Studio
    • Select “Open an existing project”
    • Navigate to the cloned repository
    • Select the root directory
  3. Sync the project:
    ./gradlew build
    

Running the Application

Android

# Run on Android device/emulator
./gradlew :kmptestapp:installDebug

# Or from Android Studio:
# 1. Select 'kmptestapp' configuration
# 2. Choose your target device
# 3. Click Run

iOS

# Generate Xcode project
./gradlew :kmptestapp:embedAndSignAppleFrameworkForXcode

# Open in Xcode
open iosApp/iosApp.xcodeproj

# Or from Android Studio with KMP plugin:
# 1. Select iOS configuration
# 2. Choose your target simulator/device
# 3. Click Run

Desktop (Experimental)

# Run desktop version
./gradlew :kmptestapp:run

Configuration

API Keys Setup

To use maps functionality, configure the required API keys:

Android

Add your Google Maps API key to android/src/main/res/values/strings.xml:

<resources>
    <string name="google_maps_key">YOUR_API_KEY_HERE</string>
</resources>

iOS

Add location permissions to iosApp/iosApp/Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to show maps</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs location access to show maps</string>

Dependencies

The app uses the following main dependencies:

commonMain.dependencies {
    implementation(projects.library)
    implementation(projects.kmpcontrols)
    implementation(projects.kmpmaps)
    
    // Navigation
    implementation(libs.voyager.navigator)
    implementation(libs.voyager.bottom.sheet.navigator)
    implementation(libs.voyager.tab.navigator)
    implementation(libs.voyager.transitions)
    
    // UI and Compose
    implementation(compose.runtime)
    implementation(compose.foundation)
    implementation(compose.material3)
    implementation(compose.ui)
    implementation(compose.components.resources)
    implementation(compose.components.uiToolingPreview)
    
    // Lifecycle
    implementation(libs.androidx.lifecycle.viewmodel)
    implementation(libs.androidx.lifecycle.runtime.compose)
    
    // Utilities
    implementation(libs.kotlinx.datetime)
    implementation(libs.kotlinx.serialization.json)
}

Key Examples

1. Main Screen Implementation

The main screen demonstrates the use of LocalGridList to navigate to different feature modules:

class MainScreen : BaseBottomBarScreen() {
    @Composable
    override fun ScreenContent() {
        val navigator = LocalNavigator.currentOrThrow
        
        val menuItems = listOf(
            MainMenuItem("Controls", "Input controls", TablerIcons.Forms) { 
                navigator.push(ControlsScreen()) 
            },
            MainMenuItem("Maps", "Map integration", TablerIcons.Map) { 
                navigator.push(MapScreen()) 
            },
            MainMenuItem("Dialogs", "Alerts & Dialogs", TablerIcons.Message) { 
                navigator.push(DialogsScreen()) 
            },
            // ... other items
        )
        
        LocalGridList(
            list = menuItems,
            columns = 2,
            contentPadding = PaddingValues(16.dp)
        ) { _, item ->
            MenuCard(item)
        }
    }
}

2. Form Validation Example

@Composable
fun UserFormExample() {
    var name by remember { mutableStateOf("") }
    var email by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }
    
    val isFormValid = remember(name, email, password) {
        name.isNotBlank() && 
        StringUtils.isValidEmail(email) && 
        password.length >= 6
    }
    
    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        OutlinedTextField(
            value = name,
            onValueChange = { name = it },
            label = { Text("Name") },
            modifier = Modifier.fillMaxWidth()
        )
        
        OutlinedTextField(
            value = email,
            onValueChange = { email = it },
            label = { Text("Email") },
            isError = email.isNotBlank() && !StringUtils.isValidEmail(email),
            modifier = Modifier.fillMaxWidth()
        )
        
        PasswordControl(
            value = password,
            onValueChange = { password = it },
            label = "Password",
            supportingText = if (password.isNotBlank() && password.length < 6) {
                "Password must be at least 6 characters"
            } else null
        )
        
        Button(
            onClick = { /* Submit form */ },
            enabled = isFormValid,
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Register")
        }
    }
}

3. Network Integration

class MainViewModel : BaseViewModel() {
    fun testNetworkCall() {
        viewModelScope.launch {
            setLoading(true)
            try {
                val result = NetworkUtils.get<String>("https://api.example.com/test")
                PopupHandler.showAlert(
                    title = "Success",
                    message = "Response: $result"
                )
            } catch (e: Exception) {
                setError(e.message)
            } finally {
                setLoading(false)
            }
        }
    }
}

Architecture Patterns

MVVM Pattern

The app follows the Model-View-ViewModel pattern:

Uses Voyager for type-safe navigation:

@Composable
fun App() {
    AppTheme {
        Navigator(screen = SplashScreen()) { navigator ->
            BaseViewModel.navigator = navigator
            SlideTransition(navigator)
        }
    }
}

State Management

Utilizes StateFlow for reactive state management:

class MainViewModel : BaseViewModel() {
    private val _uiState = MutableStateFlow(MainUiState())
    val uiState: StateFlow<MainUiState> = _uiState.asStateFlow()
}

Testing

The app includes examples of:

Customization

Themes

Custom themes are defined in the theme package:

@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = if (darkTheme) {
        DarkColorScheme
    } else {
        LightColorScheme
    }
    
    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        content = content
    )
}

Adding New Features

  1. Create new screen in screens package
  2. Add navigation logic
  3. Implement ViewModel if needed
  4. Add to main navigation

Troubleshooting

Common Issues

  1. Build errors: Ensure all dependencies are properly synced
  2. iOS build issues: Check Xcode version compatibility
  3. Maps not showing: Verify API keys are correctly configured
  4. Network errors: Check internet connectivity and API endpoints

Getting Help

Contributing

Contributions are welcome! Please read the Contributing Guide for details on our code of conduct and the process for submitting pull requests.

License

This project is part of the SweetMeSoft KMP Library suite. See the LICENSE file for details.