...
This commit is contained in:
parent
7d03cc78b1
commit
a320dcde02
@ -766,7 +766,7 @@ object AutoTradingManager {
|
||||
lastForceCheckMinute = currentMinute // 실행 완료 기록
|
||||
}
|
||||
}
|
||||
else if((now.hour == 8 || now.hour == 16 || now.hour == 17) && (currentMinute % 10 == 1) || (currentMinute % 10 == 6)) {
|
||||
else if((now.hour == 8 || now.hour == 16 || now.hour == 17 || now.hour == 18 || now.hour == 19) && (currentMinute % 10 == 1) || (currentMinute % 10 == 6)) {
|
||||
if (lastForceCheckMinute != currentMinute) {
|
||||
TradingLogStore.addAnalyzer(
|
||||
" - ",
|
||||
@ -774,9 +774,9 @@ object AutoTradingManager {
|
||||
"⏰ [강제 스케줄 실행] 오후 ${now.hour}시 ${currentMinute}분 - 보유주식 시간외 단일가 또는 대체마켓 체크를 시작합니다.",
|
||||
true
|
||||
)
|
||||
var list = mutableListOf<String>("x")
|
||||
var list = mutableListOf<String>("X")
|
||||
if (now.hour != 8) {
|
||||
list.add("y")
|
||||
list.add("Y")
|
||||
}
|
||||
list.forEach { code ->
|
||||
KisTradeService.fetchIntegratedBalance(code).getOrNull()?.let {
|
||||
|
||||
@ -205,55 +205,49 @@ fun TradingDecisionLog() {
|
||||
}
|
||||
Column(modifier = Modifier.weight(0.5f).padding(6.dp).fillMaxHeight().background(Color.White)) {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(2), // 2열 병렬 배치
|
||||
columns = GridCells.Fixed(6), // 💡 2와 3의 최소공배수인 6열로 통합!
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
modifier = Modifier.fillMaxWidth().weight(0.5f).background(Color.White)
|
||||
modifier = Modifier.fillMaxSize().background(Color.White) // fillMaxWidth() 대신 전체 채우기
|
||||
) {
|
||||
// ==========================================
|
||||
// 1️⃣ 첫 번째 섹션: 2열 배치 구간 (span = 3)
|
||||
// ==========================================
|
||||
var firstSet = mutableSetOf<ConfigIndex>()
|
||||
item(span = { GridItemSpan(maxLineSpan) }) { // 2열을 모두 차지함
|
||||
|
||||
item(span = { GridItemSpan(maxLineSpan) }) { // 6칸 모두 차지
|
||||
Text(
|
||||
"💰 거래 기본 설정",
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
modifier = Modifier.padding(top = 10.dp, bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
var defaults = arrayOf(
|
||||
ConfigIndex.TAX_INDEX,
|
||||
ConfigIndex.PROFIT_INDEX,
|
||||
ConfigIndex.BUY_WEIGHT_INDEX,
|
||||
ConfigIndex.MAX_BUDGET_INDEX,
|
||||
ConfigIndex.MAX_PRICE_INDEX,
|
||||
ConfigIndex.MIN_PRICE_INDEX,
|
||||
ConfigIndex.MIN_PURCHASE_SCORE_INDEX,
|
||||
ConfigIndex.SELL_PROFIT,
|
||||
ConfigIndex.MAX_COUNT_INDEX,
|
||||
ConfigIndex.MAX_HOLDING_COUNT,
|
||||
ConfigIndex.TAX_INDEX, ConfigIndex.PROFIT_INDEX, ConfigIndex.BUY_WEIGHT_INDEX,
|
||||
ConfigIndex.MAX_BUDGET_INDEX, ConfigIndex.MAX_PRICE_INDEX, ConfigIndex.MIN_PRICE_INDEX,
|
||||
ConfigIndex.MIN_PURCHASE_SCORE_INDEX, ConfigIndex.SELL_PROFIT,
|
||||
ConfigIndex.MAX_COUNT_INDEX, ConfigIndex.MAX_HOLDING_COUNT,
|
||||
)
|
||||
items(defaults.size) { index ->
|
||||
|
||||
// 💡 items에 span을 주어 3칸씩 차지하게 만듦 (결과적으로 2열 배치)
|
||||
items(defaults.size, span = { GridItemSpan(3) }) { index ->
|
||||
val configKey = defaults.get(index)
|
||||
var localText by remember(configKey) { mutableStateOf(KisSession.config.getValues(configKey)?.toString() ?: "") }
|
||||
|
||||
// 1. 키보드 입력을 실시간으로 보여줄 로컬 상태 (String)
|
||||
var localText by remember(configKey) {
|
||||
mutableStateOf(KisSession.config.getValues(configKey)?.toString() ?: "")
|
||||
}
|
||||
|
||||
// 저장 로직을 공통 함수로 분리
|
||||
val saveAction = {
|
||||
var newValue = localText.toDoubleOrNull() ?: 0.0
|
||||
var oldValue = KisSession.config.getValues(configKey)
|
||||
if (configKey.label.contains("PROFIT")) {
|
||||
newValue = newValue / KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)
|
||||
newValue = newValue / KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)
|
||||
}
|
||||
if (firstSet.contains(configKey)) {
|
||||
TradingLogStore.addSettingLog(configKey.label,oldValue.toString(),newValue.toString(),"💾 저장됨: ${configKey.label} = $newValue")
|
||||
TradingLogStore.addSettingLog(configKey.label, oldValue.toString(), newValue.toString(), "💾 저장됨: ${configKey.label} = $newValue")
|
||||
} else {
|
||||
firstSet.add(configKey)
|
||||
}
|
||||
|
||||
KisSession.config.setValues(configKey, newValue)
|
||||
DatabaseFactory.saveConfig(KisSession.config)
|
||||
println("💾 저장됨: ${configKey.label} = $newValue")
|
||||
}
|
||||
|
||||
var text = if (configKey.label.contains("PROFIT")) {
|
||||
@ -264,55 +258,114 @@ fun TradingDecisionLog() {
|
||||
|
||||
OutlinedTextField(
|
||||
value = text,
|
||||
onValueChange = { localText = it }, // 화면에는 즉시 반영
|
||||
onValueChange = { localText = it },
|
||||
label = { Text(configKey.label) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.onFocusChanged { focusState ->
|
||||
// 2. 포커스를 잃었을 때 저장
|
||||
if (!focusState.isFocused) {
|
||||
saveAction()
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
keyboardType = KeyboardType.Decimal
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
// 3. 엔터(Done) 키를 눌렀을 때 저장
|
||||
onDone = {
|
||||
saveAction()
|
||||
}
|
||||
),
|
||||
.onFocusChanged { focusState -> if (!focusState.isFocused) saveAction() },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done, keyboardType = KeyboardType.Decimal),
|
||||
keyboardActions = KeyboardActions(onDone = { saveAction() }),
|
||||
singleLine = true
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(3), // 2열 병렬 배치
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
modifier = Modifier.fillMaxWidth().weight(0.5f).background(Color.White)
|
||||
) {
|
||||
var firstSet = mutableSetOf<ConfigIndex>()
|
||||
item(span = { GridItemSpan(maxLineSpan) }) { // 2열을 모두 차지함
|
||||
// --- 🛡️ 수익 및 리스크 관리 섹션 ---
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Divider(color = Color.LightGray, thickness = 1.dp)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = "🛡️ 수익 및 리스크 관리",
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
fontWeight = FontWeight.ExtraBold,
|
||||
color = Color.DarkGray,
|
||||
modifier = Modifier.padding(bottom = 12.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 💡 2열 배치니까 각각 span = 3
|
||||
item(span = { GridItemSpan(3) }) {
|
||||
SettingSwitchField(
|
||||
label = "자동 익절 활성화",
|
||||
initialChecked = KisSession.config.getValues(ConfigIndex.TAKE_PROFIT) == 1.0,
|
||||
onCheckedChange = { KisSession.config.setValues(ConfigIndex.TAKE_PROFIT, if (it) 1.0 else 0.0) },
|
||||
helperText = "목표 수익률 도달 시 기계적 익절"
|
||||
)
|
||||
}
|
||||
|
||||
item(span = { GridItemSpan(3) }) {
|
||||
SettingSwitchField(
|
||||
label = "자동 손절 활성화",
|
||||
initialChecked = KisSession.config.getValues(ConfigIndex.STOP_LOSS) == 1.0,
|
||||
onCheckedChange = { KisSession.config.setValues(ConfigIndex.STOP_LOSS, if (it) 1.0 else 0.0) },
|
||||
helperText = "손실 방어선 도달 시 기계적 손절"
|
||||
)
|
||||
}
|
||||
|
||||
item(span = { GridItemSpan(3) }) {
|
||||
SettingInputField(
|
||||
label = "최소 손절 라인 (%)",
|
||||
initialValue = KisSession.config.getValues(ConfigIndex.LOSS_MINRATE).toString(),
|
||||
placeholder = "-1.5",
|
||||
onSave = { KisSession.config.setValues(ConfigIndex.LOSS_MINRATE, it.toDoubleOrNull() ?: -1.5) },
|
||||
helperText = "타협 가능한 최소 라인"
|
||||
)
|
||||
}
|
||||
|
||||
item(span = { GridItemSpan(3) }) {
|
||||
SettingInputField(
|
||||
label = "최대 허용 손절률 (%)",
|
||||
initialValue = KisSession.config.getValues(ConfigIndex.LOSS_MAXRATE).toString(),
|
||||
placeholder = "-5.0",
|
||||
onSave = { KisSession.config.setValues(ConfigIndex.LOSS_MAXRATE, it.toDoubleOrNull() ?: -5.0) },
|
||||
helperText = "절대 방어선 (기계적 매도)"
|
||||
)
|
||||
}
|
||||
|
||||
// 💡 금액은 길게 뻗어야 하니 maxLineSpan
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
SettingInputField(
|
||||
label = "최대 허용 손실 금액 (원)",
|
||||
initialValue = KisSession.config.getValues(ConfigIndex.LOSS_MAX_MONEY).toLong().toString(),
|
||||
placeholder = "50000",
|
||||
onSave = { KisSession.config.setValues(ConfigIndex.LOSS_MAX_MONEY, it.toDoubleOrNull() ?: 0.0) },
|
||||
helperText = "1종목당 허용할 수 있는 최대 손실액 (원 단위)"
|
||||
)
|
||||
}
|
||||
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Divider(color = Color.LightGray, thickness = 1.dp)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 2️⃣ 두 번째 섹션: 3열 배치 구간 (span = 2)
|
||||
// ==========================================
|
||||
item(span = { GridItemSpan(maxLineSpan) }) { // 6칸 모두 차지
|
||||
Text(
|
||||
"💰매수 정책 및 기대 수익률",
|
||||
"💰 매수 정책 및 기대 수익률",
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
modifier = Modifier.padding(top = 10.dp, bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
var defaults2 = arrayOf(
|
||||
arrayOf(ConfigIndex.GRADE_5_BUY, ConfigIndex.GRADE_5_PROFIT,ConfigIndex.GRADE_5_ALLOCATIONRATE),
|
||||
arrayOf(ConfigIndex.GRADE_4_BUY, ConfigIndex.GRADE_4_PROFIT,ConfigIndex.GRADE_4_ALLOCATIONRATE),
|
||||
arrayOf(ConfigIndex.GRADE_3_BUY, ConfigIndex.GRADE_3_PROFIT,ConfigIndex.GRADE_3_ALLOCATIONRATE),
|
||||
arrayOf(ConfigIndex.GRADE_2_BUY, ConfigIndex.GRADE_2_PROFIT,ConfigIndex.GRADE_2_ALLOCATIONRATE),
|
||||
arrayOf(ConfigIndex.GRADE_1_BUY, ConfigIndex.GRADE_1_PROFIT,ConfigIndex.GRADE_1_ALLOCATIONRATE),
|
||||
arrayOf(ConfigIndex.GRADE_5_BUY, ConfigIndex.GRADE_5_PROFIT, ConfigIndex.GRADE_5_ALLOCATIONRATE),
|
||||
arrayOf(ConfigIndex.GRADE_4_BUY, ConfigIndex.GRADE_4_PROFIT, ConfigIndex.GRADE_4_ALLOCATIONRATE),
|
||||
arrayOf(ConfigIndex.GRADE_3_BUY, ConfigIndex.GRADE_3_PROFIT, ConfigIndex.GRADE_3_ALLOCATIONRATE),
|
||||
arrayOf(ConfigIndex.GRADE_2_BUY, ConfigIndex.GRADE_2_PROFIT, ConfigIndex.GRADE_2_ALLOCATIONRATE),
|
||||
arrayOf(ConfigIndex.GRADE_1_BUY, ConfigIndex.GRADE_1_PROFIT, ConfigIndex.GRADE_1_ALLOCATIONRATE),
|
||||
)
|
||||
|
||||
for (items in defaults2) {
|
||||
val common = findLongestCommonSubstring(items.first().label,items.last().label)
|
||||
item(span = { GridItemSpan(maxLineSpan) }) { // 2열을 모두 차지함
|
||||
val common = findLongestCommonSubstring(items.first().label, items.last().label)
|
||||
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
Text(
|
||||
common,
|
||||
style = MaterialTheme.typography.body1,
|
||||
@ -320,72 +373,42 @@ fun TradingDecisionLog() {
|
||||
)
|
||||
}
|
||||
|
||||
items(items.size) { index ->
|
||||
// 💡 items에 span을 주어 2칸씩 차지하게 만듦 (결과적으로 3열 배치)
|
||||
items(items.size, span = { GridItemSpan(2) }) { index ->
|
||||
val configKey = items.get(index)
|
||||
|
||||
// 1. 키보드 입력을 실시간으로 보여줄 로컬 상태 (String)
|
||||
var localText by remember(configKey) {
|
||||
mutableStateOf(KisSession.config.getValues(configKey)?.toString() ?: "")
|
||||
}
|
||||
|
||||
var labelText by remember(configKey) {
|
||||
mutableStateOf(KisSession.config.getValues(configKey)?.toString() ?: "")
|
||||
}
|
||||
var localText by remember(configKey) { mutableStateOf(KisSession.config.getValues(configKey)?.toString() ?: "") }
|
||||
var labelText by remember(configKey) { mutableStateOf("") }
|
||||
|
||||
val saveAction = {
|
||||
var oldValue = KisSession.config.getValues(configKey)
|
||||
var newValue = localText.toDoubleOrNull() ?: 0.0
|
||||
//
|
||||
KisSession.config.setValues(configKey, newValue)
|
||||
DatabaseFactory.saveConfig(KisSession.config)
|
||||
if (firstSet.contains(configKey)) {
|
||||
TradingLogStore.addSettingLog(configKey.label,oldValue.toString(),newValue.toString(),"💾 저장됨: ${configKey.label} = $newValue")
|
||||
TradingLogStore.addSettingLog(configKey.label, oldValue.toString(), newValue.toString(), "💾 저장됨: ${configKey.label} = $newValue")
|
||||
} else {
|
||||
firstSet.add(configKey)
|
||||
}
|
||||
labelText = if (configKey.name.contains("PROFIT")) {
|
||||
getRemaining(configKey.label,common) + ": 기준율(${KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)}) * 성향별 비율(${KisSession.config.getValues(configKey)}) + 세금제비용(${KisSession.config.getValues(
|
||||
ConfigIndex.TAX_INDEX)}) = ${(localText.toDouble() * KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)) + KisSession.config.getValues(
|
||||
ConfigIndex.TAX_INDEX)}"
|
||||
} else if (configKey.name.contains("ALLOCATIONRATE")) {
|
||||
getRemaining(configKey.label,common) + ": 최대 ${KisSession.config.getValues(ConfigIndex.MAX_BUDGET_INDEX) * newValue}원 투자}"
|
||||
} else {
|
||||
getRemaining(configKey.label,common) + ": -${localText} 호가 매수}"
|
||||
}
|
||||
}
|
||||
|
||||
// labelText 업데이트 로직 (기존과 동일)
|
||||
labelText = if (configKey.name.contains("PROFIT")) {
|
||||
getRemaining(configKey.label,common) + ": 기준율(${KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)}) * 성향별 비율(${KisSession.config.getValues(configKey)}) + 세금제비용(${KisSession.config.getValues(
|
||||
ConfigIndex.TAX_INDEX)}) = ${(localText.toDouble() * KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)) + KisSession.config.getValues(
|
||||
ConfigIndex.TAX_INDEX)} "
|
||||
} else if (configKey.name.contains("ALLOCATIONRATE")) {
|
||||
getRemaining(configKey.label,common) + ": 최대 ${KisSession.config.getValues(ConfigIndex.MAX_BUDGET_INDEX) * localText.toDouble() }원 투자}"
|
||||
getRemaining(configKey.label, common) + ": 기준율(${KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)}) * 비율(${KisSession.config.getValues(configKey)}) + 세금(${KisSession.config.getValues(ConfigIndex.TAX_INDEX)}) = ${(localText.toDoubleOrNull() ?: 0.0) * KisSession.config.getValues(ConfigIndex.PROFIT_INDEX) + KisSession.config.getValues(ConfigIndex.TAX_INDEX)}"
|
||||
} else if (configKey.name.contains("ALLOCATIONRATE")) {
|
||||
getRemaining(configKey.label, common) + ": 최대 ${KisSession.config.getValues(ConfigIndex.MAX_BUDGET_INDEX) * (localText.toDoubleOrNull() ?: 0.0)}원"
|
||||
} else {
|
||||
getRemaining(configKey.label,common) + ": -${localText} 호가 매수}"
|
||||
getRemaining(configKey.label, common) + ": -${localText} 호가 매수"
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
value = localText,
|
||||
onValueChange = { localText = it }, // 화면에는 즉시 반영
|
||||
onValueChange = { localText = it },
|
||||
label = { Text(labelText) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.onFocusChanged { focusState ->
|
||||
// 2. 포커스를 잃었을 때 저장
|
||||
if (!focusState.isFocused) {
|
||||
saveAction()
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
keyboardType = KeyboardType.Decimal
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
// 3. 엔터(Done) 키를 눌렀을 때 저장
|
||||
onDone = {
|
||||
saveAction()
|
||||
}
|
||||
),
|
||||
.onFocusChanged { focusState -> if (!focusState.isFocused) saveAction() },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done, keyboardType = KeyboardType.Decimal),
|
||||
keyboardActions = KeyboardActions(onDone = { saveAction() }),
|
||||
singleLine = true
|
||||
)
|
||||
}
|
||||
@ -533,3 +556,99 @@ fun CsvDropZone(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun SettingInputField(
|
||||
label: String,
|
||||
initialValue: String, // 💡 value -> initialValue 로 변경
|
||||
placeholder: String = "",
|
||||
helperText: String = "",
|
||||
onSave: (String) -> Unit // 💡 타자 칠 때마다가 아니라, 완료 시 저장하도록 콜백 변경
|
||||
) {
|
||||
// 💡 화면에 즉시 글자를 그려주기 위한 로컬 상태 (핵심 해결책)
|
||||
var localText by remember { mutableStateOf(initialValue) }
|
||||
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
OutlinedTextField(
|
||||
value = localText,
|
||||
onValueChange = { localText = it }, // 타자 칠 때 화면 즉시 반영
|
||||
label = { Text(label, fontWeight = FontWeight.Bold) },
|
||||
placeholder = { Text(placeholder) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.onFocusChanged { focusState ->
|
||||
// 💡 포커스를 잃었을 때 (다른 칸을 클릭했을 때) 저장
|
||||
if (!focusState.isFocused) {
|
||||
onSave(localText)
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
keyboardType = KeyboardType.Decimal
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
// 💡 모바일 키보드나 키보드에서 엔터(Done) 쳤을 때 저장
|
||||
onDone = {
|
||||
onSave(localText)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if (helperText.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = helperText,
|
||||
color = Color.Gray,
|
||||
fontSize = 11.sp,
|
||||
modifier = Modifier.padding(start = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingSwitchField(
|
||||
label: String,
|
||||
initialChecked: Boolean,
|
||||
helperText: String = "",
|
||||
onCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
// 💡 스위치 애니메이션을 즉각 보여주기 위한 로컬 상태
|
||||
var localChecked by remember { mutableStateOf(initialChecked) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp, horizontal = 4.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Switch(
|
||||
checked = localChecked,
|
||||
onCheckedChange = { isChecked ->
|
||||
localChecked = isChecked // 화면 스위치 즉시 변경
|
||||
onCheckedChange(isChecked) // 실제 DB/설정 저장 트리거
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (helperText.isNotEmpty()) {
|
||||
Text(
|
||||
text = helperText,
|
||||
color = Color.Gray,
|
||||
fontSize = 11.sp,
|
||||
modifier = Modifier.padding(start = 2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user