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.
This application demonstrates:
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
git clone https://github.com/erickvelasco11/KmpLibrary.git
cd KmpLibrary
./gradlew build
# Run on Android device/emulator
./gradlew :kmptestapp:installDebug
# Or from Android Studio:
# 1. Select 'kmptestapp' configuration
# 2. Choose your target device
# 3. Click Run
# 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
# Run desktop version
./gradlew :kmptestapp:run
To use maps functionality, configure the required API keys:
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>
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>
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)
}
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)
}
}
}
@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")
}
}
}
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)
}
}
}
}
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)
}
}
}
Utilizes StateFlow for reactive state management:
class MainViewModel : BaseViewModel() {
private val _uiState = MutableStateFlow(MainUiState())
val uiState: StateFlow<MainUiState> = _uiState.asStateFlow()
}
The app includes examples of:
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
)
}
screens packageContributions are welcome! Please read the Contributing Guide for details on our code of conduct and the process for submitting pull requests.
This project is part of the SweetMeSoft KMP Library suite. See the LICENSE file for details.