推荐你一个基于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

相关文章

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

Vue ElementUI中Upload组件批量上传的实现代码

《VueElementUI中Upload组件批量上传的实现代码》ElementUI中Upload组件批量上传通过获取upload组件的DOM、文件、上传地址和数据,封装uploadFiles方法,使... ElementUI中Upload组件如何批量上传首先就是upload组件 <el-upl

SpringBoot项目注入 traceId 追踪整个请求的日志链路(过程详解)

《SpringBoot项目注入traceId追踪整个请求的日志链路(过程详解)》本文介绍了如何在单体SpringBoot项目中通过手动实现过滤器或拦截器来注入traceId,以追踪整个请求的日志链... SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排

前端 CSS 动态设置样式::class、:style 等技巧(推荐)

《前端CSS动态设置样式::class、:style等技巧(推荐)》:本文主要介绍了Vue.js中动态绑定类名和内联样式的两种方法:对象语法和数组语法,通过对象语法,可以根据条件动态切换类名或样式;通过数组语法,可以同时绑定多个类名或样式,此外,还可以结合计算属性来生成复杂的类名或样式对象,详细内容请阅读本文,希望能对你有所帮助...

Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)

《Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)》本文介绍了如何使用Python和Selenium结合ddddocr库实现图片验证码的识别和点击功能,感兴趣的朋友一起看... 目录1.获取图片2.目标识别3.背景坐标识别3.1 ddddocr3.2 打码平台4.坐标点击5.图

Java中实现订单超时自动取消功能(最新推荐)

《Java中实现订单超时自动取消功能(最新推荐)》本文介绍了Java中实现订单超时自动取消功能的几种方法,包括定时任务、JDK延迟队列、Redis过期监听、Redisson分布式延迟队列、Rocket... 目录1、定时任务2、JDK延迟队列 DelayQueue(1)定义实现Delayed接口的实体类 (

Vue3中的动态组件详解

《Vue3中的动态组件详解》本文介绍了Vue3中的动态组件,通过`component:is=动态组件名或组件对象/component`来实现根据条件动态渲染不同的组件,此外,还提到了使用`markRa... 目录vue3动态组件动态组件的基本使用第一种写法第二种写法性能优化解决方法总结Vue3动态组件动态

shell脚本自动删除30天以前的文件(最新推荐)

《shell脚本自动删除30天以前的文件(最新推荐)》该文章介绍了如何使用Shell脚本自动删除指定目录下30天以前的文件,并通过crontab设置定时任务,此外,还提供了如何使用Shell脚本删除E... 目录shell脚本自动删除30天以前的文件linux按照日期定时删除elasticsearch索引s

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内