Reference version

This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

使用 Jetpack Compose 扩展

了解如何创建与 Expo UI 集成的自定义 Jetpack Compose 组件和修饰符

Android
Recommended version:
~57.0.18

本指南介绍如何使用 Expo Modules API 创建可与 Expo UI 集成的自定义 Jetpack Compose 组件和修饰符。

Prerequisites

3 requirements

1.

@expo/ui 已安装

更多信息请参阅 Expo UI 中的 Jetpack Compose 组件。

Terminal
- npx expo install @expo/ui

2.

应用的开发版本

自定义 Expo 模块包含原生代码,Expo Go 无法加载。请为应用创建一个开发版本。

3.

熟悉 Expo Modules API 和 Jetpack Compose 的基础知识

本指南假设你对 Expo Modules API 和 Jetpack Compose 有基本了解。

创建自定义组件

项目设置

1

在项目中创建一个本地 Expo 模块:

Terminal
- npx create-expo-module@latest --local my-ui

2

更新模块的 android/build.gradle,以启用 Jetpack Compose 并依赖 expo-ui。以下代码片段中的注释标出了需要添加到默认脚手架中的行:

modules/my-ui/android/build.gradle
// Pull in the Kotlin Compose compiler plugin classpath. buildscript { repositories { mavenCentral() } dependencies { classpath("org.jetbrains.kotlin.plugin.compose:org.jetbrains.kotlin.plugin.compose.gradle.plugin:${kotlinVersion}") } } apply plugin: 'com.android.library' apply plugin: 'expo-module-gradle-plugin' apply plugin: 'org.jetbrains.kotlin.plugin.compose' // Apply the Compose compiler plugin. // ... group / version android { // ... namespace, defaultConfig // Turn on Jetpack Compose for this module. buildFeatures { compose true } } // Depend on `expo-ui` plus the Compose libraries you use. dependencies { if (findProject(':expo-ui') != null) { implementation project(':expo-ui') } else { implementation 'expo.modules.ui:expo.modules.ui:+' } implementation 'androidx.compose.foundation:foundation-android:1.10.6' implementation 'androidx.compose.ui:ui-android:1.10.6' implementation 'androidx.compose.material3:material3:1.5.0-alpha17' }

创建 Compose 视图

3

创建 Compose 视图。它包含两部分:

  1. Props 数据类:带有 @OptimizedComposeProps 注解,实现 ComposeProps,并包含一个用于 modifiers 属性的 modifiers: ModifierList 字段。
  2. @Composable 内容函数:FunctionalComposableScope 的扩展函数,因此可以调用 ModifierRegistry.applyModifiers(...) 并渲染 Children(...)。
modules/my-ui/android/src/main/java/expo/modules/myui/MyCustomView.kt
package expo.modules.myui import androidx.compose.foundation.layout.Column import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import expo.modules.kotlin.views.ComposeProps import expo.modules.kotlin.views.FunctionalComposableScope import expo.modules.kotlin.views.OptimizedComposeProps import expo.modules.ui.ModifierList import expo.modules.ui.ModifierRegistry import expo.modules.ui.UIComposableScope @OptimizedComposeProps data class MyCustomViewProps( val title: String = "", val modifiers: ModifierList = emptyList() ) : ComposeProps @Composable fun FunctionalComposableScope.MyCustomViewContent(props: MyCustomViewProps) { Column( modifier = ModifierRegistry.applyModifiers( props.modifiers, appContext, composableScope, globalEventDispatcher ) ) { Text(text = props.title, style = MaterialTheme.typography.titleMedium) Children(UIComposableScope()) // Renders React children } }

4

使用 ExpoUIView 在模块中注册视图。这会将 @Composable 内容接入 Expo 模块的视图系统,并使其可供 JavaScript 使用:

modules/my-ui/android/src/main/java/expo/modules/myui/MyUiModule.kt
package expo.modules.myui import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition import expo.modules.ui.ExpoUIView class MyUiModule : Module() { override fun definition() = ModuleDefinition { Name("MyUi") ExpoUIView<MyCustomViewProps>("MyCustomView") { Content { props -> MyCustomViewContent(props) } } } }

5

创建一个封装组件,将修饰符与事件处理连接起来。createViewModifierEventListener 工具可让 clickable 和 onVisibilityChanged 等基于事件的修饰符在自定义视图中正常工作:

modules/my-ui/src/MyCustomView.tsx
import { type PrimitiveBaseProps } from '@expo/ui/jetpack-compose'; import { createViewModifierEventListener } from '@expo/ui/jetpack-compose/modifiers'; import { requireNativeView } from 'expo'; export interface MyCustomViewProps extends PrimitiveBaseProps { title: string; children?: React.ReactNode; } const NativeMyCustomView = requireNativeView<MyCustomViewProps>( 'MyUi', 'MyCustomView' ); export function MyCustomView({ modifiers, ...restProps }: MyCustomViewProps) { return ( <NativeMyCustomView modifiers={modifiers} {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)} {...restProps} /> ); }

6

从模块的入口文件导出组件:

modules/my-ui/index.ts
export { MyCustomView, type MyCustomViewProps, } from './src/MyCustomView';

使用自定义组件

现在,自定义组件可以使用所有内置的 Expo UI 修饰符:

src/app/index.tsx
import { Host, Text } from '@expo/ui/jetpack-compose'; import { background, clip, paddingAll, } from '@expo/ui/jetpack-compose/modifiers'; import { MyCustomView } from '../../modules/my-ui'; export default function Index() { return ( <Host style={{ flex: 1 }}> <MyCustomView title="Hello World" modifiers={[ paddingAll(16), background('#f0f0f0'), clip({ type: 'roundedCorner', radius: 12 }), ]}> <Text>Child content</Text> </MyCustomView> </Host> ); }

创建自定义修饰符

你也可以创建适用于任何 Expo UI 组件的自定义修饰符。

原生修饰符实现

1

将修饰符参数定义为 @OptimizedRecord 数据类。然后编写一个根据这些参数返回 Modifier 的函数:

modules/my-ui/android/src/main/java/expo/modules/myui/CustomBorderModifier.kt
package expo.modules.myui import android.graphics.Color import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.border import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import expo.modules.kotlin.records.Field import expo.modules.kotlin.records.Record import expo.modules.kotlin.types.OptimizedRecord import expo.modules.ui.compose @OptimizedRecord data class CustomBorderParams( @Field val color: Color? = null, @Field val width: Int = 2, @Field val cornerRadius: Int = 0 ) : Record fun customBorderModifier(params: CustomBorderParams): Modifier { return Modifier.border( border = BorderStroke(params.width.dp, params.color.compose), shape = RoundedCornerShape(params.cornerRadius.dp) ) }

compose 是定义在 expo.modules.ui 包中的 android.graphics.Color? Kotlin 扩展属性。通过 import expo.modules.ui.compose 导入后,可以调用 params.color.compose,将从 JS 解析得到的 Android Color 转换为 Compose API(例如 BorderStroke)所需的 androidx.compose.ui.graphics.Color。Expo UI 的内置修饰符也使用了同一个辅助工具。

2

在模块定义中使用 ModifierRegistry 注册修饰符。使用 OnCreate 进行注册,并使用 OnDestroy 取消注册,避免工厂在模块重新加载时发生泄漏:

modules/my-ui/android/src/main/java/expo/modules/myui/MyUiModule.kt
package expo.modules.myui import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition import expo.modules.kotlin.records.recordFromMap import expo.modules.ui.ExpoUIView import expo.modules.ui.ModifierRegistry class MyUiModule : Module() { override fun definition() = ModuleDefinition { Name("MyUi") OnCreate { ModifierRegistry.register("customBorder") { map, _, _, _ -> customBorderModifier(recordFromMap<CustomBorderParams>(map)) } } OnDestroy { ModifierRegistry.unregister("customBorder") } ExpoUIView<MyCustomViewProps>("MyCustomView") { Content { props -> MyCustomViewContent(props) } } } }

register lambda 接收从 JavaScript 发送的原始映射、当前的 ComposableScope(可用于 weight 或 align 等依赖作用域的修饰符)、AppContext 和事件分发器。大多数修饰符只需要 map,并通过 recordFromMap<T>(map) 转换它。

JavaScript 修饰符函数

3

创建一个用于构造修饰符配置的 TypeScript 函数:

modules/my-ui/src/modifiers.ts
import { createModifier } from '@expo/ui/jetpack-compose/modifiers'; import { type ColorValue } from 'react-native'; export const customBorder = (params: { color?: ColorValue; width?: number; cornerRadius?: number; }) => createModifier('customBorder', params);

4

从模块中导出修饰符:

modules/my-ui/index.ts
export { MyCustomView, type MyCustomViewProps, } from './src/MyCustomView'; export { customBorder } from './src/modifiers';

使用自定义修饰符

自定义修饰符适用于任何 Expo UI 组件:

src/app/index.tsx
import { Column, Host, Text } from '@expo/ui/jetpack-compose'; import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; import { customBorder } from '../../modules/my-ui'; export default function Index() { return ( <Host style={{ flex: 1 }}> <Column modifiers={[ paddingAll(20), customBorder({ color: '#FF6B35', width: 3, cornerRadius: 8, }), ]}> <Text>This has a custom border!</Text> </Column> </Host> ); }

后续步骤

现在,自定义组件可以使用内置的修饰符系统。

以下是一些接下来可以尝试的方向:

  • 使用 Expo UI 附带的内置 Jetpack Compose 组件。
  • 为应用特定的样式模式构建自定义修饰符。
  • 封装第三方 Compose 库,以便在 React Native 中使用。
  • 将组件作为 npm 包分享给他人使用。