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:
~58.0.1

本指南介绍如何使用 Expo Modules API 创建自定义 Jetpack Compose 组件和修饰符,并将其集成到 Expo UI 中。

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: ModifierList 字段,用于 modifiers 属性。
  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, _, converterContext, _ -> customBorderModifier(recordFromMap<CustomBorderParams>(map, converterContext)) } } OnDestroy { ModifierRegistry.unregister("customBorder") } ExpoUIView<MyCustomViewProps>("MyCustomView") { Content { props -> MyCustomViewContent(props) } } } }

register lambda 会接收从 JavaScript 传来的原始映射、当前的 ComposableScope(可用于 weight 或 align 等依赖作用域的修饰符)、ConverterContext 和事件分发器。大多数修饰符只需要映射和 ConverterContext,并将它们传递给 recordFromMap<T>(map, converterContext)。

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 包分享给其他人使用。