一个基于 Jetpack Compose + Material 3 的 Android 入门示例项目,以「待办清单(Todo List)」为场景,演示 Compose 声明式 UI 的核心用法。
功能特性
- 添加待办事项
- 勾选标记完成(已完成项显示删除线)
- 删除待办事项
- 实时进度统计(已完成 / 总数)
- 空状态占位
- 支持 Material You 动态取色(Android 12+)
- 支持深色模式
架构
采用 单向数据流(UDF) + MVVM:
事件(用户操作) 状态(StateFlow)
TodoApp (Composable) ─────────────► TodoViewModel ─────────────► TodoApp (Composable)
- collectAsState 订阅 - 持有唯一可信源 uiState - 重组渲染
- onInputChange / addTodo - 不可变 TodoUiState
- toggleTodo / deleteTodo - viewModelScope 内可做异步
- 状态层 :
TodoViewModel持有StateFlow<TodoUiState>,所有字段val,变更只产生新实例。 - 事件层 :Composable 通过
viewModel::addTodo、viewModel::toggleTodo(id)等纯函数向 VM 发事件。 - View 层 :Composable 不再持有列表/输入框状态,仅
collectAsState()订阅 + 渲染,天然支持配置变更后状态保留。
演示的 Compose 核心概念
| 主题 | 涉及 API |
|---|---|
| 状态管理 | ViewModel + StateFlow / MutableStateFlow、collectAsState + by 委托 |
| 列表渲染 | LazyColumn、items + 稳定 key |
| 布局 | Scaffold、TopAppBar、Row/Column、weight、Spacer、Box |
| 交互组件 | Checkbox、OutlinedTextField、FilledIconButton、IconButton |
| Material 3 | MaterialTheme、动态取色、Surface、darkColorScheme/lightColorScheme |
| 组件化 | 抽取 StatCard、TodoRow、EmptyState 等可复用 Composable |
技术栈
- Kotlin 1.9.23
- Android Gradle Plugin 8.3.2
- Jetpack Compose BOM 2024.04.00
- Material 3
- Lifecycle / ViewModel / StateFlow(
lifecycle-viewmodel-compose) - compileSdk 34 / minSdk 26 / targetSdk 34
- 构建脚本:Kotlin DSL + 版本目录(
gradle/libs.versions.toml)
项目结构
android_project/
├── app/
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/main/
│ ├── AndroidManifest.xml
│ ├── java/com/example/composedemo/
│ │ ├── MainActivity.kt # 入口 Activity
│ │ ├── TodoApp.kt # Composable View 层
│ │ ├── TodoViewModel.kt # ViewModel + StateFlow(状态/事件)
│ │ └── ui/theme/
│ │ ├── Color.kt # 颜色定义
│ │ ├── Theme.kt # 主题(含动态取色)
│ │ └── Type.kt # 字体样式
│ └── res/
│ ├── drawable/ # adaptive icon 前后景
│ ├── mipmap-anydpi-v26/ # adaptive icon
│ ├── values/ # strings / themes
│ ├── values-night/ # 深色模式 themes
│ └── xml/ # 备份规则
├── gradle/
│ ├── libs.versions.toml # 版本目录
│ └── wrapper/gradle-wrapper.properties
├── build.gradle.kts
├── settings.gradle.kts
└── gradle.properties
运行方式
项目未包含二进制的
gradle-wrapper.jar与gradlew脚本,可通过以下任一方式补全:
方式一:Android Studio(推荐)
- Android Studio →
File→Open选择android_project目录 - 等待 Gradle Sync 完成(Studio 会自动补全 Gradle Wrapper)
- 连接真机或模拟器,点击 Run
方式二:命令行
若本机已安装 Gradle:
cd android_project
gradle wrapper # 生成 gradlew 与 wrapper jar
./gradlew installDebug # 编译并安装到已连接设备
代码导览
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ComposeDemoTheme {
TodoApp()
}
}
}
状态层:TodoViewModel.kt
data class TodoUiState(
val todos: List<TodoItem> = emptyList(),
val inputText: String = "",
val totalCount: Int = 0,
val doneCount: Int = 0
)
class TodoViewModel : ViewModel() {
private val _uiState = MutableStateFlow(TodoUiState())
val uiState: StateFlow<TodoUiState> = _uiState.asStateFlow()
fun addTodo() { /* update { ... } */ }
fun toggleTodo(id: Int) { /* update { ... } */ }
fun deleteTodo(id: Int) { /* update { ... } */ }
}
View 层:TodoApp.kt 订阅状态 + 派发事件:
@Composable
fun TodoApp(viewModel: TodoViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsState()
OutlinedTextField(
value = uiState.inputText,
onValueChange = viewModel::onInputChange
)
LazyColumn {
items(uiState.todos, key = { it.id }) { item ->
TodoRow(
item = item,
onToggle = { viewModel.toggleTodo(item.id) },
onDelete = { viewModel.deleteTodo(item.id) }
)
}
}
}
代码仓库点击跳转:Jetpack_Compose_demo - AtomGit