推荐你一个基于Koin, Ktor Paging等组件的KMM Compose Multiplatform项目

2023-11-27 13:15

本文主要是介绍推荐你一个基于Koin, Ktor Paging等组件的KMM Compose Multiplatform项目,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

推荐你一个基于Koin, Ktor & Paging等组件的KMM Compose Multiplatform项目

Kotlin Multiplatform Mobile(KMM)已经从一个雄心勃勃的想法发展成为一个稳定而强大的框架,为开发人员提供了在多个平台上无缝共享代码的能力。通过最近的稳定版本里程碑,KMM已成为跨平台开发领域的改变者。

环境设置

带有Kotlin Multiplatform插件的Android Studio

https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile

Kotlin版本:1.9.10
Gradle版本:8.1.1
用于演示的任何开放API - 在这里,我使用Internshala列表API来填充UI。
注意:使用Internshala的开放列表API,并没有使用不当的做法:)

设置Koin和Ktor

  1. 将两个依赖项添加到:shared模块中,我使用gradlelibrary目录作为依赖项。
    io.insert-koin:koin-core:3.5.0
  2. 对于Ktor,根据您的API,有多个依赖项各自具有自己的目的。
  3. 由于注入将在平台级别进行,因此我们需要将初始化部分暴露给两个平台。为此,我们将创建一个带有initKoin()方法的Helper类,并从iOS和Android的应用程序类调用此方法。
  4. initKoin() → 我们在这里定义我们稍后需要的所有依赖项。
//shared/src/commonMain/kotlin/di/Koin.kt
fun initKoin() = startKoin {modules(networkModule)
}
private val networkModule = module {single {HttpClient {defaultRequest {url.takeFrom(URLBuilder().takeFrom("https://internshala.com/"))}install(HttpTimeout) {requestTimeoutMillis = 15_000}install(ContentNegotiation) {json(Json {ignoreUnknownKeys = trueprettyPrint = true})}install(Logging) {level = LogLevel.ALLlogger = object : Logger {override fun log(message: String) {println(message)}}}}}
}
  1. 从两个平台调用此方法
  • iOS:我们需要在2个不同的文件中进行更改。(共享iOS文件和特定于平台的文件)
// :shared/src/iosMain/kotlin/Koin.ios.kt
class Koin {fun initKoin() {di.initKoin()}
}
// iosApp/iosApp/iOSApp.swift
import SwiftUI
import shared
@main
struct iOSApp: App {var body: some Scene {WindowGroup {ContentView()}}init() {KoinKt.doInitKoin()}
  • Android:与iOS不同,对于Android,我们可以直接调用:shared initKoin()方法并初始化Koin。
class App : Application() {override fun onCreate() {super.onCreate()initKoin()}
}

将API响应与Result类进行映射

我们将在Ktor的HttpClient上编写一个小的扩展,用于执行所有API调用并将响应包装在Result(成功/错误)中,并附带适当的消息。

suspend inline fun <reified T> HttpClient.fetch(block: HttpRequestBuilder.() -> Unit
): Result<T> = try {val response = request(block)if (response.status == HttpStatusCode.OK)Result.Success(response.body())elseResult.Error(Throwable("${response.status}: ${response.bodyAsText()}"))
} catch (e: Exception) {Result.Error(e)
}sealed interface Result<out R> {class Success<out R>(val value: R) : Result<R>data object Loading : Result<Nothing>class Error(val throwable: Throwable) : Result<Nothing>
}

设置分页库

在实施Koin和Ktor之后,接下来是分页库。我们将使用来自cashapp的开源库。
由于Paging提供了多种方法来处理错误和API响应,我们将创建一个组合,以处理这种行为。

@Composable
fun <T : Any> PagingListUI(data: LazyPagingItems<T>,content: @Composable (T) -> Unit
) {LazyColumn(modifier = Modifier.fillMaxSize().background(Color.White),horizontalAlignment = Alignment.CenterHorizontally,) {items(data.itemCount) { index ->val item = data[index]item?.let { content(it) }Divider(color = UiColor.background,thickness = 10.dp,modifier = Modifier.border(border = BorderStroke(0.5.dp, Color.LightGray)))}data.loadState.apply {when {refresh is LoadStateNotLoading && data.itemCount < 1 -> {item {Box(modifier = Modifier.fillParentMaxSize(),contentAlignment = Alignment.Center) {Text(text = "No Items",modifier = Modifier.align(Alignment.Center),textAlign = TextAlign.Center)}}}refresh is LoadStateLoading -> {item {Box(modifier = Modifier.fillParentMaxSize(),contentAlignment = Alignment.Center) {CircularProgressIndicator(color = UiColor.primary)}}}append is LoadStateLoading -> {item {CircularProgressIndicator(color = UiColor.primary,modifier = Modifier.fillMaxWidth().padding(16.dp).wrapContentWidth(Alignment.CenterHorizontally))}}refresh is LoadStateError -> {item {ErrorView(message = "No Internet Connection.",onClickRetry = { data.retry() },modifier = Modifier.fillParentMaxSize())}}append is LoadStateError -> {item {ErrorItem(message = "No Internet Connection",onClickRetry = { data.retry() },)}}}}}
}@Composable
private fun ErrorItem(message: String,modifier: Modifier = Modifier,onClickRetry: () -> Unit
) {Row(modifier = modifier.padding(16.dp),horizontalArrangement = Arrangement.SpaceBetween,verticalAlignment = Alignment.CenterVertically) {Text(text = message,maxLines = 1,modifier = Modifier.weight(1f),color = androidx.compose.ui.graphics.Color.Red)OutlinedButton(onClick = onClickRetry) {Text(text = "Try again")}}
}@Composable
private fun ErrorView(message: String,modifier: Modifier = Modifier,onClickRetry: () -> Unit
) {Column(modifier = modifier.padding(16.dp).onPlaced { _ ->},verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text(text = message,maxLines = 1,modifier = Modifier.align(Alignment.CenterHorizontally),color = androidx.compose.ui.graphics.Color.Red)OutlinedButton(onClick = onClickRetry, modifier = Modifier.fillMaxWidth().padding(16.dp).wrapContentWidth(Alignment.CenterHorizontally)) {Text(text = "Try again")}}
}

设置App和Internship屏幕的UI部分

我们的应用程序入口点 - App.kt

// :shared/src/commonMain/kotlin/App.kt
@Composable
fun App() {MaterialTheme {val screens = Screen.values()var selectedScreen by remember { mutableStateOf(screens.first()) }Scaffold(bottomBar = {BottomNavigation(backgroundColor = Color.White,modifier = Modifier.height(64.dp)) {screens.forEach { screen ->BottomNavigationItem(modifier = Modifier.background(Color.White),selectedContentColor = ui.theme.Color.textOnPrimary,unselectedContentColor = Color.Gray,icon = {Icon(imageVector = getIconForScreen(screen),contentDescription = screen.textValue)},label = { Text(screen.textValue) },selected = screen == selectedScreen,onClick = { selectedScreen = screen },)}}},content = { getScreen(selectedScreen) })}
}@Composable
fun getIconForScreen(screen: Screen): ImageVector {return when (screen) {Screen.INTERNSHIPS -> Icons.Default.AccountBoxScreen.JOBS -> Icons.Default.AddScreen.COURSES -> Icons.Default.Notificationselse -> Icons.Default.Home}
}@Composable
fun getScreen(selectedScreen: Screen) = when (selectedScreen) {Screen.INTERNSHIPS -> InternshipsScreen().content()Screen.JOBS -> JobsScreen()Screen.COURSES -> CoursesScreen()else -> HomeScreen()
}

获取分页数据的实习界面

class InternshipsScreen : KoinComponent {private val viewModel: InternshipViewModel by inject()@Composablefun content() {val result by rememberUpdatedState(viewModel.internships.collectAsLazyPagingItems())return Scaffold(topBar = {TopAppBar(title = { Text("Internships") },elevation = 0.dp,navigationIcon = {IconButton(onClick = { println("Drawer clicked") }) {Icon(imageVector = Icons.Default.Menu, contentDescription = "Menu")}},actions = {IconButton(onClick = { println("Search Internships!") }) {Icon(imageVector = Icons.Default.Search, contentDescription = "Search")}},backgroundColor = Color.White)},drawerContent = { /*Drawer content*/ },content = { PagingListUI(data = result, content = { InternshipCard(it) }) },)}
}

输出的预览

GitHub

https://github.com/Prashant-123/kmm-ktor-koin
https://github.com/Kamel-Media/Kamel

这篇关于推荐你一个基于Koin, Ktor Paging等组件的KMM Compose Multiplatform项目的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/427746

相关文章

Python多进程、多线程、协程典型示例解析(最新推荐)

《Python多进程、多线程、协程典型示例解析(最新推荐)》:本文主要介绍Python多进程、多线程、协程典型示例解析(最新推荐),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 目录一、multiprocessing(多进程)1. 模块简介2. 案例详解:并行计算平方和3. 实现逻

springboot+vue项目怎么解决跨域问题详解

《springboot+vue项目怎么解决跨域问题详解》:本文主要介绍springboot+vue项目怎么解决跨域问题的相关资料,包括前端代理、后端全局配置CORS、注解配置和Nginx反向代理,... 目录1. 前端代理(开发环境推荐)2. 后端全局配置 CORS(生产环境推荐)3. 后端注解配置(按接口

Vue 2 项目中配置 Tailwind CSS 和 Font Awesome 的最佳实践举例

《Vue2项目中配置TailwindCSS和FontAwesome的最佳实践举例》:本文主要介绍Vue2项目中配置TailwindCSS和FontAwesome的最... 目录vue 2 项目中配置 Tailwind css 和 Font Awesome 的最佳实践一、Tailwind CSS 配置1. 安

Spring Boot项目打包和运行的操作方法

《SpringBoot项目打包和运行的操作方法》SpringBoot应用内嵌了Web服务器,所以基于SpringBoot开发的web应用也可以独立运行,无须部署到其他Web服务器中,下面以打包dem... 目录一、打包为JAR包并运行1.打包为可执行的 JAR 包2.运行 JAR 包二、打包为WAR包并运行

PyQt6中QMainWindow组件的使用详解

《PyQt6中QMainWindow组件的使用详解》QMainWindow是PyQt6中用于构建桌面应用程序的基础组件,本文主要介绍了PyQt6中QMainWindow组件的使用,具有一定的参考价值,... 目录1. QMainWindow 组php件概述2. 使用 QMainWindow3. QMainW

Spring Boot集成SLF4j从基础到高级实践(最新推荐)

《SpringBoot集成SLF4j从基础到高级实践(最新推荐)》SLF4j(SimpleLoggingFacadeforJava)是一个日志门面(Facade),不是具体的日志实现,这篇文章主要介... 目录一、日志框架概述与SLF4j简介1.1 为什么需要日志框架1.2 主流日志框架对比1.3 SLF4

Nginx部署React项目时重定向循环问题的解决方案

《Nginx部署React项目时重定向循环问题的解决方案》Nginx在处理React项目请求时出现重定向循环,通常是由于`try_files`配置错误或`root`路径配置不当导致的,本文给大家详细介... 目录问题原因1. try_files 配置错误2. root 路径错误解决方法1. 检查 try_f

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ

解决Maven项目报错:failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0的问题

《解决Maven项目报错:failedtoexecutegoalorg.apache.maven.plugins:maven-compiler-plugin:3.13.0的问题》这篇文章主要介... 目录Maven项目报错:failed to execute goal org.apache.maven.pl

Python开发文字版随机事件游戏的项目实例

《Python开发文字版随机事件游戏的项目实例》随机事件游戏是一种通过生成不可预测的事件来增强游戏体验的类型,在这篇博文中,我们将使用Python开发一款文字版随机事件游戏,通过这个项目,读者不仅能够... 目录项目概述2.1 游戏概念2.2 游戏特色2.3 目标玩家群体技术选择与环境准备3.1 开发环境3