1-开发实战
fmtjava/Compose_Eyepetizer: 一款基于 Jetpack Compose 实现的精美仿开眼视频App(提供Kotlin、Flutter、React Native、小程序版本 😁 )]( https://github.com/fmtjava/Compose_Eyepetizer)
navController
1 | |
popUpTo (navController. graph. startDestinationId)
作用:导航时清除回退栈到指定目的地
效果:清除到起始目的地之间的所有页面,避免回退栈无限增长
saveState = true
作用:保存被清除页面的状态
效果:当用户返回时,页面状态得以保留
launchSingleTop = true
作用:如果目标页面已在栈顶,则不创建新实例
效果:避免重复创建相同页面
restoreState = true
作用:恢复目标页面之前保存的状态
效果:提升用户体验,保留页面之前的交互状态
val navController = rememberNavController()
作用:创建并记住一个 NavHostController 实例,用于管理应用内的导航
原理:rememberNavController () 是一个可组合函数,它确保在重组时返回相同的 NavHostController 实例
用途:通过这个控制器可以执行导航操作,如 navigate ()、popBackStack () 等
变量写法
StateFlow
- 状态封装原则
使用 private val _smsState 作为内部可变状态
使用 val smsState 作为对外暴露的不可变状态流
遵循了状态封装的最佳实践,防止外部直接修改状态
StateFlow 特性
MutableStateFlow<SmsState> 创建了一个可变的状态流
asStateFlow() 将可变流转换为只读流暴露给外部状态管理优势
单向数据流:外部只能观察状态,不能直接修改
线程安全:协程 Flow 保证了状态更新的线程安全性
生命周期感知:配合 Compose 或 Lifecycle 可以自动处理订阅生命周期使用场景
在 SmsCodePage 中可以通过.collect或.collectAsState来观察 smsState 的变化,根据不同的状态(Loading、Success、Error、Idle)来更新 UI 显示。
1 | |
自动重组:当 Flow 发射新值时,Compose 会触发 UI 重组。
生命周期感知:在 @Composable 环境中使用时,会自动跟随 Lifecycle 取消订阅,避免内存泄漏。
| 对比项 | collect () | collectAsState () |
|---|---|---|
| 使用环境 | 任意协程环境 | 仅 Compose |
| 返回类型 | 无返回值 (suspend 函数) | State |
| 是否触发重组 | 否❌ | 是 |
| 是否自动处理生命周期 | 否,需要手动管理✅ | Compose 自动管理 |
| 常见场景 | ViewModel、Repository、后台逻辑 | Compose UI 层 |
collectAsState
是 Jetpack Compose 中常用的一个扩展函数,用来把 Flow/StateFlow 转换成 Compose 可感知的 State<T>,从而让 UI 自动响应数据变化。
自动重组:当 Flow 发射新值时,Compose 会触发 UI 重组。
生命周期感知:在 @Composable 环境中使用时,会自动跟随 Lifecycle 取消订阅,避免内存泄漏
mutableStateOf (不推荐)
1 | |
1 | |
问题
- viewModel 的currentStep 变化 page 的currentStep 也会变化吗,没看到LaunchedEffect(currentStep) { 也会重组吗
会的——只要 currentStep 在 ViewModel 里是 mutableStateOf(或 StateFlow 转成 State),页面里读取到了这个 state,它变更时就会触发重组。不需要 LaunchedEffect(currentStep) 来驱动 UI 更新。
- 为什么不需要 LaunchedEffect
Compose 会自动跟踪在组合阶段被读取的 State(如 mutableStateOf)。
当这个 State 的值变化时,读取它的 Composable 会自动 recompose。
LaunchedEffect 用于副作用(如发起请求、导航、动画),不是用来让 UI 监听 state 的。
若你用的是 StateFlow,同理,在 VM 暴露 StateFlow<Int>,在 UI 用 collectAsState() 即可
- val accountInfoBean by viewModel. accountInfoBean
会的。只要 viewModel. accountInfoBean 是一个 mutableStateOf(或 State<T>),那这行代码就已经在 Compose 中建立了订阅关系。当 ViewModel 中的值变化时,UI 会自动重组。
小结
✅ 读 mutableStateOf/collectAsState() → 值变化自动重组
❌ 不需要 LaunchedEffect(currentStep)
✅ 仅在需要“做一次事”(如用 nextStep 初始化、发请求)时用 LaunchedEffect(…)
liveData
(1)State<T> 是 Compose 的“原生”状态类型
- Compose 的
@Composable函数天然监听State<T>的变化并触发重组。 LiveData需要通过observeAsState()转换为State才能在 Compose 中使用,多了一层转换。- 直接使用
mutableStateOf更简洁、高效,无需依赖 Android 生命周期组件。
(2)LiveData 是为传统 View 系统设计的
LiveData的核心优势是 生命周期感知(自动在onStop时停止通知),防止内存泄漏。- 但在 Compose 中,UI 的生命周期由 Compose 自己管理(通过
DisposableEffect、LaunchedEffect等),不再依赖LifecycleOwner。 - 因此
LiveData的生命周期感知优势在 Compose 中 变得冗余甚至多余。
(3)StateFlow / SharedFlow 更适合现代 Compose 架构
修改传入的值、rememberSaveable
1 | |
❗不要用 remember 包住;保证每次点击都拿到“最新的权限/隐私状态”
智能重组的坑
Compose 的重组是 “按读取字段” 触发的,不是“按对象引用”。
举个例子
1 | |
🔥 即使
orderStateBean.value = newBean(整个对象变了),只要title和hasUnread的值没变,HomePageContent就不会重组!
生命周期
1 | |
好图片

标准代码结构
UI 与逻辑分离 + 只对纯状态组件预览
将 UI 拆分为两个函数:
SmsCodePage:负责和 ViewModel、NavController、状态管理交互(不预览)SmsCodeContent:只接收 UI 状态(如 phone、otpValue、loading、error 等),负责渲染(可预览)
给
SmsCodeContent添加@Preview,让它能独立展示各种 UI 状态。
api用法
any
true:如果集合中 至少有一个 元素满足条件
false:如果 没有任何 元素满足条件
val showDisburseFailedDialog = remember(ordersList) {
mutableStateOf(ordersList.any { it.checkStatus == 6 })
}
//tood 如果ordersList里 有OrderListItem 的 checkStatus 为 6是 显示showDisburseFailedDialog
takeIf
ocr. idPhotoId.takeIf { it != null } ?: oldIdentity?. idPhotoId
满足是前面,不满足是后面
只加载一次
rememberSaveable
1 | |
图标
尺寸
mdpi 48x48
hdpi 72x72
xhdpi 96x96
xxhdpi 144x144
xxxhdpi 192x192
COMPOSE不支持.9图
去掉点击效果
1 | |
本地图片做背景
1 | |
坑
- Data class改成class 可以混淆,要不然 toString 就又能看到混淆前的数据
函数要加()
IconButton( onClick = { onBackClick()
//onBackClick 调不了 },
先后顺序
1 | |
常用组件
知识点
weight 用于设置子元素的权重,权重越大,占据的空间就越大
1 | |
thickness 分割线的厚度
1 | |
这是一个小圆点
1 | |
背景本地切图
1 | |
渐变
1 | |
Button
1 | |
Text
1 | |
lineHeight = 12.sp, 行高
Image
1 | |
ViewPager、HorizontalPager
//ViewPager2, 通过将此状态对象保存在组件中,可以确保当组件重新合成时,分页状态不会丢失。
1 | |
HorizontalPager 是一种用于构建横向滚动页面的组件。它允许您在应用程序中创建水平滑动的页面布局,类似于 ViewPager 或 RecyclerView。
1 | |
padding
在 Compose 中,确实没有 margin 修饰符,只有 padding 修饰符。如果您想在 Text 组件周围创建间距,可以使用 padding 修饰符来实现类似的效果。在您提供的示例代码中,Modifier.padding (top = 3. dp) 将在 Text 组件的顶部添加3dp 的内边距,从而创建了与 margin 类似的效果。
1 | |
或者:
1 | |
zIndex
zIndex 是指定视图的层级顺序的属性。它控制了视图在屏幕上的显示顺序。具有较高 zIndex 值的视图将显示在具有较低 zIndex 值的视图之上。
默认情况下,视图的 zIndex 值为0。如果设置一个较大的正值,则视图将显示在其他视图的上方。如果设置一个较小的负值,则视图将显示在其他视图的下方。当两个视图的 zIndex 相同时,它们将按照它们在布局文件中的顺序进行绘制。
通过调整视图的 zIndex 属性,您可以控制视图的叠加顺序,从而达到覆盖或隐藏其他视图的效果。
1 | |
evnetBus
- 定义一个 在 Activity 范围内共享的 ViewModel,存放你要广播的事件。
- 各个页面通过
viewModel()/hiltViewModel()获取同一个实例,直接订阅状态。
1 | |
通常不需要你手动取消监听。
原因有两个:
collect的生命周期是绑定在 Composable 的LaunchedEffect或DisposableEffect上的- 当 Composable 离开界面时,它的协程会自动取消,Flow 的收集也会结束。
- 所以不会像传统 EventBus 一样出现「忘记 unregister 导致泄漏」的问题。
ViewModel 的作用域受 Activity/Navigation 控制
- Activity 销毁时,ViewModel 也会销毁,内部的 Flow 也不再推送事件。