介绍 在 Android 之 Compose 开发基础 - 应用架构  中介绍了 Jetpack Compose 常用的 MVVM(Model-View-ViewModel)架构  。
在源文件中一般分为 Screen、UiState、ViewModel 三个文件。
对应如下
Screen - View(视图) 
由 Screen 中的 @Composable 函数组成 
负责界面的渲染和用户交互 
通过观察 ViewModel 中的状态( uiState )来观察界面。 
 
 
ViewModel - ViewModel(视图模型) 
持有并管理界面状态 uiState 
处理用户逻辑和用户事件 
使用 StateFlow 、 LiveData 或其他可观察的数据类型,将状态暴漏给视图 
 
 
UiState - Model(模型)  
负责数据管理和业务逻辑,如数据模型、仓库(Repository)、网络请求等。 
数据模型封装界面所需的状态数据,通常为不可变的数据类。 
ViewModel 与 Model 交互,获取或更新数据 
 
 
 
 
特点 
单向数据流 :数据从 ViewModel 流向 Screen,用户交互从 Screen 通过事件回传给 ViewModel。降低了复杂性。 
状态不可变 :使用不可变的 UiState,使状态变化可预测,减少了调试困难。 
关注点分离 :将界面展示、状态管理和业务逻辑分离,提高了代码的可读性和可维护性。 
 
1 2 3 4 5 6 7 8 9 用户交互     ↓ View(Screen)     ↓           ↑     事件       观察     ↓           ↑ ViewModel(持有 uiState)     ↓ 与 Model 交互 
 
使用 设置依赖 build.gradle.kts(Module :app)
1 implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1" ) 
 
或是使用 Version Cataloglibs.versions.toml
1 2 3 4 5 [versions] lifecycleViewModelCompose ="2.8.6" [libraries] androidx-lifecycle-viewmodel-compose  = { group = "androidx.lifecycle" , name = "lifecycle-viewmodel-compose" , version.ref = "lifecycleViewModelCompose"  }
 
build.gradle.kts(Module :app)
1 implementation(libs.androidx.lifecycle.viewmodel.compose) 
 
UiState UiState 是一个数据类,封装了界面展示所需的所有状态信息。它通常是不可变的,以确保状态的一致性和可预测性。
创建数据类 GameUiState.kt
1 2 3 4 5 6 7 data  class  GameUiState (    val  currentScrambledWord: String = "" ,     val  isGuessedWordWrong: Boolean  = false ,     val  score: Int  = 0 ,     val  currentWordCount: Int  = 1 ,     val  isGameOver: Boolean  = false  ) 
 
ViewModel ViewModel 负责处理业务逻辑和状态管理。它持有 UiState,并通过 StateFlow、LiveData 或 MutableState 将状态暴露给界面层。
创建 GameViewModel.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  androidx.compose.runtime.Composableimport  androidx.compose.runtime.collectAsStateimport  androidx.lifecycle.viewmodel.compose.viewModel@Composable class  GameViewModel  : ViewModel () {    private  val  _uiState = MutableStateFlow(GameUiState())     val  uiState: StateFlow<GameUiState> = _uiState.asStateFlow()     fun  updateUiState ()  {                           _uiState.update { currentState ->             currentState.copy(                              )         }     } } 
 
_uiState:私有的可变状态。uiState:公开的只读状态,供界面层观察。updateUiState:处理来自界面层的事件,更新 uiState。
Screen Screen 是一个或多个 @Composable 函数,调用 ViewModel 负责根据传入的 uiState 渲染界面,并通过回调或事件将用户交互传递给 ViewModel。 
创建 GameScreen.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Composable fun  GameScreen (     gameViewModel: GameViewModel  = viewModel() ){     val  gameUiState by  gameViewModel.uiState.collectAsState()                     Button(         onClick = { gameViewModel.updateUiState() }               ){         Text("Button" )     } } 
 
参考示例 By ChatGPT 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 sealed  class  LoginEvent  {    data  class  OnUsernameChanged (val  username: String) : LoginEvent()     data  class  OnPasswordChanged (val  password: String) : LoginEvent()     object  OnLoginClicked : LoginEvent() } data  class  LoginUiState (    val  username: String = "" ,     val  password: String = "" ,     val  isLoading: Boolean  = false ,     val  errorMessage: String? = null  ) class  LoginViewModel  : ViewModel () {    private  val  _uiState = MutableStateFlow(LoginUiState())     val  uiState: StateFlow<LoginUiState> = _uiState.asStateFlow()     fun  onEvent (event: LoginEvent )   {         when  (event) {             is  LoginEvent.OnUsernameChanged -> {                 _uiState.update { it.copy(username = event.username) }             }             is  LoginEvent.OnPasswordChanged -> {                 _uiState.update { it.copy(password = event.password) }             }             is  LoginEvent.OnLoginClicked -> {                 login()             }         }     }     private  fun  login ()   {         viewModelScope.launch {             _uiState.update { it.copy(isLoading = true , errorMessage = null ) }                          delay(2000 )             val  success = _uiState.value.username == "user"  && _uiState.value.password == "pass"              if  (success) {                 _uiState.update { it.copy(isLoading = false ) }                              } else  {                 _uiState.update { it.copy(isLoading = false , errorMessage = "登录失败" ) }             }         }     } } @Composable fun  LoginScreen (uiState: LoginUiState , onEvent: (LoginEvent ) -> Unit )   {    Column {         TextField(             value = uiState.username,             onValueChange = { onEvent(LoginEvent.OnUsernameChanged(it)) },             label = { Text("用户名" ) }         )         TextField(             value = uiState.password,             onValueChange = { onEvent(LoginEvent.OnPasswordChanged(it)) },             label = { Text("密码" ) },             visualTransformation = PasswordVisualTransformation()         )         if  (uiState.errorMessage != null ) {             Text(text = uiState.errorMessage, color = Color.Red)         }         Button(             onClick = { onEvent(LoginEvent.OnLoginClicked) },             enabled = !uiState.isLoading         ) {             if  (uiState.isLoading) {                 CircularProgressIndicator()             } else  {                 Text("登录" )             }         }     } } @Composable fun  LoginScreenWrapper (viewModel: LoginViewModel  = viewModel()  ) {    val  uiState by  viewModel.uiState.collectAsState()     LoginScreen(         uiState = uiState,         onEvent = viewModel::onEvent     ) }