20240218
记账本App主页页面的绘制
记账本App的主页界面绘制
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ExpenseTrackerApp(
appViewModel: ExpenseTrackerViewModel = viewModel()
) {
val appUiState by appViewModel.uiState.collectAsState()
Box(modifier = Modifier.safeContentPadding()) {
Scaffold(
topBar = {
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary,
),
title = {
Text("Expense Tracker")
}
)
},
floatingActionButton = {
ExtendedFloatingActionButton(
onClick = { appUiState.showModalBottomSheet.value = true },
icon = { Icon(Icons.Filled.Add, "Add New Expense Record") },
text = { Text(text = "Add New") },
)
}
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
ExpenseRecordList(records = appUiState.expenseRecords)
if (appUiState.showModalBottomSheet.value) {
ModalBottomSheet(
onDismissRequest = { appUiState.showModalBottomSheet.value = false },
modifier = Modifier.navigationBarsPadding(),
) {
ExpenseRecordInputSheet(appViewModel)
}
}
}
}
}
}
@Composable
fun ExpenseRecordList(records: List<ExpenseRecord>) {
LazyColumn {
items(records) { record ->
ExpenseRecordContent(record)
}
}
}
@Composable
fun ExpenseRecordContent(record: ExpenseRecord) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
Column(modifier = Modifier.padding(10.dp, 10.dp)) {
Text(
text = record.name,
style = MaterialTheme.typography.titleSmall
)
Text(
text = record.money.toString(),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.End,
style = MaterialTheme.typography.displayMedium
)
Text(
text = record.time.toString(),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.End,
style = MaterialTheme.typography.labelSmall
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ExpenseRecordInputSheet(viewModel: ExpenseTrackerViewModel) {
val inputName = viewModel.inputName
val inputMoney = viewModel.inputMoney
Column(modifier = Modifier.padding(20.dp)) {
TextField(
value = inputName.value,
onValueChange = { inputName.value = it },
label = { Text("Name") },
modifier = Modifier.fillMaxWidth().align(Alignment.CenterHorizontally).padding(10.dp)
)
TextField(
value = inputMoney.value,
onValueChange = { inputMoney.value = it },
label = { Text("Money") },
modifier = Modifier.fillMaxWidth().align(Alignment.CenterHorizontally).padding(10.dp)
//todo add filter to only allow numbers
)
Button(
onClick = { viewModel.submitData() },
modifier = Modifier.fillMaxWidth().align(Alignment.CenterHorizontally).padding(10.dp)
) {
Text("Submit")
}
}
}
在这个例子中,我们使用了Scaffold来构建整个页面的布局,Scaffold是一个Material组件,它提供了一个标准的布局,包括TopAppBar、BottomAppBar、FloatingActionButton等,我们可以通过Scaffold的参数来设置这些组件的属性。在这个例子中,我们使用了TopAppBar和FloatingActionButton,并且通过ExtendedFloatingActionButton来设置FloatingActionButton的样式。
在Scaffold的content中,我们使用了Column来垂直排列两个组件,一个是ExpenseRecordList,另一个是ModalBottomSheet。ExpenseRecordList是一个LazyColumn,它用来显示所有的记录,ModalBottomSheet是一个模态的底部表单,用来输入新的记录。
在ExpenseRecordInputSheet中,我们使用了TextField来输入记录的名字和金额,使用Button来提交数据。
这个例子中,我们使用了ViewModel来管理数据,ExpenseTrackerViewModel是一个ViewModel,它包含了所有的数据和状态,我们通过viewModel函数来获取ViewModel的实例。
在ExpenseTrackerViewModel中,我们使用了State来管理数据,State是一个可以被观察的数据,当数据发生变化时,State会通知所有的观察者。我们使用collectAsState来观察State的变化,当State发生变化时,collectAsState会重新计算Composable。
在ExpenseTrackerViewModel中,我们使用了MutableState来管理用户的输入,MutableState是一个可以被修改的数据,我们可以通过value来获取MutableState的值,通过value = newValue来修改MutableState的值。
在ExpenseTrackerViewModel中,我们使用了MutableStateFlow来管理数据的流,MutableStateFlow是一个可以被观察的数据流,当数据流发生变化时,MutableStateFlow会通知所有的观察者。我们使用collect来观察MutableStateFlow的变化,当MutableStateFlow发生变化时,collect会重新计算Composable。

浙公网安备 33010602011771号