本文主要内容参阅自
由于编辑器“文档解析”功用,一切代码段的格局均被更改,本人手动重新加上了言语符号,但真实懒得处理剩余的 Tab 了,故代码前有剩余缩进,望理解。
前语
Compose Multiplatform 是由 JetBrains 开发的声明式 UI 结构,答应开发者在不同渠道之间同享 UI 完结。1.6.0 版别带来了几个强壮的功用,以及与最新的 Kotlin 版别和 Google 最新的 Jetpack Compose 更新的兼容性。
Compose Multiplatform 1.6.0 版别更新内容包含:
- 更新了资源办理。(CMP 终于有自己的各种资源拜访支撑了!)
- 引入了 UI 测验 API。
- 增加了 iOS 辅佐功用支撑。
- 带来了许多其他功用和改善。
咱们下面将逐一介绍
通用资源 API
Compose Multiplatform 1.6.0 中最大且最受期待的改变,是改善了在通用 Kotlin 代码中同享和拜访资源的 API。该 API 现在答应您在 Compose Multiplatform 运用程序中包含和拜访更多类型的资源。
译注:在曾经,CMP 仅供给了十分简略的 resource() 函数用于返回 bytes,假如想拜访特定 String/fonts 等等,都需要凭借像 github.com/Skeptick/li… 这样的三方库
资源被组织在 commonMain
源集的一些目录中:
-
composeResources/drawable
包含图片 -
composeResources/font
包含字体 -
composeResources/values
包含字符串(以strings.xml
格局) -
composeResources/files
包含任何其他文件。
Compose Multiplatform 为一切这些资源类型(除 files
目录外)生成了类型安全的拜访器。例如,在 composeResources/drawable
目录中放置一个矢量图画 compose-multiplatform.xml
后,您能够运用生成的 Res
对象在 Compose Multiplatform 代码中拜访它:
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import kotlinproject.composeapp.generated.resources.*
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource
@OptIn(ExperimentalResourceApi::class)
@Composable
fun Logo() {
Image(
painterResource(Res.drawable.compose_multiplatform),
contentDescription = "CMP Logo"
)
}
新的资源 API 还答应您为不同用例供给同一资源的变体,包含区域设置、屏幕密度或主题。不管您是本地化文本、在深色形式下更改图标色彩,还是依据屏幕分辨率供给替代图画,您都能够经过向目录称号增加限定符来表达这些约束。
要更具体地了解资源限定符以及 Compose Multiplatform 1.6.0 中新资源 API 的全面概述,请查看官方文档! 请继续往下读
以下内容又来自:Images and resources | Kotlin Multiplatform Development Documentation
资源办理
Compose Multiplatform 供给了一个特别的库和 Gradle 插件支撑,用于在一切支撑的渠道上的通用代码中拜访资源。资源是静态内容,例如图画、字体和字符串。
该库是实验性的,其 API 或许会在将来更改。
在运用 Compose Multiplatform 中的资源时,请考虑当时状况:
-
几乎一切资源都是在调用线程中同步读取的。唯一的破例是 raw 文件和一切在 JS 渠道上异步读取的资源。
-
尚不支撑以流的形式读取大型 raw 文件,如长视频。请在用户设备上运用独自的文件,并运用文件体系 API 读取它们,例如 kotlinx-io 库。
-
尚不支撑多模块项目。JetBrains 团队正在尽力在未来的版别中增加此功用。现在,请将一切资源存储在主 application 模块中。
-
尚不支撑带有资源的 Compose Multiplatform 库的发布。JetBrains 团队正在尽力在未来的版别中增加此功用。
-
现在,仅为
commonMain
源集生成拜访器。JetBrains 团队正在尽力在未来的版别中扩展此功用。不过,您依然能够将特定于渠道的资源存储在对应渠道的
composeResources
目录中,并将它们读取为字节数组。一切资源都将包含在每个终究运用程序中。
设置
要在多渠道项目中拜访资源,请执行以下过程:
-
在
composeApp
目录中的build.gradle.kts
文件中,向commonMain
源集增加依靠项:kotlin { sourceSets { commonMain.dependencies { // 这是由 Compose Plugin 供给的特点,不需要自己在 toml 写 implementation(compose.components.resources) } } }
-
在
commonMain
目录中创立一个名为composeResources
的新目录: -
依据以下规则组织
composeResources
目录结构:- 图画应坐落
drawable
目录中。 - 字体应坐落
font
目录中。 - 字符串(
strings.xml
)应坐落values
目录中。 - 其他任何层次结构的文件应坐落
files
目录中。
- 图画应坐落
限定符
有时,相同的资源应依据环境以不同方法呈现,例如区域设置、屏幕密度或主题。例如,您或许需要为不同言语本地化文本或调整图画以适应深色主题。为此,该库供给了特别的限定符。
一切资源类型(除了 files
目录中的原始文件)都支撑限定符。运用连字符将限定符运用于目录称号:
该库支撑(按优先级次序)以下限定符:言语、主题 和 屏幕像素密度。
- 能够同时运用不同类型的限定符。例如,“drawable-en-rUS-mdpi-dark” 是英语(言语)在美国(地区)的图画,适用于深色主题的 160 DPI 屏幕。
- 假如不存在具有请求限定符的资源,则运用默许资源(没有限定符)。
言语和区域限定符
言语由两个字母的 ISO 639-1 言语代码界说。
您能够在言语代码中增加两个字母的 ISO 3166-1-alpha-2 区域代码。在这种状况下,区域代码必须具有小写 r
前缀。
言语和区域代码不区别大小写。
主题限定符
您能够增加“light”或“dark”限定符。然后,Compose Multiplatform 依据当时体系主题挑选必要的资源。
密度限定符
您能够运用以下密度限定符:
- “ldpi” − 120 DPI,0.75x 密度
- “mdpi” − 160 DPI,1x 密度
- “hdpi” − 240 DPI,1.5x 密度
- “xhdpi” − 320 DPI,2x 密度
- “xxhdpi” − 480 DPI,3x 密度
- “xxxhdpi” − 640dpi,4x 密度
资源依据体系中界说的屏幕密度进行挑选。
资源运用
导入项目后,将生成一个特别的 Res
类,用于拜访资源。要手动生成 Res
类,请运转 generateComposeResClass
Gradle 使命。
图画
您能够将可制作资源作为简略图画、光栅化图画或 XML 矢量拜访:
- 要将可制作资源作为
Painter
图画拜访,请运用painterResource()
函数:
@Composable
fun painterResource(resource: DrawableResource): Painter {...}
-
painterResource()
函数接受资源路径并返回Painter
值。该函数在除 Web 以外的一切方针上都以同步方法工作。关于 Web 方针,它在第一次重组时返回一个空的Painter
,在后续重组中用加载的图画替换它。-
painterResource()
加载光栅化图画格局(例如.png
、.jpg
、.bmp
、.webp
)的BitmapPainter
或 Android XML vector drawable 的VectorPainter
。
- XML vector drawables 与 Android 具有相同的格局,但不支撑对 Android 资源的外部引用。
-
- 要将可制作资源作为
ImageBitmap
光栅图画拜访,请运用imageResource()
函数:
@Composable
fun imageResource(resource: DrawableResource): ImageBitmap {...}
- 要将可制作资源作为
ImageVector
XML 矢量拜访,请运用vectorResource()
函数:
@Composable
fun vectorResource(resource: DrawableResource): ImageVector {...}
下面是您能够在 Compose Multiplatform 代码中拜访图画的示例:
Image(
painter = painterResource(Res.drawable.my_icon),
contentDescription = null
)
译者吐槽:原先的 painterResource(resource: String) 没得了,淦!
字符串
将一切字符串资源存储在 composeResources/values
目录中的 strings.xml
文件中,例如:
<resources>
<string name="app_name">译站</string>
<string name="title">一些标题</string>
</resources>
strings.xml
文件中的每个项目都会生成静态拜访器。
译注:
比如,关于以下的 xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="clear_content">清空</string> <string name="app_name">译站</string> <string name="tip_with_placeholder">请挑选 %s 并 %s</string> </resources>
它会生成
@ExperimentalResourceApi private object String0 { public val app_name: StringResource = org.jetbrains.compose.resources.StringResource( "string:app_name", "app_name", setOf( org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), ) ) public val clear_content: StringResource = org.jetbrains.compose.resources.StringResource( "string:clear_content", "clear_content", setOf( org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), ) ) public val tip_with_placeholder: StringResource = org.jetbrains.compose.resources.StringResource( "string:tip_with_placeholder", "tip_with_placeholder", setOf( org.jetbrains.compose.resources.ResourceItem(setOf(), "values/strings.xml"), ) ) } @ExperimentalResourceApi internal val Res.string.app_name: StringResource get() = String0.app_name @ExperimentalResourceApi internal val Res.string.clear_content: StringResource get() = String0.clear_content @ExperimentalResourceApi internal val Res.string.tip_with_placeholder: StringResource get() = String0.tip_with_placeholder
而 Res 类长这样:
@ExperimentalResourceApi internal object Res { /** * Reads the content of the resource file at the specified path and returns it as a byte array. * * Example: `val bytes = Res.readBytes("files/key.bin")` * * @param path The path of the file to read in the compose resource's directory. * @return The content of the file as a byte array. */ public suspend fun readBytes(path: String): ByteArray = readResourceBytes(path) public object drawable public object string public object font } ```
要将字符串资源作为 String
获取,请运用以下代码:
Composable 中
@Composable
fun stringResource(resource: StringResource): String {...}
@Composable
fun stringResource(resource: StringResource, vararg formatArgs: Any): String {...}
@Composable
fun stringArrayResource(resource: StringResource): List<String> {...}
例如:
Text(stringResource(Res.string.app_name))
非 Composable 中
suspend fun getString(resource: StringResource): String
suspend fun getString(resource: StringResource, vararg formatArgs: Any): String
suspend fun getStringArray(resource: StringResource): List<String>
例如:
coroutineScope.launch {
val appName = getString(Res.string.app_name)
}
您能够在字符串资源中运用特别符号:
-
n
— 换行 -
t
— 制表符 -
uXXXX
— 特定的 Unicode 字符
字符串模板
现在,字符串资源对参数具有根本支撑:
<!-- strings.xml -->
<resources>
<string name="str_template">你好,%1$s!您有 %2$d 条新消息。</string>
</resources>
关于用于 Composable 中的那些函数,%...s
和 %...d
之间没有区别,例如:
Text(stringResource(Res.string.str_template, "用户名", 100))
字体
将自界说字体((*.ttf
或 *.otf
文件)存储在 composeResources/font
目录中。
要将字体加载为 Font
类型,请运用 Font()
函数:
@Composable
fun Font(
resource: FontResource,
weight: FontWeight = FontWeight.Normal,
style: FontStyle = FontStyle.Normal
): Font
例如:
val fontAwesome = FontFamily(Font(Res.font.font_awesome))
原始文件(Raw Files)
要将任何原始文件加载为字节数组,请运用 Res.readBytes(path)
函数:
suspend fun readBytes(path: String): ByteArray
您能够将原始文件放在 composeResources/files
目录中,并在其间创立任何层次结构。
例如,要拜访原始文件,请运用以下代码:
Composable 中
var bytes by remember {
mutableStateOf(ByteArray(0))
}
LaunchedEffect(Unit) {
bytes = Res.readFileBytes("files/myDir/someFile.bin")
}
Text(bytes.decodeToString())
非 Composable 中
coroutineScope.launch {
val bytes = Res.readFileBytes("files/myDir/someFile.bin")
}
长途文件
只要作为运用程序一部分的文件才被视为资源。
您还能够经过 URL 从互联网加载长途文件:
通用 UI 测验 API
UI 测验能够帮助您保证运用程序的行为符合预期。在 Compose Multiplatform 1.6.0 中,咱们引入了一个实验性 API,答应您编写通用的 UI 测验,用于验证运用程序在结构支撑的不同渠道上的用户界面行为。
例如,您或许期望保证自界说组件在显现时正确显现带有适当时缀的信息字符串:
@Composable
fun MyInfoComposable(info: String, modifier: Modifier) {
Text(modifier = modifier, text = "[INFO] $info")
}
在最新版别的 Compose Multiplatform 中,您现在能够运用 UI 测验来验证组件在呈现时的确正确增加了前缀([info]
)。要做到这一点,您能够运用与 Android 上的 Jetpack Compose 相同的 finder、assertion、actions 和 mathcers。遵从此文档完结设置后,您能够编写一个测验来保证此前缀被正确增加:
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.runComposeUiTest
...
class InformationTest {
@OptIn(ExperimentalTestApi::class)
@Test
fun shouldPrefixWithInfoTag() = runComposeUiTest {
setContent {
MyInfoComposable("Important things!", modifier = Modifier.testTag("info"))
}
onNodeWithTag("info").assertTextContains("[INFO]", substring = true)
}
}
在任何方针渠道上运转此测验都将向您显现测验成果(关于如上的错误状况,将帮助您发现并纠正拼写错误!):
iOS 辅佐功用支撑
Compose Multiplatform 现在为 iOS 供给支撑,使残障人士能够与 Compose UI 以与原生 UI 相同的舒适度进行交互:
- 屏幕阅读器和 VoiceOver 能够拜访 Compose Multiplatform UI 的内容。
- Compose Multiplatform UI 在导航和交互上,支撑与原生 UI 相同的手势。
这是由于 Compose Multiplatform 的语义数据会主动映射到 Accessibility Tree 中。您还能够将此数据用于 Accessibility Services,或运用 XCTest 结构进行测验。
有关当时辅佐功用支撑的完结和约束的具体信息,请参阅文档页面。
Fleet 的 @Preview 注解
在 1.6.0 版别中,Compose Multiplatform 引入了常见的 @Preview
注解(之前仅适用于 Android 和 Desktop)。这个注解由 JetBrains Fleet(从 Fleet 1.31 开端支撑)。将 @Preview
增加到你的 @Composable
函数中,你就能经过边栏图标翻开预览:
在由 Kotlin Multiplatform 向导 生成的项目中试用吧!
现在,Fleet 仅支撑对没有参数的 @Composable
函数运用 @Preview
注解。要运用这个常见的注解,请将实验性的 compose.components.uiToolingPreview
库增加到你的依靠项中(而不是用于 Desktop 和 Android 的 compose.uiTooling
)。
Popus、Dialog 和下拉菜单可显现于渠道 View 之外
当将 Compose Multiplatform 与 SwiftUI 混合运用时,您或许只想让屏幕上的一些小 Widgets 运用 Compose 烘托。从版别 1.6 开端,在这些场景中创立 Dialog
、Popup
或 Dropdown
的 Composable 能够扩展超出单个 Widget 的鸿沟,乃至能够扩展到整个屏幕!
现在,这也适用于桌面方针,尽管现在是作为一个实验性特性。
请注意,弹出窗口和对话框依然无法制作超出其自身鸿沟的任何内容(例如顶层容器的阴影)。
iOS(安稳版)
在 iOS 上,默许状况下该功用是激活的。要切换回旧的行为,请将 platformLayers
参数设置为 false
:
ComposeUIViewController(
configure = {
platformLayers = false
}
) {
// 您的 Compose 代码
}
桌面(实验性)
要在桌面上运用此功用,请设置 compose.layers.type
体系特点。支撑的值:
-
WINDOW
,用于将Popup
和Dialog
组件创立为独立的无装饰窗口。 -
COMPONENT
,将Popup
或Dialog
创立为同一窗口中的独自 Swing 组件。它仅在离屏烘托时有用,当compose.swing.render.on.graphics
设置为true
时(请参阅 1.5.0 Compose Multiplatform 发行阐明的 增强的 Swing 互操作 部分)。请注意,离屏烘托仅适用于ComposePanel
组件,而不适用于完好的窗口运用程序。
以下是运用 COMPONENT
特点的示例代码:
@OptIn(ExperimentalComposeUiApi::class)
fun main() = SwingUtilities.invokeLater {
System.setProperty("compose.swing.render.on.graphics", "true")
System.setProperty("compose.layers.type", "COMPONENT")
val window = JFrame()
window.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
val contentPane = JLayeredPane()
contentPane.layout = null
val composePanel = ComposePanel()
composePanel.setBounds(200, 200, 200, 200)
composePanel.setContent {
ComposeContent()
}
composePanel.windowContainer = contentPane // Use the full window for dialogs
contentPane.add(composePanel)
window.contentPane.add(contentPane)
window.setSize(800, 600)
window.isVisible = true
}
@Composable
fun ComposeContent() {
Box(Modifier.fillMaxSize().background(Color.Green)) {
Dialog(onDismissRequest = {}) {
Box(Modifier.size(100.dp).background(Color.Yellow))
}
}
}
不管父 ComposePanel
(绿色)的鸿沟怎么,Dialog
(黄色)都会彻底制作:
来自 Compose 1.6 和 Material 3 的改变
Jetpack Compose 1.6.1
将最新版别的 Jetpack Compose 合并到项目中对一切渠道的功用都有积极影响。概况请参阅Android 开发者博客上的公告(或我的翻译: Jetpack Compose 1.6 上新:滚动功用提升 20%! )。
此版别的其他明显更新包含:
- 默许字体填充的更改仅在 Android 方针中收效。但是,请保证考虑到这种更改的副作用。
- 鼠标挑选在 Compose Multiplatform 中的其他方针已经得到支撑。从 1.6.0 开端,这也适用于 Android。
没有移植到 Compose Multiplatform 的 Jetpack Compose 功用:
- BasicTextField2
- 支撑非线性字体缩放
- MultiParagraph.fillBoundingBoxes
-
跨渠道拖放。现在仅限于 Android 上。在 Desktop 端,您能够运用现有的
Modifier.onExternalDrag
API。
JetBrains 团队正在尽力在未来版别的 Compose Multiplatform 中归入这些功用。
Compose Material 3 1.2.0
发布亮点:
-
新的实验性组件
Segmented Button
,支撑单选和多选。
单选
![segmented button with two items selected](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/543245cf6d76408c95262c1f229fb2e4~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=783&h=209&s=17250&e=png&b=f9f9f9)
多选
-
扩展的色彩集合,供给更多的 Surface 选项,以便更容易地杰出显现 UI 中的信息。
-
实施阐明:
ColorScheme
对象现在是不可变的。假如您的代码当时直接修正ColorScheme
中的色彩,请保证运用 copy 方法来更改色彩。 -
现在不再有单一的 Surface 值,而是有更多的表面 Surface 选项和Surface Container,以便更灵敏地办理色彩。
-
有关 Material 3 改变的更多具体信息,请参阅 Material Design 博客上的发布文章。
其他全渠道改变
文本装饰线款式支撑(iOS、桌面、Web)
Compose Multiplatform 现在答应运用 PlatformTextStyle
类设置文本的下划线款式。
这个类不在 common source 中可用,需要在渠道特定的代码中运用。
设置点线下划线款式的示例:
Text(
"Hello, Compose",
style = TextStyle(
textDecoration = TextDecoration.Underline,
platformStyle = PlatformTextStyle (
textDecorationLineStyle = TextDecorationLineStyle.Dotted
)
)
)
您能够运用实线、双宽实线、点线、虚线和波涛线款式。请在源代码中查看一切可用选项。
拜访体系装置的字体(iOS、桌面、Web)
您现在能够从 Compose Multiplatform 运用程序中拜访体系装置的字体:运用 SystemFont
类加载具有适当字体款式和字体权重的字体:
import androidx.compose.ui.text.platform.SystemFont
FontFamily(SystemFont("Menlo", weight = 700))
FontFamily(SystemFont("Times New Roman", FontWeight.Bold))
FontFamily(SystemFont("Webdings"))
在桌面上,您能够运用 FontFamily
函数经过指定字体系列称号来加载一切或许的字体款式(参见代码示例):
FontFamily("Menlo")
iOS
更改 Composable View 的不通明度
ComposeUIViewController
类现在有一个更多的装备选项,能够将视图的布景不通明度更改为通明。
通明布景会对功用产生负面影响,由于它会导致额定的 Blending 过程。
val appController = ComposeUIViewController(configure = {
this.opaque = false
}) {
App()
}
通明布景能够帮助您完结的示例:
经过双击和三击在 SelectionContainer 中挑选文本
曾经,Compose Multiplatform for iOS 仅答运用户在文本输入框中运用屡次点击来挑选文本。现在,双击和三击手势也适用于挑选在 SelectionContainer
内部的 Text
组件中的文本。
与 UIViewController 的互操作
一些未完结为 UIView
的原生 API,例如 UITabBarController
或 UINavigationController
,曾经无法运用现有的互操作机制嵌入到 Compose Multiplatform UI 中。
现在,Compose Multiplatform 完结了 UIKitViewController
函数,答应您在 Compose UI 中嵌入原生 iOS View Controller。
TextField 中经过长按/单击完结相似原生的光标行为
Compose Multiplatform 现在更接近于文本字段中的原生 iOS 光标行为:
- 单击文本字段后,光标方位愈加准确。
- 在文本字段中长按并拖动会导致光标移动,而不是像在 Android 上那样进入挑选形式。
桌面端
实验性支撑:改善的互操作
曩昔,运用 SwingPanel
包装器完结的互操作视图一直是矩形的,并且一直在任何 Compose Multiplatform 组件的前景中置顶。这使得任何弹出元素(下拉菜单、Toast 通知)都很难运用。经过新的完结,此问题得到处理,您现在能够在以下用例中依靠 Swing:
- 裁剪。您不再遭到矩形形状的约束:裁剪和阴影修饰符现在与 SwingPanel 正确工作。
// 启用实验性混合的必要标志
System.setProperty("compose.interop.blending", "true")
SwingPanel(
modifier = Modifier.clip(RoundedCornerShape(6.dp))
//...
)
左侧是无此功用前 `JButton` 被裁剪后的体现,右侧是开启后的:
- 重叠。能够在
SwingPanel
顶部制作任何 Compose Multiplatform 内容,并像平常一样与其进行交互。这里,”Snackbar” 在带有可点击的 OK 按钮的 Swing 面板上方:
您可在此 PR 的描绘中查看已知约束和更多具体信息。
Web
Kotlin/Wasm 构件在结构的安稳版别中可用
安稳版别的 Compose Multiplatform 现在支撑 Kotlin/Wasm 方针。在切换到 1.6.0 后,您不用在依靠列表中指定特定的 dev-wasm
版别的 compose-ui
库。
要构 建带有 Wasm 方针的 Compose Multiplatform 项目,您需要拥有 Kotlin 1.9.22 及更高版别。
已知问题:短少依靠项
默许的项目装备或许会短少几个库:
-
org.jetbrains.compose.annotation-internal:annotation
或org.jetbrains.compose.collection-internal:collection
假如某个库依靠于不与 1.6.0 兼容的 Compose Multiplatform 1.6.0-beta02,则或许会短少这些库。要找出是哪个库,运转以下命令(将
shared
替换为您的主模块称号):./gradlew shared:dependencies
您可经过将该库降级为依靠于 Compose Multiplatform 1.5.12 的版别,或要求库的作者将其晋级为 Compose Multiplatform 1.6.0 来处理此问题。
-
androidx.annotation:annotation:...
或androidx.collection:collection:...
Compose Multiplatform 1.6.0 依靠于仅在 Google Maven 存储库中可用的 collection 和 annotation 库。
要使此存储库对项目可用,请将以下行增加到模块的
build.gradle.kts
文件中:
repositories {
//...
google()
}
其他更改
- Compose Multiplatform release notes on GitHub 包含了 1.6 的翔实 PR 列表
搬迁
翻译完上面的内容后,我也自己开端了搬迁,从 1.5.11 搬迁至 1.6.0,Material 3 也搬迁至 1.2.1:
-
RichTooltip
API 产生很大改变:…/TooltipSamples.kt Gerrit Code Review (googlesource.com) -
SwipeToDismiss
被废弃,大量 API 也改变,新用法参阅:…/SwipeToDismissDemo.kt Gerrit Code Review (googlesource.com) -
原本自带的
painterResource(path: String)
不见了,自己简略完结了一个:
@Composable
fun painterDrawableRes(name: String, suffix: String = "png"): Painter {
val res = if (name.contains('.')) name else "$name.$suffix"
return painterResource("drawable/$res")
}
@OptIn(ExperimentalResourceApi::class)
@Composable
fun painterResource(resource: String): Painter {
return BitmapPainter(imageResource(DrawableResource(resource)))
}
具体改变见:bump Compose Multiplatform to 1.6.0 FunnySaltyFish/Transtation-KMP@d8ddb28 (github.com)