Skip to content

View 规范

页面视图层遵循“Route -> Screen -> Content”三级结构。该规范来源于项目内部的实践总结,现整理为对外文档,便于编写 Compose 页面时快速套用。

1. Route 层

  • 职责:作为页面入口,注入 ViewModel 并收集所有 StateFlow
  • 命名${PageName}Route,需带完整注释(描述 + 参数)。
  • 实现要点
    • 只能通过 collectAsState() 获取状态,禁止直接访问 ViewModel 字段。
    • 负责将所有回调(onBackClickonRetry、弹窗切换等)传递给下一层。
kotlin
/**
 * 商品详情路由入口
 *
 * @param viewModel 页面 ViewModel
 */
@Composable
internal fun GoodsDetailRoute(
    viewModel: GoodsDetailViewModel = hiltViewModel()
) {
    // 页面 UI 状态
    val uiState by viewModel.uiState.collectAsState()
    // 规格弹窗状态
    val specModalVisible by viewModel.specModalVisible.collectAsState()
    // ...更多状态

    GoodsDetailScreen(
        uiState = uiState,
        specModalVisible = specModalVisible,
        onBackClick = viewModel::navigateBack,
        onRetry = viewModel::retryRequest,
        onShowSpecModal = viewModel::showSpecModal,
        // ...更多回调
    )
}

2. Screen 层

  • 职责:负责页面骨架、Loading/Empty/Error 切换,以及 ScaffoldBaseNetWorkView 等容器。
  • 命名${PageName}Screen
  • 参数:路由层传下来的所有状态、回调,类型应尽量可空或提供默认值,以便 @Preview 使用。
  • 实现要点
    • 仅在此层处理 Loading/Retry/空页面。
    • 成功态通过 content lambda 将数据传给 Content 层。
kotlin
/**
 * 商品详情界面
 *
 * @param uiState 商品详情 UI 状态
 * @param specModalVisible 规格弹窗是否显示
 * @param onRetry 重试请求
 * @param onShowSpecModal 显示规格弹窗
 */
@Composable
internal fun GoodsDetailScreen(
    uiState: BaseNetWorkUiState<GoodsDetail>,
    specModalVisible: Boolean,
    onRetry: () -> Unit,
    onShowSpecModal: () -> Unit,
    // ...
) {
    BaseNetWorkView(
        uiState = uiState,
        onRetry = onRetry
    ) { detail ->
        GoodsDetailContent(
            data = detail,
            specModalVisible = specModalVisible,
            onShowSpecModal = onShowSpecModal,
            // ...
        )
    }
}

3. Content 层

  • 职责:展示成功态 UI,不直接依赖 ViewModel。
  • 命名${PageName}Content${PageName}ContentView
  • 参数:都为非空具体类型(如 GoodsDetailBoolean),确保可独立预览。
kotlin
/**
 * 商品详情成功态
 *
 * @param data 商品详情数据
 * @param specModalVisible 规格弹窗状态
 * @param onShowSpecModal 显示规格弹窗
 */
@Composable
private fun GoodsDetailContent(
    data: GoodsDetail,
    specModalVisible: Boolean,
    onShowSpecModal: () -> Unit,
    // ...
) {
    // 只负责绘制 UI,可自由拆分子组件
}

4. 预览规范

  • 每个 Screen 需提供浅色/深色两套 @Preview,注入 BaseNetWorkUiState.Success(mockData())
  • Content 层若依赖复杂数据,建议提供 mockData() 方法或 preview 扩展,方便 Preview 与单元测试复用。
kotlin
/**
 * 商品详情预览(浅色)
 */
@Preview(showBackground = true)
@Composable
internal fun GoodsDetailScreenPreview() {
    AppTheme {
        GoodsDetailScreen(
            uiState = BaseNetWorkUiState.Success(mockGoodsDetail()),
            specModalVisible = false,
            onRetry = {},
            onShowSpecModal = {}
        )
    }
}

5. 注释与命名

  • 所有 Route/Screen/Content/Preview 须保持 KDoc(描述与参数说明)。
  • 文件名使用 ${PageName}Screen.kt,Route/Screen/Content/Preview 均放在同一文件中,便于查阅。
  • 局部组件若可复用,移动到模块内的 component/ 目录。

6. 资源与文案

  • 避免硬编码文案,统一放在 res/values/strings.xml(项目全局)中。若未来拆分多模块,再单独维护。
  • 颜色、尺寸、圆角使用 core/designsystem/core/ui 的常量,以保持全局视觉一致。

7. 校验清单

  • [ ] Route 只负责收集状态与转发回调,不包含业务逻辑。
  • [ ] Screen 处理 Loading/Error/Retry,Content 只绘制成功态。
  • [ ] 所有 Compose 函数具备注释,Preview 至少包含浅/深色两套。
  • [ ] UI 不直接调用 ViewModel,所有交互通过回调完成。

遵循上述规范即可确保 Feature 层页面结构清晰,与 ViewModel 契约稳定,也让模板自动生成的代码直接可用。